shared catalog in Bestellungen, catalog picker in line items, Ersatzbeschaffung flag, vendor detail flash fix

This commit is contained in:
Matthias Hochmeister
2026-03-27 14:50:31 +01:00
parent c704e2c173
commit 29d66e37a1
16 changed files with 506 additions and 32 deletions

View File

@@ -4,7 +4,7 @@ import {
Table, TableBody, TableCell, TableHead, TableRow,
Dialog, DialogTitle, DialogContent, DialogActions, TextField,
MenuItem, Select, FormControl, InputLabel, Autocomplete,
Checkbox, LinearProgress,
Checkbox, LinearProgress, Switch, FormControlLabel, Alert,
} from '@mui/material';
import {
ArrowBack, Add as AddIcon, Delete as DeleteIcon, Edit as EditIcon,
@@ -123,6 +123,15 @@ export default function AusruestungsanfrageDetail() {
onError: () => showError('Fehler beim Aktualisieren'),
});
const zurueckgegebenMut = useMutation({
mutationFn: ({ positionId, zurueckgegeben }: { positionId: number; zurueckgegeben: boolean }) =>
ausruestungsanfrageApi.updatePositionZurueckgegeben(positionId, zurueckgegeben),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] });
},
onError: () => showError('Fehler beim Aktualisieren'),
});
// ── Edit helpers ──
const startEditing = () => {
if (!detail) return;
@@ -134,6 +143,7 @@ export default function AusruestungsanfrageDetail() {
menge: p.menge,
notizen: p.notizen,
eigenschaften: p.eigenschaften?.map(e => ({ eigenschaft_id: e.eigenschaft_id, wert: e.wert })),
ist_ersatz: p.ist_ersatz || false,
})));
const initVals: Record<number, Record<number, string>> = {};
detail.positionen.forEach((p, idx) => {
@@ -290,6 +300,23 @@ export default function AusruestungsanfrageDetail() {
))}
</Box>
)}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5, ml: 1 }}>
<FormControlLabel
control={
<Switch
size="small"
checked={item.ist_ersatz || false}
onChange={(e) => updateEditItem(idx, 'ist_ersatz', e.target.checked)}
/>
}
label="Ersatzbeschaffung"
/>
{item.ist_ersatz && (
<Alert severity="info" sx={{ py: 0, fontSize: '0.8rem' }}>
Altes Gerät muss zurückgegeben werden
</Alert>
)}
</Box>
</Box>
))}
<Button size="small" startIcon={<AddIcon />} onClick={addEditItem}>
@@ -376,19 +403,33 @@ export default function AusruestungsanfrageDetail() {
)}
<TableCell>
<Typography variant="body2" fontWeight={500} sx={p.geliefert ? { textDecoration: 'line-through' } : undefined}>{p.bezeichnung}</Typography>
{p.eigenschaften && p.eigenschaften.length > 0 && (
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mt: 0.5 }}>
{p.eigenschaften.map(e => (
<Chip key={e.eigenschaft_id} label={`${e.eigenschaft_name}: ${e.wert}`} size="small" variant="outlined" />
))}
</Box>
)}
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mt: 0.5 }}>
{p.ist_ersatz && (
<Chip label="Ersatzbeschaffung" size="small" color="warning" variant="outlined" />
)}
{p.eigenschaften && p.eigenschaften.length > 0 && p.eigenschaften.map(e => (
<Chip key={e.eigenschaft_id} label={`${e.eigenschaft_name}: ${e.wert}`} size="small" variant="outlined" />
))}
</Box>
</TableCell>
<TableCell align="right">
<Typography variant="body2" fontWeight={600}>{p.menge}x</Typography>
</TableCell>
<TableCell>
{p.notizen && <Typography variant="caption" color="text.secondary">{p.notizen}</Typography>}
{p.notizen && <Typography variant="caption" color="text.secondary" display="block">{p.notizen}</Typography>}
{p.ist_ersatz && (
<FormControlLabel
control={
<Checkbox
size="small"
checked={p.altes_geraet_zurueckgegeben}
disabled={!showAdminActions || zurueckgegebenMut.isPending}
onChange={(_, checked) => zurueckgegebenMut.mutate({ positionId: p.id, zurueckgegeben: checked })}
/>
}
label={<Typography variant="caption">Altes Gerät zurückgegeben</Typography>}
/>
)}
</TableCell>
</TableRow>
))}