feat(ausruestung): catalog-driven item tracking, im_haus in overview, order quantity override, fix stale queries
This commit is contained in:
@@ -16,6 +16,7 @@ import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { personalEquipmentApi } from '../services/personalEquipment';
|
||||
import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage';
|
||||
import { membersService } from '../services/members';
|
||||
import { usePermissionContext } from '../contexts/PermissionContext';
|
||||
import { useNotification } from '../contexts/NotificationContext';
|
||||
@@ -64,10 +65,17 @@ export default function PersoenlicheAusruestungEdit() {
|
||||
}));
|
||||
}, [membersList]);
|
||||
|
||||
const { data: artikelEigenschaften = [] } = useQuery({
|
||||
queryKey: ['ausruestungsanfrage', 'eigenschaften', item?.artikel_id],
|
||||
queryFn: () => ausruestungsanfrageApi.getArtikelEigenschaften(item!.artikel_id!),
|
||||
enabled: !!item?.artikel_id,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const [catalogEigenschaftValues, setCatalogEigenschaftValues] = useState<Record<number, string>>({});
|
||||
|
||||
// Form state
|
||||
const [bezeichnung, setBezeichnung] = useState('');
|
||||
const [kategorie, setKategorie] = useState('');
|
||||
const [groesse, setGroesse] = useState('');
|
||||
const [seriennummer, setSeriennummer] = useState('');
|
||||
const [inventarnummer, setInventarnummer] = useState('');
|
||||
const [anschaffungDatum, setAnschaffungDatum] = useState('');
|
||||
@@ -80,8 +88,6 @@ export default function PersoenlicheAusruestungEdit() {
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setBezeichnung(item.bezeichnung);
|
||||
setKategorie(item.kategorie ?? '');
|
||||
setGroesse(item.groesse ?? '');
|
||||
setSeriennummer(item.seriennummer ?? '');
|
||||
setInventarnummer(item.inventarnummer ?? '');
|
||||
setAnschaffungDatum(item.anschaffung_datum ? item.anschaffung_datum.split('T')[0] : '');
|
||||
@@ -95,6 +101,13 @@ export default function PersoenlicheAusruestungEdit() {
|
||||
wert: e.wert,
|
||||
})));
|
||||
}
|
||||
if (item.artikel_id && item.eigenschaften) {
|
||||
const vals: Record<number, string> = {};
|
||||
item.eigenschaften.forEach(e => {
|
||||
if (e.eigenschaft_id != null) vals[e.eigenschaft_id] = e.wert;
|
||||
});
|
||||
setCatalogEigenschaftValues(vals);
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
// Set userId when item + memberOptions are ready
|
||||
@@ -117,21 +130,27 @@ export default function PersoenlicheAusruestungEdit() {
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
if (!bezeichnung.trim()) return;
|
||||
if (!bezeichnung.trim() || !item) return;
|
||||
|
||||
const payload: UpdatePersoenlicheAusruestungPayload = {
|
||||
bezeichnung: bezeichnung.trim(),
|
||||
kategorie: kategorie || null,
|
||||
kategorie: null,
|
||||
user_id: userId?.id || null,
|
||||
groesse: groesse || null,
|
||||
groesse: null,
|
||||
seriennummer: seriennummer || null,
|
||||
inventarnummer: inventarnummer || null,
|
||||
anschaffung_datum: anschaffungDatum || null,
|
||||
zustand,
|
||||
notizen: notizen || null,
|
||||
eigenschaften: eigenschaften
|
||||
.filter(e => e.name.trim() && e.wert.trim())
|
||||
.map(e => ({ eigenschaft_id: e.eigenschaft_id, name: e.name, wert: e.wert })),
|
||||
eigenschaften: item.artikel_id
|
||||
? Object.entries(catalogEigenschaftValues)
|
||||
.filter(([, v]) => v.trim())
|
||||
.map(([id, wert]) => ({
|
||||
eigenschaft_id: Number(id),
|
||||
name: artikelEigenschaften.find(e => e.id === Number(id))?.name ?? '',
|
||||
wert,
|
||||
}))
|
||||
: eigenschaften.filter(e => e.name.trim() && e.wert.trim()).map(e => ({ eigenschaft_id: e.eigenschaft_id, name: e.name, wert: e.wert })),
|
||||
};
|
||||
updateMutation.mutate(payload);
|
||||
};
|
||||
@@ -198,20 +217,6 @@ export default function PersoenlicheAusruestungEdit() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
label="Kategorie"
|
||||
size="small"
|
||||
value={kategorie}
|
||||
onChange={(e) => setKategorie(e.target.value)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Größe"
|
||||
size="small"
|
||||
value={groesse}
|
||||
onChange={(e) => setGroesse(e.target.value)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Seriennummer"
|
||||
size="small"
|
||||
@@ -257,31 +262,55 @@ export default function PersoenlicheAusruestungEdit() {
|
||||
/>
|
||||
|
||||
{/* Eigenschaften */}
|
||||
<Typography variant="subtitle2">Eigenschaften</Typography>
|
||||
{eigenschaften.map((e, idx) => (
|
||||
<Box key={idx} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Name"
|
||||
value={e.name}
|
||||
onChange={(ev) => updateEigenschaft(idx, 'name', ev.target.value)}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Wert"
|
||||
value={e.wert}
|
||||
onChange={(ev) => updateEigenschaft(idx, 'wert', ev.target.value)}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<IconButton size="small" onClick={() => removeEigenschaft(idx)}>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
))}
|
||||
<Button size="small" startIcon={<AddIcon />} onClick={addEigenschaft}>
|
||||
Eigenschaft hinzufügen
|
||||
</Button>
|
||||
{item.artikel_id ? (
|
||||
<>
|
||||
<Typography variant="subtitle2">Eigenschaften</Typography>
|
||||
{artikelEigenschaften.map(e =>
|
||||
e.typ === 'options' && e.optionen?.length ? (
|
||||
<TextField key={e.id} select size="small" label={e.name} required={e.pflicht}
|
||||
value={catalogEigenschaftValues[e.id] ?? ''}
|
||||
onChange={ev => setCatalogEigenschaftValues(prev => ({ ...prev, [e.id]: ev.target.value }))}
|
||||
>
|
||||
<MenuItem value="">—</MenuItem>
|
||||
{e.optionen.map(opt => <MenuItem key={opt} value={opt}>{opt}</MenuItem>)}
|
||||
</TextField>
|
||||
) : (
|
||||
<TextField key={e.id} size="small" label={e.name} required={e.pflicht}
|
||||
value={catalogEigenschaftValues[e.id] ?? ''}
|
||||
onChange={ev => setCatalogEigenschaftValues(prev => ({ ...prev, [e.id]: ev.target.value }))}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="subtitle2">Eigenschaften</Typography>
|
||||
{eigenschaften.map((e, idx) => (
|
||||
<Box key={idx} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Name"
|
||||
value={e.name}
|
||||
onChange={(ev) => updateEigenschaft(idx, 'name', ev.target.value)}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Wert"
|
||||
value={e.wert}
|
||||
onChange={(ev) => updateEigenschaft(idx, 'wert', ev.target.value)}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<IconButton size="small" onClick={() => removeEigenschaft(idx)}>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
))}
|
||||
<Button size="small" startIcon={<AddIcon />} onClick={addEigenschaft}>
|
||||
Eigenschaft hinzufügen
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'flex-end', mt: 1 }}>
|
||||
<Button onClick={() => navigate(`/persoenliche-ausruestung/${id}`)}>
|
||||
|
||||
Reference in New Issue
Block a user