feat: Token-Nutzung Tab sichtbar + Budget-Bearbeitung
- display:none entfernt, .active-Klasse greift jetzt korrekt - Budget-Formular mit 4 Feldern (Credits, Kosten/Credit, Budget-Limit, Verbrauch) - Formular wird mit aktuellen Werten vorbelegt - Speichern aktualisiert Lizenz und lädt Daten neu
Dieser Commit ist enthalten in:
@@ -163,7 +163,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section" id="sub-tokens" style="display:none;">
|
<div class="section" id="sub-tokens">
|
||||||
<div class="token-overview">
|
<div class="token-overview">
|
||||||
<div class="token-stats-row" id="tokenStatsRow">
|
<div class="token-stats-row" id="tokenStatsRow">
|
||||||
<div class="token-stat-card">
|
<div class="token-stat-card">
|
||||||
@@ -206,6 +206,34 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody id="tokenMonthlyTable"></tbody>
|
<tbody id="tokenMonthlyTable"></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<h3 style="margin-top:24px;">Budget bearbeiten</h3>
|
||||||
|
<div class="card" style="margin-top:12px;">
|
||||||
|
<div class="card-body">
|
||||||
|
<form id="tokenBudgetForm" style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editCreditsTotal">Credits-Kontingent</label>
|
||||||
|
<input type="number" id="editCreditsTotal" placeholder="z.B. 600000">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editCostPerCredit">Kosten pro Credit (USD)</label>
|
||||||
|
<input type="number" id="editCostPerCredit" step="0.0001" placeholder="z.B. 0.0033">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editBudgetUsd">Budget-Limit (USD)</label>
|
||||||
|
<input type="number" id="editBudgetUsd" step="0.01" placeholder="z.B. 2000">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editCreditsUsed">Verbrauchte Credits</label>
|
||||||
|
<input type="number" id="editCreditsUsed" step="0.01" placeholder="Aktueller Verbrauch">
|
||||||
|
</div>
|
||||||
|
<div style="grid-column:1/-1; display:flex; gap:8px; align-items:center;">
|
||||||
|
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||||
|
<span id="tokenBudgetMsg" style="font-size:13px; color:var(--text-secondary);"></span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -595,6 +595,8 @@ async function loadOrgTokenUsage(orgId) {
|
|||||||
}
|
}
|
||||||
if (percentEl) percentEl.textContent = percent.toFixed(1) + '%';
|
if (percentEl) percentEl.textContent = percent.toFixed(1) + '%';
|
||||||
|
|
||||||
|
fillBudgetForm(budget);
|
||||||
|
|
||||||
const tbody = document.getElementById('tokenMonthlyTable');
|
const tbody = document.getElementById('tokenMonthlyTable');
|
||||||
if (tbody) {
|
if (tbody) {
|
||||||
tbody.innerHTML = monthly.map(function(m) {
|
tbody.innerHTML = monthly.map(function(m) {
|
||||||
@@ -613,6 +615,15 @@ async function loadOrgTokenUsage(orgId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Budget-Formular Felder befuellen
|
||||||
|
function fillBudgetForm(budget) {
|
||||||
|
const el = (id, val) => { const e = document.getElementById(id); if (e && val != null) e.value = val; };
|
||||||
|
el('editCreditsTotal', budget.credits_total);
|
||||||
|
el('editCostPerCredit', budget.cost_per_credit);
|
||||||
|
el('editBudgetUsd', budget.token_budget_usd);
|
||||||
|
el('editCreditsUsed', budget.credits_used ? Math.round(budget.credits_used) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadDashboardTokenStats() {
|
async function loadDashboardTokenStats() {
|
||||||
try {
|
try {
|
||||||
const data = await API.get('/api/token-usage/overview');
|
const data = await API.get('/api/token-usage/overview');
|
||||||
@@ -636,3 +647,47 @@ async function loadDashboardTokenStats() {
|
|||||||
console.error('Token-Overview laden fehlgeschlagen:', err);
|
console.error('Token-Overview laden fehlgeschlagen:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Budget-Formular Submit
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var form = document.getElementById('tokenBudgetForm');
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', async function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var msgEl = document.getElementById('tokenBudgetMsg');
|
||||||
|
if (msgEl) msgEl.textContent = 'Speichern...';
|
||||||
|
|
||||||
|
// Lizenz-ID fuer die aktuelle Org ermitteln
|
||||||
|
try {
|
||||||
|
var licenses = await API.get('/api/licenses?org_id=' + currentOrgId);
|
||||||
|
var activeLic = licenses.find(function(l) { return l.status === 'active'; });
|
||||||
|
if (!activeLic) {
|
||||||
|
if (msgEl) msgEl.textContent = 'Keine aktive Lizenz gefunden';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = {};
|
||||||
|
var creditsTotal = document.getElementById('editCreditsTotal');
|
||||||
|
var costPerCredit = document.getElementById('editCostPerCredit');
|
||||||
|
var budgetUsd = document.getElementById('editBudgetUsd');
|
||||||
|
var creditsUsed = document.getElementById('editCreditsUsed');
|
||||||
|
|
||||||
|
if (creditsTotal && creditsTotal.value) body.credits_total = parseInt(creditsTotal.value);
|
||||||
|
if (costPerCredit && costPerCredit.value) body.cost_per_credit = parseFloat(costPerCredit.value);
|
||||||
|
if (budgetUsd && budgetUsd.value) body.token_budget_usd = parseFloat(budgetUsd.value);
|
||||||
|
if (creditsUsed && creditsUsed.value !== '') body.credits_used = parseFloat(creditsUsed.value);
|
||||||
|
|
||||||
|
await API.put('/api/token-usage/budget/' + activeLic.id, body);
|
||||||
|
if (msgEl) msgEl.textContent = 'Gespeichert!';
|
||||||
|
setTimeout(function() { if (msgEl) msgEl.textContent = ''; }, 3000);
|
||||||
|
|
||||||
|
// Daten neu laden
|
||||||
|
loadOrgTokenUsage(currentOrgId);
|
||||||
|
} catch (err) {
|
||||||
|
if (msgEl) msgEl.textContent = 'Fehler: ' + err.message;
|
||||||
|
console.error('Budget speichern fehlgeschlagen:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren