refactor: move equipment type assignment from detail page to settings page
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Alert,
|
||||
Autocomplete,
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
CircularProgress,
|
||||
Container,
|
||||
Dialog,
|
||||
@@ -10,6 +12,7 @@ import {
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
IconButton,
|
||||
Paper,
|
||||
Table,
|
||||
@@ -27,6 +30,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { ausruestungTypenApi, AusruestungTyp } from '../services/ausruestungTypen';
|
||||
import { equipmentApi } from '../services/equipment';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { useNotification } from '../contexts/NotificationContext';
|
||||
|
||||
@@ -302,9 +306,129 @@ function AusruestungEinstellungen() {
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<Divider sx={{ my: 4 }} />
|
||||
<EquipmentTypeAssignment allTypes={typen} />
|
||||
</Container>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default AusruestungEinstellungen;
|
||||
|
||||
// ── Per-equipment type assignment ──────────────────────────────────────────────
|
||||
|
||||
function EquipmentTypeAssignment({ allTypes }: { allTypes: AusruestungTyp[] }) {
|
||||
const { showSuccess, showError } = useNotification();
|
||||
const { data: equipment = [], isLoading } = useQuery({
|
||||
queryKey: ['equipment'],
|
||||
queryFn: equipmentApi.getAll,
|
||||
});
|
||||
|
||||
const [assignDialog, setAssignDialog] = useState<{ equipmentId: string; equipmentName: string } | null>(null);
|
||||
const [selected, setSelected] = useState<AusruestungTyp[]>([]);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [equipmentTypesMap, setEquipmentTypesMap] = useState<Record<string, AusruestungTyp[]>>({});
|
||||
|
||||
const openAssign = async (equipmentId: string, equipmentName: string) => {
|
||||
let current = equipmentTypesMap[equipmentId];
|
||||
if (!current) {
|
||||
try { current = await ausruestungTypenApi.getTypesForEquipment(equipmentId); }
|
||||
catch { current = []; }
|
||||
setEquipmentTypesMap((m) => ({ ...m, [equipmentId]: current }));
|
||||
}
|
||||
setSelected(current);
|
||||
setAssignDialog({ equipmentId, equipmentName });
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!assignDialog) return;
|
||||
try {
|
||||
setSaving(true);
|
||||
await ausruestungTypenApi.setTypesForEquipment(assignDialog.equipmentId, selected.map((t) => t.id));
|
||||
setEquipmentTypesMap((m) => ({ ...m, [assignDialog.equipmentId]: selected }));
|
||||
setAssignDialog(null);
|
||||
showSuccess('Typen gespeichert');
|
||||
} catch {
|
||||
showError('Fehler beim Speichern');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h6" gutterBottom>Typzuweisung je Gerät</Typography>
|
||||
{isLoading ? (
|
||||
<CircularProgress size={24} />
|
||||
) : (
|
||||
<Paper variant="outlined">
|
||||
<TableContainer>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Gerät</TableCell>
|
||||
<TableCell>Zugewiesene Typen</TableCell>
|
||||
<TableCell align="right">Aktionen</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{equipment.map((e) => {
|
||||
const types = equipmentTypesMap[e.id];
|
||||
return (
|
||||
<TableRow key={e.id} hover>
|
||||
<TableCell>{e.bezeichnung}</TableCell>
|
||||
<TableCell>
|
||||
{types === undefined ? (
|
||||
<Typography variant="body2" color="text.disabled">–</Typography>
|
||||
) : types.length === 0 ? (
|
||||
<Typography variant="body2" color="text.disabled">Keine</Typography>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
|
||||
{types.map((t) => <Chip key={t.id} label={t.name} size="small" variant="outlined" />)}
|
||||
</Box>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Tooltip title="Typen zuweisen">
|
||||
<IconButton size="small" onClick={() => openAssign(e.id, e.bezeichnung)}>
|
||||
<Edit fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
<Dialog open={!!assignDialog} onClose={() => setAssignDialog(null)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
Typen für {assignDialog?.equipmentName}
|
||||
<IconButton size="small" onClick={() => setAssignDialog(null)}><Close /></IconButton>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ mt: 1 }}>
|
||||
<Autocomplete
|
||||
multiple
|
||||
options={allTypes}
|
||||
getOptionLabel={(o) => o.name}
|
||||
value={selected}
|
||||
onChange={(_e, val) => setSelected(val)}
|
||||
isOptionEqualToValue={(a, b) => a.id === b.id}
|
||||
renderInput={(params) => <TextField {...params} label="Ausrüstungstypen" />}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setAssignDialog(null)}>Abbrechen</Button>
|
||||
<Button variant="contained" onClick={handleSave} disabled={saving}
|
||||
startIcon={saving ? <CircularProgress size={16} /> : <Save />}>
|
||||
Speichern
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user