feat(org-detail): Verbrauch weiter aufgeräumt, Lizenz und Kontingent in einer Karte
Auf Nutzerwunsch vier Änderungen im Org-Detail. 1. Kundensicht (Credits) und interner Einkauf (USD) stehen in EINER Karten-Reihe (Verbraucht, Verbleibend, API-Kosten Monat). Die Karte "Budget-Limit (USD, Altmodus)" entfällt in der Anzeige, das Feld bleibt im Erweitert-Bereich des Formulars erhalten. 2. Die Laufzeitwerte-Karte (Übertrag, laufende Periode, Verfügbar gesamt) ist raus. Der Übertrag bleibt im Verbleibend-Wert als Zusatz "(inkl. X Übertrag)" sichtbar. 3. "Lizenz & Credits-Kontingent bearbeiten" steht jetzt an zweiter Stelle direkt unter der Karten-Reihe und enthält BEIDES, die Lizenz-Verwaltung (Tabelle, Neue Lizenz, Verlängern, Widerrufen) und darunter das Kontingent-Formular. Der eigene Lizenzen-Block entfällt, der Monatliche Verlauf rückt ans Ende. 4. Die Sprungmarken-Leiste (Verbrauch/Nutzer/Lizenzen/Einstellungen) oben im Org-Detail ist entfernt, die Seite ist kurz genug. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -1404,12 +1404,11 @@ details.adv-section .adv-hint { grid-column: 1 / -1; font-size: 12px; color: var
|
|||||||
|
|
||||||
/* === Org-Detail als durchgehende Seite (statt Unterreitern) === */
|
/* === Org-Detail als durchgehende Seite (statt Unterreitern) === */
|
||||||
.detail-panel.active { display: flex; flex-direction: column; }
|
.detail-panel.active { display: flex; flex-direction: column; }
|
||||||
#orgDetailTabs { order: 1; position: sticky; top: 61px; background: var(--bg-primary); z-index: 50; }
|
|
||||||
.org-block { margin-top: 26px; scroll-margin-top: 120px; }
|
.org-block { margin-top: 26px; scroll-margin-top: 120px; }
|
||||||
#sub-tokens { order: 2; }
|
#sub-tokens { order: 2; }
|
||||||
#sub-users { order: 3; }
|
#sub-users { order: 3; }
|
||||||
#sub-org-licenses { order: 4; }
|
#sub-settings { order: 4; }
|
||||||
#sub-settings { order: 5; }
|
|
||||||
.org-block-title {
|
.org-block-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>AegisSight Monitor-Verwaltung</title>
|
<title>AegisSight Monitor-Verwaltung</title>
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||||
<link rel="apple-touch-icon" href="/static/favicon.svg">
|
<link rel="apple-touch-icon" href="/static/favicon.svg">
|
||||||
<link rel="stylesheet" href="/static/css/style.css?v=20260725c">
|
<link rel="stylesheet" href="/static/css/style.css?v=20260725d">
|
||||||
<script>(function(){var t=localStorage.getItem('portal_theme');if(t==='light')document.documentElement.setAttribute('data-theme','light');try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
<script>(function(){var t=localStorage.getItem('portal_theme');if(t==='light')document.documentElement.setAttribute('data-theme','light');try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -149,16 +149,8 @@
|
|||||||
<button class="detail-back" id="orgBackBtn">← Zurück</button>
|
<button class="detail-back" id="orgBackBtn">← Zurück</button>
|
||||||
<div id="orgDetailHeader"></div>
|
<div id="orgDetailHeader"></div>
|
||||||
|
|
||||||
<!-- Alles auf einer Seite: die Pillen scrollen nur zu den Bereichen,
|
<!-- Alles auf einer Seite, Anzeige-Reihenfolge steuert CSS (order),
|
||||||
sie schalten nichts mehr um. Anzeige-Reihenfolge steuert CSS (order),
|
|
||||||
Verbrauch steht optisch zuerst. -->
|
Verbrauch steht optisch zuerst. -->
|
||||||
<div class="nav-tabs mt-16" id="orgDetailTabs">
|
|
||||||
<button class="nav-tab active" data-subtab="tokens">Verbrauch</button>
|
|
||||||
<button class="nav-tab" data-subtab="users">Nutzer</button>
|
|
||||||
<button class="nav-tab" data-subtab="org-licenses">Lizenzen</button>
|
|
||||||
<button class="nav-tab" data-subtab="settings">Einstellungen</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Bereich: Nutzer -->
|
<!-- Bereich: Nutzer -->
|
||||||
<div class="org-block" id="sub-users">
|
<div class="org-block" id="sub-users">
|
||||||
<h2 class="org-block-title">Nutzer</h2>
|
<h2 class="org-block-title">Nutzer</h2>
|
||||||
@@ -185,32 +177,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bereich: Lizenzen -->
|
|
||||||
<div class="org-block" id="sub-org-licenses">
|
|
||||||
<h2 class="org-block-title">Lizenzen</h2>
|
|
||||||
<div class="action-bar">
|
|
||||||
<span></span>
|
|
||||||
<button class="btn btn-primary" id="newLicenseBtn"><svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Neue Lizenz</button>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="table-wrap">
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Typ</th>
|
|
||||||
<th>Max Nutzer</th>
|
|
||||||
<th>Gültig ab</th>
|
|
||||||
<th>Gültig bis</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Aktionen</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="licenseTable"></tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Bereich: Einstellungen -->
|
<!-- Bereich: Einstellungen -->
|
||||||
<div class="org-block" id="sub-settings">
|
<div class="org-block" id="sub-settings">
|
||||||
<h2 class="org-block-title">Einstellungen</h2>
|
<h2 class="org-block-title">Einstellungen</h2>
|
||||||
@@ -248,8 +214,7 @@
|
|||||||
<div class="org-block" id="sub-tokens">
|
<div class="org-block" id="sub-tokens">
|
||||||
<h2 class="org-block-title">Verbrauch & Kontingent</h2>
|
<h2 class="org-block-title">Verbrauch & Kontingent</h2>
|
||||||
<div class="token-overview">
|
<div class="token-overview">
|
||||||
<!-- Kundensicht: das, was der Kunde im Monitor auch sieht -->
|
<!-- Eine Reihe: Kundensicht (Credits) und interner Einkauf (USD) -->
|
||||||
<h3 style="margin-bottom:8px;">Kundensicht (Credits)</h3>
|
|
||||||
<div class="token-stats-row" id="tokenStatsRow">
|
<div class="token-stats-row" id="tokenStatsRow">
|
||||||
<div class="token-stat-card">
|
<div class="token-stat-card">
|
||||||
<div class="token-stat-label">Verbrauchte Credits (Periode)</div>
|
<div class="token-stat-label">Verbrauchte Credits (Periode)</div>
|
||||||
@@ -259,6 +224,10 @@
|
|||||||
<div class="token-stat-label">Verbleibend</div>
|
<div class="token-stat-label">Verbleibend</div>
|
||||||
<div class="token-stat-value" id="tokenCreditsRemaining">-</div>
|
<div class="token-stat-value" id="tokenCreditsRemaining">-</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="token-stat-card">
|
||||||
|
<div class="token-stat-label">API-Kosten Monat (Einkauf)</div>
|
||||||
|
<div class="token-stat-value" id="tokenCostUsd">-</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="token-budget-bar-section">
|
<div class="token-budget-bar-section">
|
||||||
<div class="token-budget-label">
|
<div class="token-budget-label">
|
||||||
@@ -269,44 +238,36 @@
|
|||||||
<div class="token-budget-bar" id="tokenBudgetBar"></div>
|
<div class="token-budget-bar" id="tokenBudgetBar"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="tokenSourceSplit" style="margin-top:10px;font-size:13px;"></div>
|
||||||
<p style="margin-top:10px; font-size:12px; color:var(--text-muted); display:flex; align-items:center; gap:6px;">
|
<p style="margin-top:10px; font-size:12px; color:var(--text-muted); display:flex; align-items:center; gap:6px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex-shrink:0;"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex-shrink:0;"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
||||||
<span><span id="tariffInfoText">Zur Einordnung. Ein Live-Lauf kostet 45 Credits, das Anlegen einer Recherche 120 (drei Durchläufe), jede Vertiefung 40, Chat und Beschreibungs-Assistent je 1 Credit.</span>
|
<span><span id="tariffInfoText">Zur Einordnung. Ein Live-Lauf kostet 45 Credits, das Anlegen einer Recherche 120 (drei Durchläufe), jede Vertiefung 40, Chat und Beschreibungs-Assistent je 1 Credit.</span>
|
||||||
<a href="#" onclick="openRechnerTab(); return false;" style="color:var(--accent);">Verbrauchsrechner öffnen</a></span>
|
<a href="#" onclick="openRechnerTab(); return false;" style="color:var(--accent);">Verbrauchsrechner öffnen</a></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Interne Sicht: was uns der Betrieb im Einkauf kostet -->
|
<!-- An zweiter Stelle: Lizenz und Kontingent in EINER Karte einstellbar -->
|
||||||
<h3 style="margin-top:24px; margin-bottom:8px;">Interne Kosten (Einkauf, USD)</h3>
|
<h3 style="margin-top:24px;">Lizenz & Credits-Kontingent bearbeiten</h3>
|
||||||
<div class="token-stats-row">
|
<div class="card" style="margin-top:12px;">
|
||||||
<div class="token-stat-card">
|
<div class="card-header">
|
||||||
<div class="token-stat-label">API-Kosten aktueller Monat</div>
|
<span class="text-secondary" style="font-size:13px;">Aktive und frühere Lizenzen dieser Organisation</span>
|
||||||
<div class="token-stat-value" id="tokenCostUsd">-</div>
|
<button class="btn btn-primary btn-small" id="newLicenseBtn"><svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>Neue Lizenz</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="token-stat-card">
|
<div class="table-wrap">
|
||||||
<div class="token-stat-label">Budget-Limit (USD, Altmodus)</div>
|
<table>
|
||||||
<div class="token-stat-value" id="tokenBudgetUsd">-</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="tokenSourceSplit" style="margin-top:12px;font-size:14px;"></div>
|
|
||||||
<h3 style="margin-top:24px;">Monatlicher Verlauf</h3>
|
|
||||||
<table class="data-table">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Monat</th>
|
<th>Typ</th>
|
||||||
<th>Quelle</th>
|
<th>Max Nutzer</th>
|
||||||
<th>Refreshes</th>
|
<th>Gültig ab</th>
|
||||||
<th>API-Calls</th>
|
<th>Gültig bis</th>
|
||||||
<th>Input-Tokens</th>
|
<th>Status</th>
|
||||||
<th>Output-Tokens</th>
|
<th>Aktionen</th>
|
||||||
<th>Kosten (USD)</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tokenMonthlyTable"></tbody>
|
<tbody id="licenseTable"></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
<h3 style="margin-top:24px;">Credits-Kontingent bearbeiten</h3>
|
<div class="card-body" style="border-top:1px solid var(--border);">
|
||||||
<div class="card" style="margin-top:12px;">
|
|
||||||
<div class="card-body">
|
|
||||||
<form id="tokenBudgetForm" style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
|
<form id="tokenBudgetForm" style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
|
||||||
<div style="grid-column:1/-1; padding:8px 12px; background:var(--bg-tertiary); border-radius:6px; border:1px solid var(--border);">
|
<div style="grid-column:1/-1; padding:8px 12px; background:var(--bg-tertiary); border-radius:6px; border:1px solid var(--border);">
|
||||||
<label style="display:flex; align-items:center; gap:8px; cursor:pointer; margin:0;">
|
<label style="display:flex; align-items:center; gap:8px; cursor:pointer; margin:0;">
|
||||||
@@ -372,30 +333,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 style="margin-top:24px;">Laufzeitwerte</h3>
|
<h3 style="margin-top:24px;">Monatlicher Verlauf</h3>
|
||||||
<div class="card" style="margin-top:12px;">
|
<table class="data-table">
|
||||||
<div class="card-body">
|
<thead>
|
||||||
<p class="text-secondary" style="margin:0 0 12px; font-size:13px;">
|
<tr>
|
||||||
Diese Werte verwaltet der Monitor selbst, sie sind hier nicht änderbar.
|
<th>Monat</th>
|
||||||
Stand ist die letzte Lizenzprüfung des Monitors, nach einem Monatswechsel
|
<th>Quelle</th>
|
||||||
können sie kurzzeitig noch die Vorperiode zeigen.
|
<th>Refreshes</th>
|
||||||
</p>
|
<th>API-Calls</th>
|
||||||
<div style="display:grid; grid-template-columns:1fr 1fr 1fr; gap:12px;">
|
<th>Input-Tokens</th>
|
||||||
<div class="form-group">
|
<th>Output-Tokens</th>
|
||||||
<label>Übertrag aus dem Vormonat</label>
|
<th>Kosten (USD)</th>
|
||||||
<div id="viewCreditsCarried" style="font-size:16px; font-weight:600;">-</div>
|
</tr>
|
||||||
</div>
|
</thead>
|
||||||
<div class="form-group">
|
<tbody id="tokenMonthlyTable"></tbody>
|
||||||
<label>Laufende Periode</label>
|
</table>
|
||||||
<div id="viewCreditsPeriodStart" style="font-size:16px; font-weight:600;">-</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Verfügbar gesamt (Kontingent + Übertrag)</label>
|
|
||||||
<div id="viewCreditsAvailable" style="font-size:16px; font-weight:600;">-</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1217,7 +1169,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/a11y.js?v=20260725a"></script>
|
<script src="/static/js/a11y.js?v=20260725a"></script>
|
||||||
<script src="/static/js/app.js?v=20260725f"></script>
|
<script src="/static/js/app.js?v=20260725g"></script>
|
||||||
<script src="/static/js/sources.js?v=20260522x2"></script>
|
<script src="/static/js/sources.js?v=20260522x2"></script>
|
||||||
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
||||||
<script src="/static/js/source-health.js?v=20260509l"></script>
|
<script src="/static/js/source-health.js?v=20260509l"></script>
|
||||||
|
|||||||
@@ -161,17 +161,8 @@ function setupNavTabs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupOrgDetailTabs() {
|
function setupOrgDetailTabs() {
|
||||||
// Das Org-Detail ist eine durchgehende Seite, die Pillen scrollen nur noch
|
// Das Org-Detail ist eine durchgehende Seite ohne Unterreiter oder
|
||||||
// zum jeweiligen Bereich, statt Unterreiter umzuschalten.
|
// Sprungmarken, hier bleibt nur der Zurück-Knopf zu verdrahten.
|
||||||
document.querySelectorAll("#orgDetailTabs .nav-tab").forEach(tab => {
|
|
||||||
tab.addEventListener("click", () => {
|
|
||||||
document.querySelectorAll("#orgDetailTabs .nav-tab").forEach(t => t.classList.remove("active"));
|
|
||||||
tab.classList.add("active");
|
|
||||||
const block = document.getElementById(`sub-${tab.dataset.subtab}`);
|
|
||||||
if (block) block.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById("orgBackBtn").addEventListener("click", () => {
|
document.getElementById("orgBackBtn").addEventListener("click", () => {
|
||||||
document.getElementById("orgListView").style.display = "";
|
document.getElementById("orgListView").style.display = "";
|
||||||
document.getElementById("orgDetail").classList.remove("active");
|
document.getElementById("orgDetail").classList.remove("active");
|
||||||
@@ -320,10 +311,6 @@ async function openOrg(orgId) {
|
|||||||
document.getElementById("orgListView").style.display = "none";
|
document.getElementById("orgListView").style.display = "none";
|
||||||
document.getElementById("orgDetail").classList.add("active");
|
document.getElementById("orgDetail").classList.add("active");
|
||||||
|
|
||||||
// Erste Pille aktiv (Verbrauch), Ansicht nach oben
|
|
||||||
document.querySelectorAll("#orgDetailTabs .nav-tab").forEach(t => t.classList.remove("active"));
|
|
||||||
const firstPill = document.querySelector("#orgDetailTabs .nav-tab");
|
|
||||||
if (firstPill) firstPill.classList.add("active");
|
|
||||||
window.scrollTo({ top: 0 });
|
window.scrollTo({ top: 0 });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -978,14 +965,12 @@ async function loadOrgTokenUsage(orgId) {
|
|||||||
el('tokenCreditsUsed', budget.credits_used != null ? Math.round(budget.credits_used).toLocaleString('de-DE') : '-');
|
el('tokenCreditsUsed', budget.credits_used != null ? Math.round(budget.credits_used).toLocaleString('de-DE') : '-');
|
||||||
if (unlimited) {
|
if (unlimited) {
|
||||||
el('tokenCreditsRemaining', '∞ Unbegrenzt');
|
el('tokenCreditsRemaining', '∞ Unbegrenzt');
|
||||||
el('tokenBudgetUsd', '—');
|
|
||||||
} else {
|
} else {
|
||||||
var remainingTxt = budget.credits_remaining != null ? budget.credits_remaining.toLocaleString('de-DE') : '-';
|
var remainingTxt = budget.credits_remaining != null ? budget.credits_remaining.toLocaleString('de-DE') : '-';
|
||||||
if (budget.credits_carried > 0 && budget.credits_remaining != null) {
|
if (budget.credits_carried > 0 && budget.credits_remaining != null) {
|
||||||
remainingTxt += ' (inkl. ' + Math.round(budget.credits_carried).toLocaleString('de-DE') + ' Übertrag)';
|
remainingTxt += ' (inkl. ' + Math.round(budget.credits_carried).toLocaleString('de-DE') + ' Übertrag)';
|
||||||
}
|
}
|
||||||
el('tokenCreditsRemaining', remainingTxt);
|
el('tokenCreditsRemaining', remainingTxt);
|
||||||
el('tokenBudgetUsd', budget.token_budget_usd != null ? '$' + Number(budget.token_budget_usd).toFixed(2) : '-');
|
|
||||||
}
|
}
|
||||||
el('tokenCostUsd', usage.total_cost_usd != null ? '$' + Number(usage.total_cost_usd).toFixed(2) : '-');
|
el('tokenCostUsd', usage.total_cost_usd != null ? '$' + Number(usage.total_cost_usd).toFixed(2) : '-');
|
||||||
|
|
||||||
@@ -1075,14 +1060,6 @@ function fillBudgetForm(budget) {
|
|||||||
onUnlimitedToggle();
|
onUnlimitedToggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Laufzeitwerte (verwaltet der Monitor, hier nur Anzeige)
|
|
||||||
const txt = (id, val) => { const e = document.getElementById(id); if (e) e.textContent = val; };
|
|
||||||
const carried = budget.credits_carried || 0;
|
|
||||||
txt('viewCreditsCarried', carried > 0 ? Math.round(carried).toLocaleString('de-DE') + ' Credits' : 'Kein Übertrag');
|
|
||||||
txt('viewCreditsPeriodStart', budget.credits_period_start || 'Noch nicht initialisiert');
|
|
||||||
txt('viewCreditsAvailable', budget.unlimited_budget
|
|
||||||
? '∞ Unbegrenzt'
|
|
||||||
: (budget.credits_available != null ? Math.round(budget.credits_available).toLocaleString('de-DE') + ' Credits' : '-'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlimited-Toggle: Felder ausgrauen wenn aktiv
|
// Unlimited-Toggle: Felder ausgrauen wenn aktiv
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren