P42 Dashboard: erster Stand

FastAPI-Backend und React-Frontend mit Konten, Sendungsverfolgung,
Projekt-Artikeln, Vorrat & Einkauf mit Rezeptabgleich, Reise-Weltkarte,
Kanban-Projekten mit Priorität, Server-Monitoring und den Designs
Matrix und Blackwall. Betrieb per Docker hinter nginx (siehe DEPLOY.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-14 14:08:11 +02:00
commit dd58b3d1f8
75 files changed
+11418

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
// Designs der Oberfläche. Die Auswahl wird pro Browser gespeichert; index.html setzt sie vor dem ersten
// Zeichnen (kein Aufblitzen des falschen Designs), dieses Modul hält sie danach aktuell.
const KEY = 'p42.design';
const EVENT = 'p42:design';
export const THEMES = [
{ key: 'matrix', name: 'Matrix', hint: 'Rotes Retro-Terminal', swatch: ['#ff3a3a', '#1d0505'], color: '#050000' },
{ key: 'blackwall', name: 'Blackwall', hint: 'Cyberpunk-Netrunner', swatch: ['#ff2d6f', '#3ff6ff', '#1a0508'], color: '#060104' },
];
const valid = (key) => THEMES.some((t) => t.key === key);
export function getTheme() {
const current = document.documentElement.dataset.theme;
return valid(current) ? current : 'matrix';
}
export function setTheme(key) {
if (!valid(key)) return;
try { localStorage.setItem(KEY, key); } catch { /* ignorieren */ }
const still = document.documentElement.classList.contains('calm')
|| window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Überblenden, wo der Browser View Transitions kann; kurz warten, bis React den neuen Hintergrund gezeichnet hat
if (document.startViewTransition && !still) {
document.startViewTransition(async () => {
apply(key);
await new Promise((resolve) => { setTimeout(resolve, 40); });
});
} else {
apply(key);
}
}
export function onThemeChange(fn) {
window.addEventListener(EVENT, fn);
return () => window.removeEventListener(EVENT, fn);
}
function apply(key) {
document.documentElement.dataset.theme = key;
// Browser-Leiste (Handy) passend einfärben
document.querySelector('meta[name="theme-color"]')?.setAttribute('content', THEMES.find((t) => t.key === key).color);
window.dispatchEvent(new Event(EVENT));
}
apply(getTheme());