diff --git a/DEVLOG.md b/DEVLOG.md index 6833f9c..9072110 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -6342,3 +6342,78 @@ Keine Commits in dieser Session. - frontend/src/settings/startseite.ts | 30 - --- +## 2026-09-07 12:25 – 12:30 (5m) +**Beschreibung:** Claude Code Session +**Projekt:** frontend + +### Commits +- a468975 feat(dashboard): Tailwind CSS + shadcn/ui eingeführt, Dashboard modernisiert + +### Geänderte Dateien +- DEVLOG.md | 31 ++++ +- frontend/DEVLOG.md | 62 +++++++ +- frontend/package-lock.json | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- +- frontend/package.json | 9 +- +- frontend/src/components/status/ReadinessBadge.tsx | 32 ++-- +- frontend/src/components/ui/badge.tsx | 25 +++ +- frontend/src/components/ui/button.tsx | 42 +++++ +- frontend/src/components/ui/card.tsx | 29 +++ +- frontend/src/lib/utils.ts | 6 + +- frontend/src/main.tsx | 1 + +- frontend/src/pages/dashboard/DashboardPage.tsx | 105 ++++++++--- +- frontend/src/pages/dashboard/TileFrame.tsx | 69 +++++-- +- frontend/src/pages/dashboard/layout.ts | 41 ++++- +- frontend/src/styles/tailwind.css | 23 +++ +- frontend/vite.config.ts | 2 + + +--- +## 2026-09-07 12:38 – 12:39 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- DEVLOG.md | 31 ++++ +- frontend/DEVLOG.md | 62 +++++++ +- frontend/package-lock.json | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- +- frontend/package.json | 9 +- +- frontend/src/components/status/ReadinessBadge.tsx | 32 ++-- +- frontend/src/components/ui/badge.tsx | 25 +++ +- frontend/src/components/ui/button.tsx | 42 +++++ +- frontend/src/components/ui/card.tsx | 29 +++ +- frontend/src/lib/utils.ts | 6 + +- frontend/src/main.tsx | 1 + +- frontend/src/pages/dashboard/DashboardPage.tsx | 105 ++++++++--- +- frontend/src/pages/dashboard/TileFrame.tsx | 69 +++++-- +- frontend/src/pages/dashboard/layout.ts | 41 ++++- +- frontend/src/styles/tailwind.css | 23 +++ +- frontend/vite.config.ts | 2 + + +--- +## 2026-09-07 12:40 – 12:40 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- DEVLOG.md | 31 ++++ +- frontend/DEVLOG.md | 62 +++++++ +- frontend/package-lock.json | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- +- frontend/package.json | 9 +- +- frontend/src/components/status/ReadinessBadge.tsx | 32 ++-- +- frontend/src/components/ui/badge.tsx | 25 +++ +- frontend/src/components/ui/button.tsx | 42 +++++ +- frontend/src/components/ui/card.tsx | 29 +++ +- frontend/src/lib/utils.ts | 6 + +- frontend/src/main.tsx | 1 + +- frontend/src/pages/dashboard/DashboardPage.tsx | 105 ++++++++--- +- frontend/src/pages/dashboard/TileFrame.tsx | 69 +++++-- +- frontend/src/pages/dashboard/layout.ts | 41 ++++- +- frontend/src/styles/tailwind.css | 23 +++ +- frontend/vite.config.ts | 2 + + +--- diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8408472..af5f9b7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,9 @@ "name": "mabea-frontend", "version": "0.1.0", "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@radix-ui/react-slot": "^1.3.3", "@zxing/browser": "^0.1.5", "class-variance-authority": "^0.7.1", @@ -1622,6 +1625,59 @@ "node": ">=6.9.0" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -6272,6 +6328,12 @@ "node": ">=14.0.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-fest": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9fa6a90..3df0193 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,9 @@ "test:e2e:ui": "playwright test --ui" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@radix-ui/react-slot": "^1.3.3", "@zxing/browser": "^0.1.5", "class-variance-authority": "^0.7.1", diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx index 8dcc04b..f65a2a9 100644 --- a/frontend/src/components/ui/card.tsx +++ b/frontend/src/components/ui/card.tsx @@ -5,7 +5,7 @@ import { cn } from "../../lib/utils"; export const Card = forwardRef>(({ className, ...props }, ref) => (
)); diff --git a/frontend/src/pages/dashboard/DashboardPage.tsx b/frontend/src/pages/dashboard/DashboardPage.tsx index 2711baa..6db3a1c 100644 --- a/frontend/src/pages/dashboard/DashboardPage.tsx +++ b/frontend/src/pages/dashboard/DashboardPage.tsx @@ -1,3 +1,6 @@ +import type { DragEndEvent } from "@dnd-kit/core"; +import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; +import { arrayMove, rectSortingStrategy, SortableContext, sortableKeyboardCoordinates } from "@dnd-kit/sortable"; import { useCallback, useEffect, useState } from "react"; import { apiRequest } from "../../api/client"; @@ -86,13 +89,18 @@ export function DashboardPage() { return () => document.removeEventListener("fullscreenchange", fullscreenGeaendert); }, []); - function tileVerschieben(id: DashboardTileId, richtung: -1 | 1) { - const index = layout.reihenfolge.indexOf(id); - const zielIndex = index + richtung; - if (zielIndex < 0 || zielIndex >= layout.reihenfolge.length) return; - const neueReihenfolge = [...layout.reihenfolge]; - [neueReihenfolge[index], neueReihenfolge[zielIndex]] = [neueReihenfolge[zielIndex], neueReihenfolge[index]]; - const neuesLayout = { ...layout, reihenfolge: neueReihenfolge }; + const sensoren = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), + useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) + ); + + function dragEnde(event: DragEndEvent) { + const { active, over } = event; + if (!over || active.id === over.id) return; + const von = layout.reihenfolge.indexOf(active.id as DashboardTileId); + const nach = layout.reihenfolge.indexOf(over.id as DashboardTileId); + if (von === -1 || nach === -1) return; + const neuesLayout = { ...layout, reihenfolge: arrayMove(layout.reihenfolge, von, nach) }; setLayout(neuesLayout); layoutSpeichern(neuesLayout); } @@ -242,34 +250,35 @@ export function DashboardPage() {
)} {!laedt && bereitschaft && ( -
- {layout.reihenfolge.map((id, index) => { - const ausgeblendet = layout.ausgeblendet.includes(id); - if (ausgeblendet && !bearbeitenModus) return null; - const inhalt = tileInhalt(id); - if (!inhalt) return null; - const groesse: DashboardTileGroesse = layout.groessen[id] ?? (id === "aufgaben" ? "voll" : "normal"); - return ( - 0} - kannRunterVerschieben={index < layout.reihenfolge.length - 1} - onAusblendenUmschalten={() => ausblendenUmschalten(id)} - onGroesseAendern={() => groesseAendern(id)} - onHoch={() => tileVerschieben(id, -1)} - onRunter={() => tileVerschieben(id, 1)} - > - {inhalt} - - ); - })} -
+ + +
+ {layout.reihenfolge.map((id) => { + const ausgeblendet = layout.ausgeblendet.includes(id); + if (ausgeblendet && !bearbeitenModus) return null; + const inhalt = tileInhalt(id); + if (!inhalt) return null; + const groesse: DashboardTileGroesse = layout.groessen[id] ?? (id === "aufgaben" ? "voll" : "normal"); + return ( + ausblendenUmschalten(id)} + onGroesseAendern={() => groesseAendern(id)} + > + {inhalt} + + ); + })} +
+
+
)} ); diff --git a/frontend/src/pages/dashboard/TileFrame.tsx b/frontend/src/pages/dashboard/TileFrame.tsx index 48b722c..37e31ac 100644 --- a/frontend/src/pages/dashboard/TileFrame.tsx +++ b/frontend/src/pages/dashboard/TileFrame.tsx @@ -1,9 +1,11 @@ +import { useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; import type { ReactNode } from "react"; import { Button } from "../../components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card"; import { cn } from "../../lib/utils"; -import { GROESSE_LABEL, type DashboardTileGroesse } from "./layout"; +import { GROESSE_LABEL, type DashboardTileGroesse, type DashboardTileId } from "./layout"; export type TileAkzent = "neutral" | "warning" | "danger"; @@ -14,65 +16,63 @@ const AKZENT_KLASSE: Record = { }; interface Props { + id: DashboardTileId; title: string; flexBasis: string; groesse: DashboardTileGroesse; akzent?: TileAkzent; bearbeitenAktiv: boolean; ausgeblendet: boolean; - kannHochVerschieben: boolean; - kannRunterVerschieben: boolean; onAusblendenUmschalten: () => void; onGroesseAendern: () => void; - onHoch: () => void; - onRunter: () => void; children: ReactNode; } export function TileFrame({ + id, title, flexBasis, groesse, akzent = "neutral", bearbeitenAktiv, ausgeblendet, - kannHochVerschieben, - kannRunterVerschieben, onAusblendenUmschalten, onGroesseAendern, - onHoch, - onRunter, children, }: Props) { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ + id, + disabled: !bearbeitenAktiv, + }); + return ( - {title} +
+ {bearbeitenAktiv && ( + + )} + {title} +
{bearbeitenAktiv && (
- - diff --git a/frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx b/frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx index a0f3744..12ab5e0 100644 --- a/frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx +++ b/frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx @@ -6,14 +6,17 @@ interface Props { export function KennzahlenTile({ kennzahlen }: Props) { return ( -
    -
  • Offene Fehlbestände: {kennzahlen.anzahl_offener_fehlbestaende}
  • -
  • Genehmigte Mindermengen: {kennzahlen.anzahl_genehmigter_mindermengen}
  • -
  • - Kürzlich erledigt (7 Tage): {kennzahlen.anzahl_kuerzlich_erledigter_fehlbestaende} -
  • -
  • Problematische Objekte: {kennzahlen.anzahl_problematischer_objekte}
  • -
  • Objekte gesamt: {kennzahlen.anzahl_objekte_gesamt}
  • -
+
+

{kennzahlen.anzahl_offener_fehlbestaende}

+

Offene Fehlbestände

+
    +
  • Genehmigte Mindermengen: {kennzahlen.anzahl_genehmigter_mindermengen}
  • +
  • + Kürzlich erledigt (7 Tage): {kennzahlen.anzahl_kuerzlich_erledigter_fehlbestaende} +
  • +
  • Problematische Objekte: {kennzahlen.anzahl_problematischer_objekte}
  • +
  • Objekte gesamt: {kennzahlen.anzahl_objekte_gesamt}
  • +
+
); } diff --git a/frontend/src/pages/dashboard/tiles/MaengelTile.tsx b/frontend/src/pages/dashboard/tiles/MaengelTile.tsx index 6eb358c..6133076 100644 --- a/frontend/src/pages/dashboard/tiles/MaengelTile.tsx +++ b/frontend/src/pages/dashboard/tiles/MaengelTile.tsx @@ -15,16 +15,13 @@ export function MaengelTile({ maengel, maengelNeuAnzahl, maengelKritischAnzahl } return ( <> -

{maengelOffenListe.length} offen

+

{maengelOffenListe.length}

+

Offen

{maengelNeuAnzahl > 0 && ( -

- {maengelNeuAnzahl} neu gemeldet -

+

{maengelNeuAnzahl} neu gemeldet

)} {maengelKritischAnzahl > 0 && ( -

- {maengelKritischAnzahl} kritisch (blockiert Einsatzbereitschaft) -

+

{maengelKritischAnzahl} kritisch (blockiert Einsatzbereitschaft)

)} {maengelOffenListe.length > 0 && ( <>