feat(dashboard): Tailwind CSS + shadcn/ui eingeführt, Dashboard modernisiert
CI / backend-tests (push) Failing after 2m8s
CI / frontend-build (push) Successful in 20s

Kachel-Größe/Ausblenden-Fix (flexBasis pro Kachel statt nur Aufgaben-Kachel, sichtbare Liste ausgeblendeter Kacheln zum Zurückholen). Tailwind v4 + shadcn-Basiskomponenten (Button/Card/Badge) neu eingeführt, gezielt fürs Dashboard-Redesign genutzt (kein Preflight, global.css bleibt für restliche App unangetastet). TileFrame nutzt jetzt Card mit farbigem Akzentbalken je Kritikalität (Mängel/Prüftermine/Ablaufdaten/Einsatzbereitschaft), ReadinessBadge von Emoji auf CSS-Punkte umgestellt, größere Touch-Ziele im Anpassen-Modus, Kiosk-Look im Status-Monitor (Chrome ausgeblendet), "zuletzt aktualisiert"-Anzeige.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-07 12:29:54 +02:00
co-authored by Claude Sonnet 5
parent 5fc8079df6
commit a46897570d
15 changed files with 1094 additions and 59 deletions
+38 -3
View File
@@ -19,9 +19,21 @@ export const STANDARD_REIHENFOLGE: DashboardTileId[] = [
"aktivitaet",
];
export type DashboardTileGroesse = "schmal" | "normal" | "breit" | "voll";
export const GROESSEN_REIHENFOLGE: DashboardTileGroesse[] = ["schmal", "normal", "breit", "voll"];
export const GROESSE_LABEL: Record<DashboardTileGroesse, string> = {
schmal: "Schmal",
normal: "Normal",
breit: "Breit",
voll: "Volle Breite",
};
export interface DashboardLayout {
reihenfolge: DashboardTileId[];
ausgeblendet: DashboardTileId[];
groessen: Partial<Record<DashboardTileId, DashboardTileGroesse>>;
}
const SPEICHER_KEY = "mabea-dashboard-layout";
@@ -30,19 +42,42 @@ function istGueltigeId(wert: unknown): wert is DashboardTileId {
return typeof wert === "string" && (STANDARD_REIHENFOLGE as string[]).includes(wert);
}
function istGueltigeGroesse(wert: unknown): wert is DashboardTileGroesse {
return typeof wert === "string" && (GROESSEN_REIHENFOLGE as string[]).includes(wert);
}
export function layoutLaden(): DashboardLayout {
try {
const roh = localStorage.getItem(SPEICHER_KEY);
if (!roh) return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [] };
if (!roh) return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [], groessen: {} };
const geparst = JSON.parse(roh);
const gespeicherteReihenfolge = Array.isArray(geparst.reihenfolge)
? geparst.reihenfolge.filter(istGueltigeId)
: [];
const fehlende = STANDARD_REIHENFOLGE.filter((id) => !gespeicherteReihenfolge.includes(id));
const ausgeblendet = Array.isArray(geparst.ausgeblendet) ? geparst.ausgeblendet.filter(istGueltigeId) : [];
return { reihenfolge: [...gespeicherteReihenfolge, ...fehlende], ausgeblendet };
const groessen: Partial<Record<DashboardTileId, DashboardTileGroesse>> = {};
if (geparst.groessen && typeof geparst.groessen === "object") {
for (const [id, groesse] of Object.entries(geparst.groessen)) {
if (istGueltigeId(id) && istGueltigeGroesse(groesse)) groessen[id] = groesse;
}
}
return { reihenfolge: [...gespeicherteReihenfolge, ...fehlende], ausgeblendet, groessen };
} catch {
return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [] };
return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [], groessen: {} };
}
}
export function flexBasisFuerGroesse(groesse: DashboardTileGroesse): string {
switch (groesse) {
case "schmal":
return "14rem";
case "breit":
return "28rem";
case "voll":
return "100%";
default:
return "18rem";
}
}