From 3ce8adfa073c251fa5724258399a4bb158346ec5 Mon Sep 17 00:00:00 2001 From: Matthias Hochmeister Date: Tue, 24 Mar 2026 10:07:35 +0100 Subject: [PATCH] rework internal order system --- .../services/ausruestungsanfrage.service.ts | 8 ++-- frontend/src/pages/Ausruestungsanfrage.tsx | 46 +++++++++++++++++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/backend/src/services/ausruestungsanfrage.service.ts b/backend/src/services/ausruestungsanfrage.service.ts index f8b8315..02345b0 100644 --- a/backend/src/services/ausruestungsanfrage.service.ts +++ b/backend/src/services/ausruestungsanfrage.service.ts @@ -286,8 +286,8 @@ async function getRequests(filters?: { status?: string; anfrager_id?: string }) const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; const result = await pool.query( `SELECT a.*, - u.display_name AS anfrager_name, - u2.display_name AS bearbeitet_von_name, + COALESCE(u.given_name || ' ' || u.family_name, u.name) AS anfrager_name, + COALESCE(u2.given_name || ' ' || u2.family_name, u2.name) AS bearbeitet_von_name, (SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id) AS positionen_count FROM ausruestung_anfragen a LEFT JOIN users u ON u.id = a.anfrager_id @@ -314,8 +314,8 @@ async function getMyRequests(userId: string) { async function getRequestById(id: number) { const reqResult = await pool.query( `SELECT a.*, - u.display_name AS anfrager_name, - u2.display_name AS bearbeitet_von_name + COALESCE(u.given_name || ' ' || u.family_name, u.name) AS anfrager_name, + COALESCE(u2.given_name || ' ' || u2.family_name, u2.name) AS bearbeitet_von_name FROM ausruestung_anfragen a LEFT JOIN users u ON u.id = a.anfrager_id LEFT JOIN users u2 ON u2.id = a.bearbeitet_von diff --git a/frontend/src/pages/Ausruestungsanfrage.tsx b/frontend/src/pages/Ausruestungsanfrage.tsx index 79f20e6..32e617a 100644 --- a/frontend/src/pages/Ausruestungsanfrage.tsx +++ b/frontend/src/pages/Ausruestungsanfrage.tsx @@ -725,6 +725,10 @@ function KatalogTab() { queryFn: () => ausruestungsanfrageApi.getKategorien(), }); + // Split categories into top-level and children + const topKategorien = useMemo(() => kategorien.filter(k => !k.parent_id), [kategorien]); + const subKategorienOf = useCallback((parentId: number) => kategorien.filter(k => k.parent_id === parentId), [kategorien]); + // Build display names for hierarchical categories (e.g. "Kleidung > A-Uniform") const kategorieOptions = useMemo(() => { const map = new Map(kategorien.map(k => [k.id, k])); @@ -738,6 +742,10 @@ function KatalogTab() { return kategorien.map(k => ({ id: k.id, name: getDisplayName(k), isChild: !!k.parent_id })); }, [kategorien]); + // For artikel dialog: track main + sub category separately + const [artikelMainKat, setArtikelMainKat] = useState(''); + const artikelSubKats = useMemo(() => artikelMainKat ? subKategorienOf(artikelMainKat as number) : [], [artikelMainKat, subKategorienOf]); + const createItemMut = useMutation({ mutationFn: (data: AusruestungArtikelFormData) => ausruestungsanfrageApi.createItem(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] }); showSuccess('Artikel erstellt'); setArtikelDialogOpen(false); }, @@ -757,11 +765,19 @@ function KatalogTab() { const openNewArtikel = () => { setEditArtikel(null); setArtikelForm({ bezeichnung: '' }); + setArtikelMainKat(''); setArtikelDialogOpen(true); }; const openEditArtikel = (a: AusruestungArtikel) => { setEditArtikel(a); setArtikelForm({ bezeichnung: a.bezeichnung, beschreibung: a.beschreibung, kategorie_id: a.kategorie_id ?? null }); + // Determine main category (could be the item's category or its parent) + const kat = kategorien.find(k => k.id === a.kategorie_id); + if (kat?.parent_id) { + setArtikelMainKat(kat.parent_id); + } else { + setArtikelMainKat(a.kategorie_id || ''); + } setArtikelDialogOpen(true); }; const saveArtikel = () => { @@ -846,14 +862,36 @@ function KatalogTab() { setArtikelForm(f => ({ ...f, beschreibung: e.target.value }))} /> setArtikelForm(f => ({ ...f, kategorie_id: e.target.value ? Number(e.target.value) : null }))} + label="Hauptkategorie" + value={artikelMainKat} + onChange={e => { + const val = e.target.value ? Number(e.target.value) : ''; + setArtikelMainKat(val); + // If no subcategories, set kategorie_id to main; otherwise clear it + if (val) { + const subs = subKategorienOf(val as number); + setArtikelForm(f => ({ ...f, kategorie_id: subs.length === 0 ? (val as number) : null })); + } else { + setArtikelForm(f => ({ ...f, kategorie_id: null })); + } + }} fullWidth > Keine - {kategorieOptions.map(k => {k.name})} + {topKategorien.map(k => {k.name})} + {artikelMainKat && artikelSubKats.length > 0 && ( + setArtikelForm(f => ({ ...f, kategorie_id: e.target.value ? Number(e.target.value) : (artikelMainKat as number) }))} + fullWidth + > + Keine (nur Hauptkategorie) + {artikelSubKats.map(k => {k.name})} + + )} {canManage && }