import { useState, useMemo, useCallback } from 'react'; import { Box, Typography, Paper, Button, TextField, IconButton, Chip, MenuItem, Divider, Checkbox, FormControlLabel, LinearProgress, Autocomplete, } from '@mui/material'; import { ArrowBack, Delete as DeleteIcon, Add as AddIcon, Edit as EditIcon, } from '@mui/icons-material'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useParams, useNavigate } from 'react-router-dom'; import DashboardLayout from '../components/dashboard/DashboardLayout'; import { useNotification } from '../contexts/NotificationContext'; import { usePermissionContext } from '../contexts/PermissionContext'; import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage'; import { bestellungApi } from '../services/bestellung'; import type { Lieferant } from '../types/bestellung.types'; import type { AusruestungArtikel, AusruestungArtikelFormData, AusruestungEigenschaft, AusruestungKategorie, } from '../types/ausruestungsanfrage.types'; // ── EigenschaftenEditor ── function EigenschaftenEditor({ artikelId }: { artikelId: number | null }) { const { showSuccess, showError } = useNotification(); const queryClient = useQueryClient(); const [newName, setNewName] = useState(''); const [newTyp, setNewTyp] = useState<'options' | 'freitext'>('options'); const [newOptionen, setNewOptionen] = useState(''); const [newPflicht, setNewPflicht] = useState(false); // Per-eigenschaft edit state const [rowState, setRowState] = useState>({}); const { data: eigenschaften = [] } = useQuery({ queryKey: ['ausruestungsanfrage', 'eigenschaften', artikelId], queryFn: () => ausruestungsanfrageApi.getArtikelEigenschaften(artikelId!), enabled: artikelId != null, }); const getRow = (e: AusruestungEigenschaft) => rowState[e.id] ?? { name: e.name, typ: e.typ, optionen: e.optionen?.join(', ') || '', pflicht: e.pflicht }; const updateRow = (e: AusruestungEigenschaft, patch: Partial<{ name: string; typ: 'options' | 'freitext'; optionen: string; pflicht: boolean }>) => { const current = rowState[e.id] ?? { name: e.name, typ: e.typ, optionen: e.optionen?.join(', ') || '', pflicht: e.pflicht }; setRowState(prev => ({ ...prev, [e.id]: { ...current, ...patch } })); }; const upsertMut = useMutation({ mutationFn: (data: { eigenschaft_id?: number; name: string; typ: string; optionen?: string[]; pflicht?: boolean; sort_order?: number }) => ausruestungsanfrageApi.upsertArtikelEigenschaft(artikelId!, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage', 'eigenschaften', artikelId] }); showSuccess('Eigenschaft gespeichert'); setNewName(''); setNewOptionen(''); setNewPflicht(false); }, onError: () => showError('Fehler beim Speichern'), }); const deleteMut = useMutation({ mutationFn: (id: number) => ausruestungsanfrageApi.deleteArtikelEigenschaft(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage', 'eigenschaften', artikelId] }); showSuccess('Eigenschaft gelöscht'); }, onError: () => showError('Fehler beim Löschen'), }); const handleAdd = () => { if (!newName.trim()) return; const optionen = newTyp === 'options' ? newOptionen.split(',').map(s => s.trim()).filter(Boolean) : undefined; upsertMut.mutate({ name: newName.trim(), typ: newTyp, optionen, pflicht: newPflicht, sort_order: eigenschaften.length }); }; const handleSaveRow = (e: AusruestungEigenschaft) => { const row = getRow(e); if (!row.name.trim()) return; upsertMut.mutate({ eigenschaft_id: e.id, name: row.name.trim(), typ: row.typ, optionen: row.typ === 'options' ? row.optionen.split(',').map(s => s.trim()).filter(Boolean) : undefined, pflicht: row.pflicht, sort_order: e.sort_order, }); }; if (artikelId == null) return Bitte speichern Sie den Artikel zuerst, bevor Sie Eigenschaften hinzufügen.; return ( Eigenschaften {eigenschaften.map(e => { const row = getRow(e); return ( updateRow(e, { name: ev.target.value })} sx={{ flexGrow: 1 }} /> updateRow(e, { typ: ev.target.value as 'options' | 'freitext' })} sx={{ minWidth: 120 }} > Auswahl Freitext updateRow(e, { pflicht: ev.target.checked })} />} label="Pflicht" /> deleteMut.mutate(e.id)}> {row.typ === 'options' && ( updateRow(e, { optionen: ev.target.value })} fullWidth /> )} ); })} setNewName(e.target.value)} sx={{ flexGrow: 1 }} /> setNewTyp(e.target.value as 'options' | 'freitext')} sx={{ minWidth: 120 }} > Auswahl Freitext setNewPflicht(e.target.checked)} />} label="Pflicht" /> {newTyp === 'options' && ( setNewOptionen(e.target.value)} placeholder="S, M, L, XL" fullWidth /> )} ); } // ══════════════════════════════════════════════════════════════════════════════ // Main Component // ══════════════════════════════════════════════════════════════════════════════ export default function AusruestungsanfrageArtikelDetail() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { showSuccess, showError } = useNotification(); const { hasPermission } = usePermissionContext(); const isCreate = !id || id === 'neu'; const artikelId = isCreate ? null : Number(id); const canManage = hasPermission('ausruestungsanfrage:manage_catalog'); // ── State ── const [editing, setEditing] = useState(isCreate); const [form, setForm] = useState({ bezeichnung: '' }); const [mainKat, setMainKat] = useState(''); // ── Queries ── const { data: artikel, isLoading, isError } = useQuery({ queryKey: ['ausruestungsanfrage', 'item', artikelId], queryFn: () => ausruestungsanfrageApi.getItem(artikelId!), enabled: artikelId != null, retry: 1, }); const { data: kategorien = [] } = useQuery({ queryKey: ['ausruestungsanfrage', 'kategorien'], queryFn: () => ausruestungsanfrageApi.getKategorien(), }); const { data: eigenschaften = [] } = useQuery({ queryKey: ['ausruestungsanfrage', 'eigenschaften', artikelId], queryFn: () => ausruestungsanfrageApi.getArtikelEigenschaften(artikelId!), enabled: artikelId != null, }); const { data: lieferanten = [] } = useQuery({ queryKey: ['bestellungen', 'lieferanten'], queryFn: () => bestellungApi.getVendors(), enabled: editing || isCreate, }); const topKategorien = useMemo(() => kategorien.filter(k => !k.parent_id), [kategorien]); const subKategorienOf = useCallback((parentId: number) => kategorien.filter(k => k.parent_id === parentId), [kategorien]); const subKats = useMemo(() => mainKat ? subKategorienOf(mainKat as number) : [], [mainKat, subKategorienOf]); const kategorieOptions = useMemo(() => { const map = new Map(kategorien.map(k => [k.id, k])); const getDisplayName = (k: AusruestungKategorie): string => { if (k.parent_id) { const parent = map.get(k.parent_id); if (parent) return `${parent.name} > ${k.name}`; } return k.name; }; return kategorien.map(k => ({ id: k.id, name: getDisplayName(k) })); }, [kategorien]); // ── Mutations ── const createMut = useMutation({ mutationFn: (data: AusruestungArtikelFormData) => ausruestungsanfrageApi.createItem(data), onSuccess: (newItem) => { queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] }); showSuccess('Artikel erstellt'); navigate(`/ausruestungsanfrage/artikel/${newItem.id}`, { replace: true }); }, onError: () => showError('Fehler beim Erstellen'), }); const updateMut = useMutation({ mutationFn: ({ itemId, data }: { itemId: number; data: Partial }) => ausruestungsanfrageApi.updateItem(itemId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] }); showSuccess('Artikel aktualisiert'); setEditing(false); }, onError: () => showError('Fehler beim Aktualisieren'), }); const deleteMut = useMutation({ mutationFn: (itemId: number) => ausruestungsanfrageApi.deleteItem(itemId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] }); showSuccess('Artikel geloescht'); navigate('/ausruestungsanfrage?tab=2'); }, onError: () => showError('Fehler beim Loeschen'), }); // ── Edit helpers ── const startEditing = () => { if (!artikel) return; setForm({ bezeichnung: artikel.bezeichnung, beschreibung: artikel.beschreibung, kategorie_id: artikel.kategorie_id ?? null, bevorzugter_lieferant_id: artikel.bevorzugter_lieferant_id ?? null, }); const kat = kategorien.find(k => k.id === artikel.kategorie_id); if (kat?.parent_id) { setMainKat(kat.parent_id); } else { setMainKat(artikel.kategorie_id || ''); } setEditing(true); }; const handleSave = () => { if (!form.bezeichnung.trim()) return; if (isCreate) { createMut.mutate(form); } else if (artikelId) { updateMut.mutate({ itemId: artikelId, data: form }); } }; const handleCancel = () => { if (isCreate) { navigate('/ausruestungsanfrage?tab=2'); } else { setEditing(false); } }; const getKategorieName = (katId?: number) => { if (!katId) return '-'; return kategorieOptions.find(k => k.id === katId)?.name || '-'; }; return ( {/* Header */} navigate('/ausruestungsanfrage?tab=2')}> {isCreate ? 'Neuer Katalogartikel' : (artikel?.bezeichnung ?? '...')} {!isCreate && canManage && !editing && ( <> { if (artikelId) deleteMut.mutate(artikelId); }}> )} {!isCreate && isLoading ? ( ) : !isCreate && isError ? ( Fehler beim Laden des Artikels. ) : editing ? ( /* ── Edit / Create Mode ── */ setForm(f => ({ ...f, bezeichnung: e.target.value }))} fullWidth autoFocus /> setForm(f => ({ ...f, beschreibung: e.target.value }))} fullWidth /> { const val = e.target.value ? Number(e.target.value) : ''; setMainKat(val); if (val) { subKategorienOf(val as number); setForm(f => ({ ...f, kategorie_id: val as number })); } else { setForm(f => ({ ...f, kategorie_id: null })); } }} fullWidth > Keine {topKategorien.map(k => {k.name})} {mainKat && subKats.length > 0 && ( setForm(f => ({ ...f, kategorie_id: e.target.value ? Number(e.target.value) : (mainKat as number) }))} fullWidth > Keine (nur Hauptkategorie) {subKats.map(k => {k.name})} )} l.name} value={lieferanten.find(l => l.id === form.bevorzugter_lieferant_id) || null} onChange={(_, v) => setForm(f => ({ ...f, bevorzugter_lieferant_id: v?.id ?? null }))} renderInput={params => } fullWidth /> {canManage && } ) : artikel ? ( /* ── View Mode ── */ <> Bezeichnung {artikel.bezeichnung} Kategorie {getKategorieName(artikel.kategorie_id)} {artikel.beschreibung && ( Beschreibung {artikel.beschreibung} )} Status Erstellt am {new Date(artikel.erstellt_am).toLocaleDateString('de-AT')} {artikel.bevorzugter_lieferant_name && ( Bevorzugter Lieferant {artikel.bevorzugter_lieferant_name} )} {eigenschaften.length > 0 && ( Eigenschaften ({eigenschaften.length}) {eigenschaften.map(e => ( {e.name} ({e.typ === 'options' ? `Auswahl: ${e.optionen?.join(', ')}` : 'Freitext'}) {e.pflicht && } ))} )} ) : null} ); }