feat(org-detail): Verbrauchs-Korrektur direkt an der Kachel, Zahlenfelder ohne Pfeile

Die Korrektur des Verbrauchszählers sitzt jetzt direkt an der Kachel
"Verbrauchte Credits", wo man den Zähler sieht, als Eingabefeld mit
Korrigieren-Knopf und Sicherheitsrückfrage. Das Feld im Kontingent-
Formular entfällt dafür. Außerdem sind die Hochzähl-Pfeile an allen
Zahlenfeldern des Portals entfernt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-07-25 16:33:24 +00:00
Ursprung 73ae7be76d
Commit 50a8237e96
3 geänderte Dateien mit 47 neuen und 11 gelöschten Zeilen

Datei anzeigen

@@ -1464,3 +1464,13 @@ details.adv-section .adv-hint { grid-column: 1 / -1; font-size: 12px; color: var
background: rgba(239, 68, 68, 0.16); background: rgba(239, 68, 68, 0.16);
color: var(--danger); color: var(--danger);
} }
/* === Verbrauchs-Korrektur direkt an der Kachel === */
.stat-korrektur { display: flex; gap: 6px; margin-top: 10px; }
.stat-korrektur input { font-size: 12px; padding: 6px 8px; }
/* === Zahlenfelder ohne Hochzaehl-Pfeile (portalweit) === */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

Datei anzeigen

@@ -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=20260725f"> <link rel="stylesheet" href="/static/css/style.css?v=20260725g">
<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>
@@ -162,6 +162,10 @@
<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>
<div class="token-stat-value" id="tokenCreditsUsed">-</div> <div class="token-stat-value" id="tokenCreditsUsed">-</div>
<div class="stat-korrektur" title="Überschreibt den Verbrauchszähler direkt, z.B. für Kulanz (Zähler runtersetzen = Credits zurückgeben). Der Kunde sieht die Änderung sofort.">
<input type="number" id="korrCreditsUsed" min="0" placeholder="Zähler neu setzen">
<button type="button" class="btn btn-secondary btn-small" onclick="korrigiereVerbrauch()">Korrigieren</button>
</div>
</div> </div>
<div class="token-stat-card"> <div class="token-stat-card">
<div class="token-stat-label">Verbleibend</div> <div class="token-stat-label">Verbleibend</div>
@@ -259,11 +263,6 @@
<label for="editWarnPercent">Warnschwelle (%, 0 = aus)</label> <label for="editWarnPercent">Warnschwelle (%, 0 = aus)</label>
<input type="number" id="editWarnPercent" min="0" max="100" placeholder="80"> <input type="number" id="editWarnPercent" min="0" max="100" placeholder="80">
</div> </div>
<div class="form-group">
<label for="editCreditsUsed">Verbrauch korrigieren (Credits, laufende Periode)</label>
<input type="number" id="editCreditsUsed" step="0.01" placeholder="Aktueller Verbrauch">
<small class="text-secondary">Überschreibt den Verbrauchszähler direkt, z.B. für Kulanz ("Zähler runtersetzen = Credits zurückgeben"). Der Kunde sieht die Änderung sofort.</small>
</div>
<div style="grid-column:1/-1; display:flex; gap:8px; align-items:center;"> <div style="grid-column:1/-1; display:flex; gap:8px; align-items:center;">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
<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"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></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"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
@@ -1122,7 +1121,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=20260725l"></script> <script src="/static/js/app.js?v=20260725m"></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>

Datei anzeigen

@@ -386,6 +386,37 @@ function setOrgLanguage(wert) {
_saveOrgSetting({ output_language: wert }, "Pipeline-Sprache gespeichert"); _saveOrgSetting({ output_language: wert }, "Pipeline-Sprache gespeichert");
} }
// Verbrauchszähler direkt an der Kachel korrigieren (Kulanz, Fehlbereinigung)
async function korrigiereVerbrauch() {
const input = document.getElementById("korrCreditsUsed");
if (!input || input.value === "") {
showToast("Bitte den neuen Zählerstand eintragen", "error");
return;
}
const n = parseFloat(input.value);
if (isNaN(n) || n < 0) {
showToast("Der Zählerstand muss 0 oder größer sein", "error");
return;
}
showConfirm(
"Verbrauch korrigieren",
"Verbrauchszähler dieser Organisation auf " + n.toLocaleString("de-DE") + " Credits setzen? Der Kunde sieht die Änderung sofort.",
async () => {
try {
const licenses = await API.get("/api/licenses?org_id=" + currentOrgId);
const aktiv = licenses.find(l => l.status === "active");
if (!aktiv) { showToast("Keine aktive Lizenz gefunden", "error"); return; }
await API.put("/api/token-usage/budget/" + aktiv.id, { credits_used: n });
input.value = "";
showToast("Verbrauchszähler gesetzt", "success");
loadOrgTokenUsage(currentOrgId);
} catch (err) {
showToast(err.message, "error");
}
}
);
}
// Max Nutzer der aktiven Lizenz direkt in der Tabelle ändern // Max Nutzer der aktiven Lizenz direkt in der Tabelle ändern
async function setLicenseMaxUsers(licId, wert) { async function setLicenseMaxUsers(licId, wert) {
const n = parseInt(wert, 10); const n = parseInt(wert, 10);
@@ -1124,7 +1155,6 @@ async function loadOrgTokenUsage(orgId) {
function fillBudgetForm(budget) { function fillBudgetForm(budget) {
const el = (id, val) => { const e = document.getElementById(id); if (e && val != null) e.value = val; }; const el = (id, val) => { const e = document.getElementById(id); if (e && val != null) e.value = val; };
el('editCreditsTotal', budget.credits_total); el('editCreditsTotal', budget.credits_total);
el('editCreditsUsed', budget.credits_used ? Math.round(budget.credits_used) : 0);
el('editWarnPercent', budget.budget_warning_percent); el('editWarnPercent', budget.budget_warning_percent);
const periodSel = document.getElementById('editCreditsPeriod'); const periodSel = document.getElementById('editCreditsPeriod');
if (periodSel) periodSel.value = budget.credits_period || 'monthly'; if (periodSel) periodSel.value = budget.credits_period || 'monthly';
@@ -1282,7 +1312,6 @@ document.addEventListener('DOMContentLoaded', function() {
body.unlimited_budget = isUnlimited; body.unlimited_budget = isUnlimited;
var creditsTotal = document.getElementById('editCreditsTotal'); var creditsTotal = document.getElementById('editCreditsTotal');
var creditsUsed = document.getElementById('editCreditsUsed');
if (!isUnlimited) { if (!isUnlimited) {
if (creditsTotal && creditsTotal.value) body.credits_total = parseInt(creditsTotal.value); if (creditsTotal && creditsTotal.value) body.credits_total = parseInt(creditsTotal.value);
@@ -1295,8 +1324,6 @@ document.addEventListener('DOMContentLoaded', function() {
var warnPercent = document.getElementById('editWarnPercent'); var warnPercent = document.getElementById('editWarnPercent');
if (warnPercent && warnPercent.value) body.budget_warning_percent = parseInt(warnPercent.value); if (warnPercent && warnPercent.value) body.budget_warning_percent = parseInt(warnPercent.value);
} }
// credits_used immer mitsenden (auch im Unlimited-Modus, fuer Korrekturen)
if (creditsUsed && creditsUsed.value !== '') body.credits_used = parseFloat(creditsUsed.value);
var result = await API.put('/api/token-usage/budget/' + activeLic.id, body); var result = await API.put('/api/token-usage/budget/' + activeLic.id, body);
if (msgEl) { if (msgEl) {