feat(design): AegisSight-Design, Lucide-Icons und aufgeräumter Verbrauchs-Bereich
Drei Pakete auf Nutzerwunsch. 1. Design-Angleichung an das AegisSight-Design-System. Navy/Gold-Token statt Slate/Amber, Light- UND Dark-Theme mit goldenem Schiebeschalter oben rechts (Dark = Standard, Wahl persistiert), Barrierefreiheits- Panel über js/a11y.js (identische Datei wie im Monitor), echtes Logo mit heller Plakette in Kopfzeile und Login, Poppins/Inter-Typografie, Radius 6/8 nach Styleguide. Automatisch erzeugte Light-Theme- Überschreibungen für alle Badge-Pastelltöne. 2. Lucide-Icons (Inline-SVG, keine externen Quellen) für alle Navigations-Tabs inkl. nachgeladenem Verbrauchsrechner, die Anlege-Knöpfe, Abmelden und Speichern. 3. Verbrauchs-Bereich aufgeräumt. Reiter heißt jetzt Verbrauch, Statistik in Kundensicht (Credits) und Interne Kosten (Einkauf, USD) getrennt, Leiste heißt Kontingent-Auslastung, Sätze-Infozeile mit Sprung zum Verbrauchsrechner, Altmodus-Felder (USD) und die Verbrauchs-Korrektur liegen in einem eingeklappten Bereich Erweitert mit Erklärtext. Der Rechner trägt einen deutlichen Simulations-Hinweis. Lizenz verlängern nutzt ein eigenes Modal statt des Browser-prompt(). Cache-Buster auf 20260725 gebumpt, alle Restfarben tokenisiert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -45,6 +45,36 @@ const API = {
|
||||
},
|
||||
};
|
||||
|
||||
// --- Theme (Dark = Standard, Wahl bleibt in localStorage erhalten) ---
|
||||
const ThemeManager = {
|
||||
_key: "portal_theme",
|
||||
init() {
|
||||
const theme = localStorage.getItem(this._key) === "light" ? "light" : "dark";
|
||||
this._apply(theme);
|
||||
},
|
||||
toggle() {
|
||||
const next = document.documentElement.getAttribute("data-theme") === "light" ? "dark" : "light";
|
||||
localStorage.setItem(this._key, next);
|
||||
this._apply(next);
|
||||
},
|
||||
_apply(theme) {
|
||||
if (theme === "light") document.documentElement.setAttribute("data-theme", "light");
|
||||
else document.documentElement.removeAttribute("data-theme");
|
||||
const el = document.getElementById("theme-toggle");
|
||||
if (el) {
|
||||
el.classList.remove("dark", "light");
|
||||
el.classList.add(theme);
|
||||
el.setAttribute("aria-checked", theme === "dark" ? "true" : "false");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Sprung zum (nachgeladenen) Verbrauchsrechner-Tab
|
||||
function openRechnerTab() {
|
||||
const tab = document.querySelector('.nav-tab[data-section="rechner"]');
|
||||
if (tab) tab.click();
|
||||
}
|
||||
|
||||
// --- State ---
|
||||
let currentOrgId = null;
|
||||
let orgsCache = [];
|
||||
@@ -53,6 +83,10 @@ let orgsCache = [];
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (!API.token) { window.location.href = "/"; return; }
|
||||
|
||||
ThemeManager.init();
|
||||
// Barrierefreiheits-Panel (a11y.js, identische Datei wie im Monitor)
|
||||
if (typeof A11yManager !== "undefined") A11yManager.init();
|
||||
|
||||
document.getElementById("headerUser").textContent = localStorage.getItem("username") || "";
|
||||
document.getElementById("logoutBtn").addEventListener("click", logout);
|
||||
|
||||
@@ -368,17 +402,37 @@ async function loadOrgLicenses(orgId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function extendLicense(licId) {
|
||||
const days = prompt("Um wie viele Tage verlängern?", "365");
|
||||
if (!days) return;
|
||||
try {
|
||||
await API.put(`/api/licenses/${licId}/extend?days=${parseInt(days)}`);
|
||||
if (currentOrgId) loadOrgLicenses(currentOrgId);
|
||||
} catch (err) {
|
||||
showToast(err.message, "error");
|
||||
}
|
||||
// Lizenz verlängern über ein eigenes Modal statt des rohen Browser-prompt()
|
||||
let _extendLicenseId = null;
|
||||
function extendLicense(licId) {
|
||||
_extendLicenseId = licId;
|
||||
const errEl = document.getElementById("extendLicenseError");
|
||||
if (errEl) errEl.style.display = "none";
|
||||
openModal("modalExtendLicense");
|
||||
const input = document.getElementById("extendDays");
|
||||
if (input) { input.value = "365"; input.focus(); }
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const form = document.getElementById("extendLicenseForm");
|
||||
if (!form) return;
|
||||
form.addEventListener("submit", async function (e) {
|
||||
e.preventDefault();
|
||||
const errEl = document.getElementById("extendLicenseError");
|
||||
if (errEl) errEl.style.display = "none";
|
||||
const days = parseInt(document.getElementById("extendDays").value);
|
||||
if (!days || days < 1) return;
|
||||
try {
|
||||
await API.put(`/api/licenses/${_extendLicenseId}/extend?days=${days}`);
|
||||
closeModal("modalExtendLicense");
|
||||
if (currentOrgId) loadOrgLicenses(currentOrgId);
|
||||
loadExpiringLicenses();
|
||||
} catch (err) {
|
||||
if (errEl) { errEl.textContent = err.message; errEl.style.display = "block"; }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function confirmRevokeLicense(licId) {
|
||||
showConfirm(
|
||||
"Lizenz widerrufen",
|
||||
@@ -1169,6 +1223,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
// --- Verbrauchsrechner nachladen (eigenstaendiges Modul, siehe rechner.js) ---
|
||||
(function () {
|
||||
var s = document.createElement("script");
|
||||
s.src = "/static/js/rechner.js?v=20260724d";
|
||||
s.src = "/static/js/rechner.js?v=20260725a";
|
||||
document.head.appendChild(s);
|
||||
})();
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren