rework internal order system

This commit is contained in:
Matthias Hochmeister
2026-03-24 10:07:35 +01:00
parent 0389c3d2aa
commit 3ce8adfa07
2 changed files with 46 additions and 8 deletions

View File

@@ -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

View File

@@ -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<number | ''>('');
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() {
<TextField label="Beschreibung" multiline rows={2} value={artikelForm.beschreibung ?? ''} onChange={e => setArtikelForm(f => ({ ...f, beschreibung: e.target.value }))} />
<TextField
select
label="Kategorie"
value={artikelForm.kategorie_id ?? ''}
onChange={e => 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
>
<MenuItem value="">Keine</MenuItem>
{kategorieOptions.map(k => <MenuItem key={k.id} value={k.id}>{k.name}</MenuItem>)}
{topKategorien.map(k => <MenuItem key={k.id} value={k.id}>{k.name}</MenuItem>)}
</TextField>
{artikelMainKat && artikelSubKats.length > 0 && (
<TextField
select
label="Unterkategorie"
value={artikelForm.kategorie_id ?? ''}
onChange={e => setArtikelForm(f => ({ ...f, kategorie_id: e.target.value ? Number(e.target.value) : (artikelMainKat as number) }))}
fullWidth
>
<MenuItem value={artikelMainKat as number}>Keine (nur Hauptkategorie)</MenuItem>
{artikelSubKats.map(k => <MenuItem key={k.id} value={k.id}>{k.name}</MenuItem>)}
</TextField>
)}
{canManage && <EigenschaftenEditor artikelId={editArtikel?.id ?? null} />}
</DialogContent>
<DialogActions>