catalog search/sort, edit-page characteristics, preferred vendor per article
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useCallback } from 'react';
|
||||
import {
|
||||
Box, Typography, Paper, Button, Chip, IconButton,
|
||||
Table, TableBody, TableCell, TableHead, TableRow,
|
||||
@@ -21,6 +21,7 @@ import { AUSRUESTUNG_STATUS_LABELS, AUSRUESTUNG_STATUS_COLORS } from '../types/a
|
||||
import type {
|
||||
AusruestungAnfrage, AusruestungAnfrageDetailResponse,
|
||||
AusruestungAnfrageFormItem, AusruestungAnfrageStatus,
|
||||
AusruestungEigenschaft,
|
||||
} from '../types/ausruestungsanfrage.types';
|
||||
|
||||
// ── Helpers ──
|
||||
@@ -57,6 +58,10 @@ export default function AusruestungsanfrageDetail() {
|
||||
const [adminNotizen, setAdminNotizen] = useState('');
|
||||
const [statusChangeValue, setStatusChangeValue] = useState('');
|
||||
|
||||
// Eigenschaften state for edit mode
|
||||
const [editItemEigenschaften, setEditItemEigenschaften] = useState<Record<number, AusruestungEigenschaft[]>>({});
|
||||
const [editItemEigenschaftValues, setEditItemEigenschaftValues] = useState<Record<number, Record<number, string>>>({});
|
||||
|
||||
// Permissions
|
||||
const showAdminActions = hasPermission('ausruestungsanfrage:approve');
|
||||
const canEditAny = hasPermission('ausruestungsanfrage:edit');
|
||||
@@ -73,9 +78,17 @@ export default function AusruestungsanfrageDetail() {
|
||||
const { data: catalogItems = [] } = useQuery({
|
||||
queryKey: ['ausruestungsanfrage', 'items-for-edit'],
|
||||
queryFn: () => ausruestungsanfrageApi.getItems({ aktiv: true }),
|
||||
enabled: editing,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const loadEigenschaftenForItem = useCallback(async (artikelId: number) => {
|
||||
if (editItemEigenschaften[artikelId]) return;
|
||||
try {
|
||||
const eigs = await ausruestungsanfrageApi.getArtikelEigenschaften(artikelId);
|
||||
if (eigs?.length > 0) setEditItemEigenschaften(prev => ({ ...prev, [artikelId]: eigs }));
|
||||
} catch { /* ignore */ }
|
||||
}, [editItemEigenschaften]);
|
||||
|
||||
// ── Mutations ──
|
||||
const updateMut = useMutation({
|
||||
mutationFn: (data: { bezeichnung?: string; notizen?: string; items?: AusruestungAnfrageFormItem[] }) =>
|
||||
@@ -122,15 +135,31 @@ export default function AusruestungsanfrageDetail() {
|
||||
notizen: p.notizen,
|
||||
eigenschaften: p.eigenschaften?.map(e => ({ eigenschaft_id: e.eigenschaft_id, wert: e.wert })),
|
||||
})));
|
||||
const initVals: Record<number, Record<number, string>> = {};
|
||||
detail.positionen.forEach((p, idx) => {
|
||||
if (p.artikel_id) loadEigenschaftenForItem(p.artikel_id);
|
||||
if (p.eigenschaften?.length) {
|
||||
initVals[idx] = {};
|
||||
p.eigenschaften.forEach(e => { initVals[idx][e.eigenschaft_id] = e.wert; });
|
||||
}
|
||||
});
|
||||
setEditItemEigenschaftValues(initVals);
|
||||
setEditing(true);
|
||||
};
|
||||
|
||||
const handleSaveEdit = () => {
|
||||
if (editItems.length === 0) return;
|
||||
const items = editItems.map((item, idx) => {
|
||||
const vals = editItemEigenschaftValues[idx] || {};
|
||||
const eigenschaften = Object.entries(vals)
|
||||
.filter(([, wert]) => wert.trim())
|
||||
.map(([eid, wert]) => ({ eigenschaft_id: Number(eid), wert }));
|
||||
return { ...item, eigenschaften: eigenschaften.length > 0 ? eigenschaften : undefined };
|
||||
});
|
||||
updateMut.mutate({
|
||||
bezeichnung: editBezeichnung || undefined,
|
||||
notizen: editNotizen || undefined,
|
||||
items: editItems,
|
||||
items,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -197,7 +226,8 @@ export default function AusruestungsanfrageDetail() {
|
||||
/>
|
||||
<Typography variant="subtitle2" sx={{ mt: 1 }}>Positionen</Typography>
|
||||
{editItems.map((item, idx) => (
|
||||
<Box key={idx} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<Box key={idx} sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<Autocomplete
|
||||
freeSolo
|
||||
options={catalogItems}
|
||||
@@ -209,6 +239,8 @@ export default function AusruestungsanfrageDetail() {
|
||||
updateEditItem(idx, 'artikel_id', undefined);
|
||||
} else if (v) {
|
||||
setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, artikel_id: v.id, bezeichnung: v.bezeichnung } : it));
|
||||
loadEigenschaftenForItem(v.id);
|
||||
setEditItemEigenschaftValues(prev => { const n = { ...prev }; delete n[idx]; return n; });
|
||||
}
|
||||
}}
|
||||
onInputChange={(_, val, reason) => {
|
||||
@@ -231,6 +263,33 @@ export default function AusruestungsanfrageDetail() {
|
||||
<IconButton size="small" onClick={() => removeEditItem(idx)}>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
{item.artikel_id && editItemEigenschaften[item.artikel_id]?.length > 0 && (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, ml: 1, pl: 1.5, borderLeft: '2px solid', borderColor: 'divider' }}>
|
||||
{editItemEigenschaften[item.artikel_id].map(e => (
|
||||
e.typ === 'options' && e.optionen?.length ? (
|
||||
<TextField key={e.id} select size="small" label={e.name} required={e.pflicht}
|
||||
value={editItemEigenschaftValues[idx]?.[e.id] || ''}
|
||||
onChange={ev => setEditItemEigenschaftValues(prev => ({
|
||||
...prev, [idx]: { ...(prev[idx] || {}), [e.id]: ev.target.value }
|
||||
}))}
|
||||
sx={{ minWidth: 140 }}
|
||||
>
|
||||
<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={editItemEigenschaftValues[idx]?.[e.id] || ''}
|
||||
onChange={ev => setEditItemEigenschaftValues(prev => ({
|
||||
...prev, [idx]: { ...(prev[idx] || {}), [e.id]: ev.target.value }
|
||||
}))}
|
||||
sx={{ minWidth: 160 }}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
<Button size="small" startIcon={<AddIcon />} onClick={addEditItem}>
|
||||
|
||||
Reference in New Issue
Block a user