From d38c7adbc79d1f66405d728861ed019d91c8c7df Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 7 Sep 2026 22:38:36 +0200 Subject: [PATCH] feat(dashboard): Design-Review umgesetzt - EmptyStates, Statuslabel, Monitor-Skalierung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Einheitliche EmptyState-Komponente statt uneinheitlicher Empty-Texte pro Kachel, gemeinsames Listen-Pattern (Inline-Styles entfernt, u.a. KennzahlenTile, MaengelTile, AktivitaetTile, AufgabenTile), Status-Textlabel im Kachel-Header zusätzlich zur Randfarbe (Kontrast/Distanz/Farbfehlsichtigkeit), Status-Monitor-Modus jetzt per zoom vergrößert für Wandmonitor-Lesbarkeit. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV --- .../src/pages/dashboard/DashboardPage.tsx | 2 +- frontend/src/pages/dashboard/EmptyState.tsx | 15 +++++++++++ frontend/src/pages/dashboard/TileFrame.tsx | 26 ++++++++++++++++++- .../pages/dashboard/tiles/AblaufdatenTile.tsx | 3 ++- .../pages/dashboard/tiles/AktivitaetTile.tsx | 16 +++++++----- .../pages/dashboard/tiles/AufgabenTile.tsx | 12 ++++----- .../pages/dashboard/tiles/KennzahlenTile.tsx | 26 +++++++++++++------ .../src/pages/dashboard/tiles/MaengelTile.tsx | 26 +++++++++++-------- .../dashboard/tiles/PrueftermineTile.tsx | 3 ++- .../dashboard/tiles/QualifikationTile.tsx | 3 ++- frontend/src/styles/global.css | 11 ++++++++ 11 files changed, 106 insertions(+), 37 deletions(-) create mode 100644 frontend/src/pages/dashboard/EmptyState.tsx diff --git a/frontend/src/pages/dashboard/DashboardPage.tsx b/frontend/src/pages/dashboard/DashboardPage.tsx index 14d2281..ee47fc8 100644 --- a/frontend/src/pages/dashboard/DashboardPage.tsx +++ b/frontend/src/pages/dashboard/DashboardPage.tsx @@ -210,7 +210,7 @@ export function DashboardPage() { ); return ( -
+
{!monitorModus && (
diff --git a/frontend/src/pages/dashboard/EmptyState.tsx b/frontend/src/pages/dashboard/EmptyState.tsx new file mode 100644 index 0000000..679c4ba --- /dev/null +++ b/frontend/src/pages/dashboard/EmptyState.tsx @@ -0,0 +1,15 @@ +interface Props { + text: string; + icon?: string; +} + +export function EmptyState({ text, icon = "✓" }: Props) { + return ( +
+ + {icon} + +

{text}

+
+ ); +} diff --git a/frontend/src/pages/dashboard/TileFrame.tsx b/frontend/src/pages/dashboard/TileFrame.tsx index 1ecafb9..40ce688 100644 --- a/frontend/src/pages/dashboard/TileFrame.tsx +++ b/frontend/src/pages/dashboard/TileFrame.tsx @@ -12,6 +12,18 @@ const AKZENT_KLASSE: Record = { danger: "border-l-[var(--color-danger)]", }; +// Statusfarbe nie allein über die Randfarbe transportieren (Distanz/Wandmonitor, +// Farbfehlsichtigkeit) - zusätzliches Text-Label neben dem Titel. +const AKZENT_LABEL: Record, string> = { + warning: "Achtung", + danger: "Kritisch", +}; + +const AKZENT_LABEL_KLASSE: Record, string> = { + warning: "bg-[var(--color-warning)]/15 text-[var(--color-warning)]", + danger: "bg-[var(--color-danger)]/15 text-[var(--color-danger)]", +}; + interface Props extends HTMLAttributes { title: string; akzent?: TileAkzent; @@ -47,7 +59,19 @@ export const TileFrame = memo( {...rest} > - {title} +
+ {title} + {akzent !== "neutral" && ( + + {AKZENT_LABEL[akzent]} + + )} +
{bearbeitenAktiv && ( {maengelOffen && ( -
    +
      {maengelOffenListe.map((m) => ( -
    • - +
    • + {m.beschreibung} - {m.status === "neu" && ( - - neu - - )} + {m.status === "neu" && neu} - + {m.prioritaet}
    • @@ -49,7 +53,7 @@ function MaengelTileKomponente({ maengel, maengelNeuAnzahl, maengelKritischAnzah )} )} - {maengelOffenListe.length === 0 &&

      Keine offenen Mängel.

      } + {maengelOffenListe.length === 0 && } ); } diff --git a/frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx b/frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx index 08d84a8..a3372de 100644 --- a/frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx +++ b/frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx @@ -2,6 +2,7 @@ import { memo, useState } from "react"; import { Link } from "react-router-dom"; import { InspectionStatus } from "../../../components/status/InspectionStatus"; +import { EmptyState } from "../EmptyState"; import type { Prueftermineintrag } from "../types"; const ANZAHL_OHNE_AUFKLAPPEN = 5; @@ -14,7 +15,7 @@ function PrueftermineTileKomponente({ prueftermine }: Props) { const [alleAnzeigen, setAlleAnzeigen] = useState(false); if (prueftermine.length === 0) { - return

      Keine anstehenden oder überfälligen Geräteprüfungen.

      ; + return ; } const sichtbare = alleAnzeigen ? prueftermine : prueftermine.slice(0, ANZAHL_OHNE_AUFKLAPPEN); diff --git a/frontend/src/pages/dashboard/tiles/QualifikationTile.tsx b/frontend/src/pages/dashboard/tiles/QualifikationTile.tsx index 81d507f..397019d 100644 --- a/frontend/src/pages/dashboard/tiles/QualifikationTile.tsx +++ b/frontend/src/pages/dashboard/tiles/QualifikationTile.tsx @@ -1,6 +1,7 @@ import { memo, useState } from "react"; import { FristBadge } from "../../../components/status/FristBadge"; +import { EmptyState } from "../EmptyState"; import type { Qualifikationsablauf } from "../types"; const ANZAHL_OHNE_AUFKLAPPEN = 5; @@ -13,7 +14,7 @@ function QualifikationTileKomponente({ qualifikationsablaeufe }: Props) { const [alleAnzeigen, setAlleAnzeigen] = useState(false); if (qualifikationsablaeufe.length === 0) { - return

      Keine bald ablaufenden Qualifikationen.

      ; + return ; } const sichtbare = alleAnzeigen ? qualifikationsablaeufe : qualifikationsablaeufe.slice(0, ANZAHL_OHNE_AUFKLAPPEN); diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 804576d..93b5095 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -288,6 +288,17 @@ a { padding: 1.75rem 2rem 3rem; } +/* Status-Monitor-Modus (Dashboard dauerhaft auf Wandmonitor, Betrachtung aus + mehreren Metern Entfernung) - alles per zoom vergrößern statt einzelne + Tailwind-rem-Klassen zu duplizieren (rem ist immer relativ zum Root-, + Container-Schriftgröße ändern hätte also keine Wirkung auf Tailwind-Text- + /Abstandsklassen). zoom skaliert das gesamte gerenderte Layout inkl. + react-grid-layout-Raster gemeinsam mit den echten Pixelmaßen. */ +[data-monitor="true"] { + max-width: none; + zoom: 1.3; +} + /* -- Cards ---------------------------------------------------------------- */ .card {