From 3b818bd1c6e31c1327eb2faaa55bf32233f1fc88 Mon Sep 17 00:00:00 2001 From: schakal00783 Date: Fri, 18 Sep 2026 11:49:36 +0200 Subject: [PATCH] fix(projekte): Kacheln als durchgehendes Raster statt Kundengruppen Jede Kundenueberschrift begann eine neue Rasterzeile. Bei einem Projekt je Kunde stand dadurch eine Kachel allein in der Zeile und rechts blieb der Platz leer - auf der Produktion mit fuenf Kunden untereinander sichtbar. Die Kacheln laufen jetzt in einem Raster, sortiert nach Kunde und Projekt. Der Kundenname steht dafuer neben dem Projektcode auf der Kachel selbst. Die Detailansicht steht wie in der Liste unter der Uebersicht. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 7 ++- src/components/projects/Projects.tsx | 81 +++++++++++++--------------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 0a3d1df..dc51859 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,11 @@ Dieselbe Komponente wird an drei Stellen benutzt: ein gesetzter Kundenfilter wird übernommen (`GET /api/entries?...&customer_id=`). - **Kunden**: Umschalter *Karten / Liste* mit Ort, Projektanzahl, Stunden, Umsatz erbracht/offen und Team. -`src/components/projects/ProjectTable.tsx` ist die Listenansicht der Projekte (Umschalter *Karten / Liste*): flach über alle -Kunden statt nach Kunde gruppiert, sortierbar nach Projekt, Kunde, Typ, gebuchten Stunden, Budget, Verbrauch und offenem Betrag. +`src/components/projects/ProjectTable.tsx` ist die Listenansicht der Projekte (Umschalter *Karten / Liste*): +sortierbar nach Projekt, Kunde, Typ, gebuchten Stunden, Budget, Verbrauch und offenem Betrag. +Die Kachelansicht läuft als ein durchgehendes Raster, nach Kunde und Projekt sortiert, mit dem Kundennamen auf der Kachel. +Eine Überschrift je Kunde gab es früher, sie zwang aber jede Gruppe in eine neue Zeile - bei einem Projekt je Kunde blieb der +Rest der Zeile leer. Ein Klick auf eine Zeile öffnet dieselbe Detailansicht wie eine Kachel, hier unterhalb der Tabelle. Beträge sieht nur, wer mindestens Vorgesetzter ist. diff --git a/src/components/projects/Projects.tsx b/src/components/projects/Projects.tsx index 75a730b..c2da29a 100644 --- a/src/components/projects/Projects.tsx +++ b/src/components/projects/Projects.tsx @@ -50,14 +50,16 @@ export const Projects: React.FC = () => { ); }, [list, search, customerFilter, status]); - const groups = useMemo(() => { - const map = new Map(); - for (const p of filtered) { - if (!map.has(p.customer_id)) map.set(p.customer_id, { customer: p.customer, projects: [] }); - map.get(p.customer_id)!.projects.push(p); - } - return [...map.values()].sort((a, b) => a.customer.name.localeCompare(b.customer.name, 'de')); - }, [filtered]); + // Kacheln laufen in einem durchgehenden Raster, nach Kunde und Projekt + // sortiert. Eine Ueberschrift je Kunde wuerde jede Gruppe in eine neue Zeile + // zwingen - bei einem Projekt pro Kunde bliebe der Rest der Zeile leer. + const ordered = useMemo( + () => + [...filtered].sort( + (a, b) => a.customer.name.localeCompare(b.customer.name, 'de') || a.name.localeCompare(b.name, 'de') + ), + [filtered] + ); const selected = list.find((p) => p.id === selectedId) ?? null; const customerOptions = useMemo(() => [...new Map(list.map((p) => [p.customer_id, p.customer])).values()].sort((a, b) => a.name.localeCompare(b.name, 'de')), [list]); @@ -72,8 +74,7 @@ export const Projects: React.FC = () => { if (projects.error && !projects.data) return ; if (!projects.data) return ; - // Die Detailansicht steht in der Kachelansicht unter ihrer Kundengruppe, - // in der Liste unter der Tabelle - der Inhalt ist derselbe. + // Die Detailansicht steht in beiden Ansichten unter der Uebersicht. const detail = selected && ( { )} - {groups.length === 0 ? ( + {ordered.length === 0 ? ( { )} ) : ( -
- {groups.map((group, gi) => ( -
-
- -

{group.customer.name}

- {group.projects.length} {group.projects.length === 1 ? 'Projekt' : 'Projekte'} -
-
- {group.projects - .sort((a, b) => a.name.localeCompare(b.name, 'de')) - .map((p, i) => ( - navigate(p.id === selectedId ? 'projekte' : `projekte/${p.id}`)} - /> - ))} -
- {selected && selected.customer_id === group.customer.id && ( -
- {detail} -
- )} -
- ))} +
+
+ {ordered.map((p, i) => ( + navigate(p.id === selectedId ? 'projekte' : `projekte/${p.id}`)} + /> + ))} +
+ {selected && ( +
+ {detail} +
+ )}
)} @@ -236,7 +226,12 @@ const ProjectCard: React.FC<{
-

{p.code}

+ {/* Ohne Kundengruppen steht der Kunde auf der Kachel selbst. */} +

+ {p.code} + · + {p.customer.name} +

{p.display_name}