fix: add checklisten sidebar sub-items and replace unicode escapes with proper umlauts
This commit is contained in:
@@ -30,7 +30,7 @@ import type { CreateFahrzeugItemPayload } from '../../types/checklist.types';
|
|||||||
// ── Helpers ──
|
// ── Helpers ──
|
||||||
|
|
||||||
const formatDate = (iso?: string) =>
|
const formatDate = (iso?: string) =>
|
||||||
iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) : '\u2013';
|
iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) : '–';
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════════════════════
|
||||||
// Component
|
// Component
|
||||||
@@ -76,8 +76,8 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
|
|
||||||
const addItemMutation = useMutation({
|
const addItemMutation = useMutation({
|
||||||
mutationFn: (data: CreateFahrzeugItemPayload) => checklistenApi.addVehicleItem(fahrzeugId, data),
|
mutationFn: (data: CreateFahrzeugItemPayload) => checklistenApi.addVehicleItem(fahrzeugId, data),
|
||||||
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-fahrzeug-items', fahrzeugId] }); setNewItem({ bezeichnung: '', pflicht: false, sort_order: 0 }); showSuccess('Item hinzugef\u00fcgt'); },
|
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-fahrzeug-items', fahrzeugId] }); setNewItem({ bezeichnung: '', pflicht: false, sort_order: 0 }); showSuccess('Item hinzugefügt'); },
|
||||||
onError: () => showError('Fehler beim Hinzuf\u00fcgen'),
|
onError: () => showError('Fehler beim Hinzufügen'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteItemMutation = useMutation({
|
const deleteItemMutation = useMutation({
|
||||||
@@ -120,7 +120,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
vehicleItems.map((item) => (
|
vehicleItems.map((item) => (
|
||||||
<TableRow key={item.id}>
|
<TableRow key={item.id}>
|
||||||
<TableCell>{item.bezeichnung}</TableCell>
|
<TableCell>{item.bezeichnung}</TableCell>
|
||||||
<TableCell align="center">{item.pflicht ? <Chip label="Pflicht" size="small" color="warning" /> : '\u2013'}</TableCell>
|
<TableCell align="center">{item.pflicht ? <Chip label="Pflicht" size="small" color="warning" /> : '–'}</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<Switch
|
<Switch
|
||||||
size="small"
|
size="small"
|
||||||
@@ -144,7 +144,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
<TextField size="small" placeholder="Neues Item..." value={newItem.bezeichnung} onChange={(e) => setNewItem((n) => ({ ...n, bezeichnung: e.target.value }))} sx={{ flexGrow: 1 }} />
|
<TextField size="small" placeholder="Neues Item..." value={newItem.bezeichnung} onChange={(e) => setNewItem((n) => ({ ...n, bezeichnung: e.target.value }))} sx={{ flexGrow: 1 }} />
|
||||||
<FormControlLabel control={<Switch size="small" checked={newItem.pflicht} onChange={(e) => setNewItem((n) => ({ ...n, pflicht: e.target.checked }))} />} label="Pflicht" />
|
<FormControlLabel control={<Switch size="small" checked={newItem.pflicht} onChange={(e) => setNewItem((n) => ({ ...n, pflicht: e.target.checked }))} />} label="Pflicht" />
|
||||||
<Button size="small" variant="outlined" startIcon={<Add />} disabled={!newItem.bezeichnung.trim() || addItemMutation.isPending} onClick={() => addItemMutation.mutate(newItem)}>
|
<Button size="small" variant="outlined" startIcon={<Add />} disabled={!newItem.bezeichnung.trim() || addItemMutation.isPending} onClick={() => addItemMutation.mutate(newItem)}>
|
||||||
Hinzuf\u00fcgen
|
Hinzufügen
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
@@ -159,7 +159,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
{templatesLoading ? (
|
{templatesLoading ? (
|
||||||
<CircularProgress size={24} />
|
<CircularProgress size={24} />
|
||||||
) : templates.length === 0 ? (
|
) : templates.length === 0 ? (
|
||||||
<Typography color="text.secondary">Keine Vorlagen f\u00fcr dieses Fahrzeug.</Typography>
|
<Typography color="text.secondary">Keine Vorlagen für dieses Fahrzeug.</Typography>
|
||||||
) : (
|
) : (
|
||||||
<TableContainer component={Paper} variant="outlined">
|
<TableContainer component={Paper} variant="outlined">
|
||||||
<Table size="small">
|
<Table size="small">
|
||||||
@@ -167,7 +167,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>Vorlage</TableCell>
|
<TableCell>Vorlage</TableCell>
|
||||||
<TableCell>Intervall</TableCell>
|
<TableCell>Intervall</TableCell>
|
||||||
<TableCell>N\u00e4chste F\u00e4lligkeit</TableCell>
|
<TableCell>Nächste Fälligkeit</TableCell>
|
||||||
<TableCell align="right">Aktion</TableCell>
|
<TableCell align="right">Aktion</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
@@ -178,11 +178,11 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
<TableRow key={t.id}>
|
<TableRow key={t.id}>
|
||||||
<TableCell>{t.name}</TableCell>
|
<TableCell>{t.name}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{t.intervall === 'weekly' ? 'W\u00f6chentlich' : t.intervall === 'monthly' ? 'Monatlich' : t.intervall === 'yearly' ? 'J\u00e4hrlich' : t.intervall === 'custom' ? `${t.intervall_tage ?? '?'} Tage` : '\u2013'}
|
{t.intervall === 'weekly' ? 'Wöchentlich' : t.intervall === 'monthly' ? 'Monatlich' : t.intervall === 'yearly' ? 'Jährlich' : t.intervall === 'custom' ? `${t.intervall_tage ?? '?'} Tage` : '–'}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{due ? (
|
{due ? (
|
||||||
<Chip icon={<Warning />} label={`F\u00e4llig: ${formatDate(due.naechste_faellig_am)}`} color="error" size="small" />
|
<Chip icon={<Warning />} label={`Fällig: ${formatDate(due.naechste_faellig_am)}`} color="error" size="small" />
|
||||||
) : (
|
) : (
|
||||||
<Typography variant="body2" color="text.secondary">Aktuell</Typography>
|
<Typography variant="body2" color="text.secondary">Aktuell</Typography>
|
||||||
)}
|
)}
|
||||||
@@ -195,7 +195,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
startIcon={<PlayArrow />}
|
startIcon={<PlayArrow />}
|
||||||
onClick={() => navigate(`/checklisten/ausfuehrung/new?fahrzeug=${fahrzeugId}&vorlage=${t.id}`)}
|
onClick={() => navigate(`/checklisten/ausfuehrung/new?fahrzeug=${fahrzeugId}&vorlage=${t.id}`)}
|
||||||
>
|
>
|
||||||
Ausf\u00fchren
|
Ausführen
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -210,12 +210,12 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
|
|
||||||
{/* Section 3: Recent executions */}
|
{/* Section 3: Recent executions */}
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h6" sx={{ mb: 1.5 }}>Letzte Ausf\u00fchrungen</Typography>
|
<Typography variant="h6" sx={{ mb: 1.5 }}>Letzte Ausführungen</Typography>
|
||||||
|
|
||||||
{executionsLoading ? (
|
{executionsLoading ? (
|
||||||
<CircularProgress size={24} />
|
<CircularProgress size={24} />
|
||||||
) : executions.length === 0 ? (
|
) : executions.length === 0 ? (
|
||||||
<Typography color="text.secondary">Noch keine Ausf\u00fchrungen f\u00fcr dieses Fahrzeug.</Typography>
|
<Typography color="text.secondary">Noch keine Ausführungen für dieses Fahrzeug.</Typography>
|
||||||
) : (
|
) : (
|
||||||
<TableContainer component={Paper} variant="outlined">
|
<TableContainer component={Paper} variant="outlined">
|
||||||
<Table size="small">
|
<Table size="small">
|
||||||
@@ -224,7 +224,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
<TableCell>Datum</TableCell>
|
<TableCell>Datum</TableCell>
|
||||||
<TableCell>Vorlage</TableCell>
|
<TableCell>Vorlage</TableCell>
|
||||||
<TableCell>Status</TableCell>
|
<TableCell>Status</TableCell>
|
||||||
<TableCell>Ausgef\u00fchrt von</TableCell>
|
<TableCell>Ausgeführt von</TableCell>
|
||||||
<TableCell>Freigegeben von</TableCell>
|
<TableCell>Freigegeben von</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
@@ -232,12 +232,12 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
|
|||||||
{executions.slice(0, 20).map((e) => (
|
{executions.slice(0, 20).map((e) => (
|
||||||
<TableRow key={e.id} hover sx={{ cursor: 'pointer' }} onClick={() => navigate(`/checklisten/ausfuehrung/${e.id}`)}>
|
<TableRow key={e.id} hover sx={{ cursor: 'pointer' }} onClick={() => navigate(`/checklisten/ausfuehrung/${e.id}`)}>
|
||||||
<TableCell>{formatDate(e.ausgefuehrt_am ?? e.created_at)}</TableCell>
|
<TableCell>{formatDate(e.ausgefuehrt_am ?? e.created_at)}</TableCell>
|
||||||
<TableCell>{e.vorlage_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.vorlage_name ?? '–'}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Chip label={CHECKLIST_STATUS_LABELS[e.status]} color={CHECKLIST_STATUS_COLORS[e.status]} size="small" />
|
<Chip label={CHECKLIST_STATUS_LABELS[e.status]} color={CHECKLIST_STATUS_COLORS[e.status]} size="small" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{e.ausgefuehrt_von_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.ausgefuehrt_von_name ?? '–'}</TableCell>
|
||||||
<TableCell>{e.freigegeben_von_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.freigegeben_von_name ?? '–'}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
|
|||||||
@@ -228,6 +228,16 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
|||||||
issuesSubItems.push({ text: 'Einstellungen', path: `/issues?tab=${issuesSubItems.length}` });
|
issuesSubItems.push({ text: 'Einstellungen', path: `/issues?tab=${issuesSubItems.length}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build Checklisten sub-items dynamically (tab order must match Checklisten.tsx)
|
||||||
|
const checklistenSubItems: SubItem[] = [
|
||||||
|
{ text: 'Übersicht', path: '/checklisten?tab=0' },
|
||||||
|
];
|
||||||
|
if (hasPermission('checklisten:manage_templates')) {
|
||||||
|
checklistenSubItems.push({ text: 'Vorlagen', path: '/checklisten?tab=1' });
|
||||||
|
checklistenSubItems.push({ text: 'Fahrzeugtypen', path: '/checklisten?tab=2' });
|
||||||
|
}
|
||||||
|
checklistenSubItems.push({ text: 'Historie', path: `/checklisten?tab=${checklistenSubItems.length}` });
|
||||||
|
|
||||||
const items = baseNavigationItems
|
const items = baseNavigationItems
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (item.path === '/fahrzeuge') return fahrzeugeItem;
|
if (item.path === '/fahrzeuge') return fahrzeugeItem;
|
||||||
@@ -239,6 +249,7 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
|||||||
return { ...item, subItems: ausruestungSubItems, permission: canSeeAusruestung ? undefined : 'ausruestungsanfrage:view' };
|
return { ...item, subItems: ausruestungSubItems, permission: canSeeAusruestung ? undefined : 'ausruestungsanfrage:view' };
|
||||||
}
|
}
|
||||||
if (item.path === '/issues') return { ...item, subItems: issuesSubItems };
|
if (item.path === '/issues') return { ...item, subItems: issuesSubItems };
|
||||||
|
if (item.path === '/checklisten') return { ...item, subItems: checklistenSubItems };
|
||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
.filter((item) => !item.permission || hasPermission(item.permission));
|
.filter((item) => !item.permission || hasPermission(item.permission));
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import type { ChecklistAusfuehrungItem } from '../types/checklist.types';
|
|||||||
// ── Helpers ──
|
// ── Helpers ──
|
||||||
|
|
||||||
const formatDate = (iso?: string) =>
|
const formatDate = (iso?: string) =>
|
||||||
iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : '\u2013';
|
iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : '–';
|
||||||
|
|
||||||
const ERGEBNIS_ICONS: Record<string, JSX.Element> = {
|
const ERGEBNIS_ICONS: Record<string, JSX.Element> = {
|
||||||
ok: <CheckCircle fontSize="small" color="success" />,
|
ok: <CheckCircle fontSize="small" color="success" />,
|
||||||
@@ -110,7 +110,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
queryClient.invalidateQueries({ queryKey: ['checklisten-faellig'] });
|
queryClient.invalidateQueries({ queryKey: ['checklisten-faellig'] });
|
||||||
showSuccess('Checkliste abgeschlossen');
|
showSuccess('Checkliste abgeschlossen');
|
||||||
},
|
},
|
||||||
onError: () => showError('Fehler beim Abschlie\u00dfen'),
|
onError: () => showError('Fehler beim Abschließen'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Approve ──
|
// ── Approve ──
|
||||||
@@ -145,7 +145,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
<Alert severity="error">Checkliste konnte nicht geladen werden.</Alert>
|
<Alert severity="error">Checkliste konnte nicht geladen werden.</Alert>
|
||||||
<Button startIcon={<ArrowBack />} onClick={() => navigate('/checklisten')} sx={{ mt: 2 }}>Zur\u00fcck</Button>
|
<Button startIcon={<ArrowBack />} onClick={() => navigate('/checklisten')} sx={{ mt: 2 }}>Zurück</Button>
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
{execution.vorlage_name ?? 'Checkliste'}
|
{execution.vorlage_name ?? 'Checkliste'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="subtitle1" color="text.secondary">
|
<Typography variant="subtitle1" color="text.secondary">
|
||||||
{execution.fahrzeug_name ?? '\u2013'} · {formatDate(execution.ausgefuehrt_am ?? execution.created_at)}
|
{execution.fahrzeug_name ?? '–'} · {formatDate(execution.ausgefuehrt_am ?? execution.created_at)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Chip
|
<Chip
|
||||||
@@ -233,7 +233,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
|
|
||||||
{execution.status === 'unvollstaendig' && (
|
{execution.status === 'unvollstaendig' && (
|
||||||
<Alert severity="warning" sx={{ mb: 2 }}>
|
<Alert severity="warning" sx={{ mb: 2 }}>
|
||||||
Diese Checkliste wurde als unvollst\u00e4ndig abgeschlossen. Einige Pflicht-Items wurden nicht mit "OK" bewertet.
|
Diese Checkliste wurde als unvollständig abgeschlossen. Einige Pflicht-Items wurden nicht mit "OK" bewertet.
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -260,7 +260,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
fullWidth
|
fullWidth
|
||||||
multiline
|
multiline
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder="Zus\u00e4tzliche Notizen..."
|
placeholder="Zusätzliche Notizen..."
|
||||||
value={notizen}
|
value={notizen}
|
||||||
onChange={(e) => setNotizen(e.target.value)}
|
onChange={(e) => setNotizen(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -277,7 +277,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
disabled={submitMutation.isPending}
|
disabled={submitMutation.isPending}
|
||||||
startIcon={submitMutation.isPending ? <CircularProgress size={16} /> : undefined}
|
startIcon={submitMutation.isPending ? <CircularProgress size={16} /> : undefined}
|
||||||
>
|
>
|
||||||
Abschlie\u00dfen
|
Abschließen
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@ export default function ChecklistAusfuehrung() {
|
|||||||
<Paper variant="outlined" sx={{ p: 2, mt: 3 }}>
|
<Paper variant="outlined" sx={{ p: 2, mt: 3 }}>
|
||||||
{execution.ausgefuehrt_von_name && (
|
{execution.ausgefuehrt_von_name && (
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Ausgef\u00fchrt von: {execution.ausgefuehrt_von_name} am {formatDate(execution.ausgefuehrt_am)}
|
Ausgeführt von: {execution.ausgefuehrt_von_name} am {formatDate(execution.ausgefuehrt_am)}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
{execution.freigegeben_von_name && (
|
{execution.freigegeben_von_name && (
|
||||||
|
|||||||
@@ -64,12 +64,12 @@ import type {
|
|||||||
// ── Helpers ──
|
// ── Helpers ──
|
||||||
|
|
||||||
const formatDate = (iso?: string) =>
|
const formatDate = (iso?: string) =>
|
||||||
iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) : '\u2013';
|
iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) : '–';
|
||||||
|
|
||||||
const INTERVALL_LABELS: Record<string, string> = {
|
const INTERVALL_LABELS: Record<string, string> = {
|
||||||
weekly: 'W\u00f6chentlich',
|
weekly: 'Wöchentlich',
|
||||||
monthly: 'Monatlich',
|
monthly: 'Monatlich',
|
||||||
yearly: 'J\u00e4hrlich',
|
yearly: 'Jährlich',
|
||||||
custom: 'Benutzerdefiniert',
|
custom: 'Benutzerdefiniert',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ export default function Checklisten() {
|
|||||||
const canManageTemplates = hasPermission('checklisten:manage_templates');
|
const canManageTemplates = hasPermission('checklisten:manage_templates');
|
||||||
const canExecute = hasPermission('checklisten:execute');
|
const canExecute = hasPermission('checklisten:execute');
|
||||||
|
|
||||||
// Tabs: 0=\u00dcbersicht, 1=Vorlagen (if perm), 2=Fahrzeugtypen (if perm), 3=Historie
|
// Tabs: 0=Übersicht, 1=Vorlagen (if perm), 2=Fahrzeugtypen (if perm), 3=Historie
|
||||||
const manageTabs = canManageTemplates ? 2 : 0;
|
const manageTabs = canManageTemplates ? 2 : 0;
|
||||||
const TAB_COUNT = 2 + manageTabs;
|
const TAB_COUNT = 2 + manageTabs;
|
||||||
|
|
||||||
@@ -160,14 +160,14 @@ export default function Checklisten() {
|
|||||||
variant="scrollable"
|
variant="scrollable"
|
||||||
scrollButtons="auto"
|
scrollButtons="auto"
|
||||||
>
|
>
|
||||||
<Tab label="\u00dcbersicht" />
|
<Tab label="Übersicht" />
|
||||||
{canManageTemplates && <Tab label="Vorlagen" />}
|
{canManageTemplates && <Tab label="Vorlagen" />}
|
||||||
{canManageTemplates && <Tab label="Fahrzeugtypen" />}
|
{canManageTemplates && <Tab label="Fahrzeugtypen" />}
|
||||||
<Tab label="Historie" />
|
<Tab label="Historie" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Tab 0: \u00dcbersicht */}
|
{/* Tab 0: Übersicht */}
|
||||||
<TabPanel value={tab} index={0}>
|
<TabPanel value={tab} index={0}>
|
||||||
{vehiclesLoading ? (
|
{vehiclesLoading ? (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}><CircularProgress /></Box>
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}><CircularProgress /></Box>
|
||||||
@@ -185,7 +185,7 @@ export default function Checklisten() {
|
|||||||
{vOverdue.length > 0 && (
|
{vOverdue.length > 0 && (
|
||||||
<Chip
|
<Chip
|
||||||
icon={<Warning />}
|
icon={<Warning />}
|
||||||
label={`${vOverdue.length} f\u00e4llig`}
|
label={`${vOverdue.length} fällig`}
|
||||||
color="error"
|
color="error"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
@@ -200,7 +200,7 @@ export default function Checklisten() {
|
|||||||
<Warning fontSize="small" color="error" />
|
<Warning fontSize="small" color="error" />
|
||||||
<Typography variant="body2">{f.vorlage_name}</Typography>
|
<Typography variant="body2">{f.vorlage_name}</Typography>
|
||||||
<Typography variant="caption" color="error.main">
|
<Typography variant="caption" color="error.main">
|
||||||
({days > 0 ? `${days}d \u00fcberf\u00e4llig` : 'heute f\u00e4llig'})
|
({days > 0 ? `${days}d überfällig` : 'heute fällig'})
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{canExecute && (
|
{canExecute && (
|
||||||
@@ -304,8 +304,8 @@ function VorlagenTab({ vorlagen, loading, fahrzeugTypen, queryClient, showSucces
|
|||||||
|
|
||||||
const deleteMutation = useMutation({
|
const deleteMutation = useMutation({
|
||||||
mutationFn: (id: number) => checklistenApi.deleteVorlage(id),
|
mutationFn: (id: number) => checklistenApi.deleteVorlage(id),
|
||||||
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-vorlagen'] }); showSuccess('Vorlage gel\u00f6scht'); },
|
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-vorlagen'] }); showSuccess('Vorlage gelöscht'); },
|
||||||
onError: () => showError('Fehler beim L\u00f6schen der Vorlage'),
|
onError: () => showError('Fehler beim Löschen der Vorlage'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const openCreate = () => { setEditingVorlage(null); setForm(emptyForm); setDialogOpen(true); };
|
const openCreate = () => { setEditingVorlage(null); setForm(emptyForm); setDialogOpen(true); };
|
||||||
@@ -353,9 +353,9 @@ function VorlagenTab({ vorlagen, loading, fahrzeugTypen, queryClient, showSucces
|
|||||||
<React.Fragment key={v.id}>
|
<React.Fragment key={v.id}>
|
||||||
<TableRow hover sx={{ cursor: 'pointer' }} onClick={() => setExpandedVorlageId(expandedVorlageId === v.id ? null : v.id)}>
|
<TableRow hover sx={{ cursor: 'pointer' }} onClick={() => setExpandedVorlageId(expandedVorlageId === v.id ? null : v.id)}>
|
||||||
<TableCell>{v.name}</TableCell>
|
<TableCell>{v.name}</TableCell>
|
||||||
<TableCell>{v.fahrzeug_typ?.name ?? '\u2013'}</TableCell>
|
<TableCell>{v.fahrzeug_typ?.name ?? '–'}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{v.intervall ? INTERVALL_LABELS[v.intervall] || v.intervall : '\u2013'}
|
{v.intervall ? INTERVALL_LABELS[v.intervall] || v.intervall : '–'}
|
||||||
{v.intervall === 'custom' && v.intervall_tage ? ` (${v.intervall_tage} Tage)` : ''}
|
{v.intervall === 'custom' && v.intervall_tage ? ` (${v.intervall_tage} Tage)` : ''}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
@@ -396,9 +396,9 @@ function VorlagenTab({ vorlagen, loading, fahrzeugTypen, queryClient, showSucces
|
|||||||
<InputLabel>Intervall</InputLabel>
|
<InputLabel>Intervall</InputLabel>
|
||||||
<Select label="Intervall" value={form.intervall ?? ''} onChange={(e) => setForm((f) => ({ ...f, intervall: (e.target.value || undefined) as CreateVorlagePayload['intervall'] }))}>
|
<Select label="Intervall" value={form.intervall ?? ''} onChange={(e) => setForm((f) => ({ ...f, intervall: (e.target.value || undefined) as CreateVorlagePayload['intervall'] }))}>
|
||||||
<MenuItem value="">Kein Intervall</MenuItem>
|
<MenuItem value="">Kein Intervall</MenuItem>
|
||||||
<MenuItem value="weekly">W\u00f6chentlich</MenuItem>
|
<MenuItem value="weekly">Wöchentlich</MenuItem>
|
||||||
<MenuItem value="monthly">Monatlich</MenuItem>
|
<MenuItem value="monthly">Monatlich</MenuItem>
|
||||||
<MenuItem value="yearly">J\u00e4hrlich</MenuItem>
|
<MenuItem value="yearly">Jährlich</MenuItem>
|
||||||
<MenuItem value="custom">Benutzerdefiniert</MenuItem>
|
<MenuItem value="custom">Benutzerdefiniert</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -431,8 +431,8 @@ function VorlageItemsSection({ vorlageId, queryClient, showSuccess, showError }:
|
|||||||
|
|
||||||
const addMutation = useMutation({
|
const addMutation = useMutation({
|
||||||
mutationFn: (data: CreateVorlageItemPayload) => checklistenApi.addVorlageItem(vorlageId, data),
|
mutationFn: (data: CreateVorlageItemPayload) => checklistenApi.addVorlageItem(vorlageId, data),
|
||||||
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-vorlage-items', vorlageId] }); setNewItem({ bezeichnung: '', pflicht: false, sort_order: 0 }); showSuccess('Item hinzugef\u00fcgt'); },
|
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-vorlage-items', vorlageId] }); setNewItem({ bezeichnung: '', pflicht: false, sort_order: 0 }); showSuccess('Item hinzugefügt'); },
|
||||||
onError: () => showError('Fehler beim Hinzuf\u00fcgen'),
|
onError: () => showError('Fehler beim Hinzufügen'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteMutation = useMutation({
|
const deleteMutation = useMutation({
|
||||||
@@ -458,7 +458,7 @@ function VorlageItemsSection({ vorlageId, queryClient, showSuccess, showError }:
|
|||||||
<TextField size="small" placeholder="Neues Item..." value={newItem.bezeichnung} onChange={(e) => setNewItem((n) => ({ ...n, bezeichnung: e.target.value }))} sx={{ flexGrow: 1 }} />
|
<TextField size="small" placeholder="Neues Item..." value={newItem.bezeichnung} onChange={(e) => setNewItem((n) => ({ ...n, bezeichnung: e.target.value }))} sx={{ flexGrow: 1 }} />
|
||||||
<FormControlLabel control={<Switch size="small" checked={newItem.pflicht} onChange={(e) => setNewItem((n) => ({ ...n, pflicht: e.target.checked }))} />} label="Pflicht" />
|
<FormControlLabel control={<Switch size="small" checked={newItem.pflicht} onChange={(e) => setNewItem((n) => ({ ...n, pflicht: e.target.checked }))} />} label="Pflicht" />
|
||||||
<Button size="small" variant="outlined" disabled={!newItem.bezeichnung.trim() || addMutation.isPending} onClick={() => addMutation.mutate(newItem)}>
|
<Button size="small" variant="outlined" disabled={!newItem.bezeichnung.trim() || addMutation.isPending} onClick={() => addMutation.mutate(newItem)}>
|
||||||
Hinzuf\u00fcgen
|
Hinzufügen
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -495,8 +495,8 @@ function FahrzeugTypenTab({ fahrzeugTypen, queryClient, showSuccess, showError }
|
|||||||
|
|
||||||
const deleteMutation = useMutation({
|
const deleteMutation = useMutation({
|
||||||
mutationFn: (id: number) => fahrzeugTypenApi.delete(id),
|
mutationFn: (id: number) => fahrzeugTypenApi.delete(id),
|
||||||
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['fahrzeug-typen'] }); showSuccess('Fahrzeugtyp gel\u00f6scht'); },
|
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['fahrzeug-typen'] }); showSuccess('Fahrzeugtyp gelöscht'); },
|
||||||
onError: () => showError('Fehler beim L\u00f6schen'),
|
onError: () => showError('Fehler beim Löschen'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const openCreate = () => { setEditing(null); setForm({ name: '', beschreibung: '', icon: '' }); setDialogOpen(true); };
|
const openCreate = () => { setEditing(null); setForm({ name: '', beschreibung: '', icon: '' }); setDialogOpen(true); };
|
||||||
@@ -536,8 +536,8 @@ function FahrzeugTypenTab({ fahrzeugTypen, queryClient, showSuccess, showError }
|
|||||||
fahrzeugTypen.map((t) => (
|
fahrzeugTypen.map((t) => (
|
||||||
<TableRow key={t.id} hover>
|
<TableRow key={t.id} hover>
|
||||||
<TableCell>{t.name}</TableCell>
|
<TableCell>{t.name}</TableCell>
|
||||||
<TableCell>{t.beschreibung ?? '\u2013'}</TableCell>
|
<TableCell>{t.beschreibung ?? '–'}</TableCell>
|
||||||
<TableCell>{t.icon ?? '\u2013'}</TableCell>
|
<TableCell>{t.icon ?? '–'}</TableCell>
|
||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
<IconButton size="small" onClick={() => openEdit(t)}><EditIcon fontSize="small" /></IconButton>
|
<IconButton size="small" onClick={() => openEdit(t)}><EditIcon fontSize="small" /></IconButton>
|
||||||
<IconButton size="small" color="error" onClick={() => deleteMutation.mutate(t.id)}><DeleteIcon fontSize="small" /></IconButton>
|
<IconButton size="small" color="error" onClick={() => deleteMutation.mutate(t.id)}><DeleteIcon fontSize="small" /></IconButton>
|
||||||
@@ -620,24 +620,24 @@ function HistorieTab({ executions, loading, navigate }: HistorieTabProps) {
|
|||||||
<TableCell>Vorlage</TableCell>
|
<TableCell>Vorlage</TableCell>
|
||||||
<TableCell>Datum</TableCell>
|
<TableCell>Datum</TableCell>
|
||||||
<TableCell>Status</TableCell>
|
<TableCell>Status</TableCell>
|
||||||
<TableCell>Ausgef\u00fchrt von</TableCell>
|
<TableCell>Ausgeführt von</TableCell>
|
||||||
<TableCell>Freigegeben von</TableCell>
|
<TableCell>Freigegeben von</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{filtered.length === 0 ? (
|
{filtered.length === 0 ? (
|
||||||
<TableRow><TableCell colSpan={6} align="center">Keine Eintr\u00e4ge</TableCell></TableRow>
|
<TableRow><TableCell colSpan={6} align="center">Keine Einträge</TableCell></TableRow>
|
||||||
) : (
|
) : (
|
||||||
filtered.map((e) => (
|
filtered.map((e) => (
|
||||||
<TableRow key={e.id} hover sx={{ cursor: 'pointer' }} onClick={() => navigate(`/checklisten/ausfuehrung/${e.id}`)}>
|
<TableRow key={e.id} hover sx={{ cursor: 'pointer' }} onClick={() => navigate(`/checklisten/ausfuehrung/${e.id}`)}>
|
||||||
<TableCell>{e.fahrzeug_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.fahrzeug_name ?? '–'}</TableCell>
|
||||||
<TableCell>{e.vorlage_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.vorlage_name ?? '–'}</TableCell>
|
||||||
<TableCell>{formatDate(e.ausgefuehrt_am ?? e.created_at)}</TableCell>
|
<TableCell>{formatDate(e.ausgefuehrt_am ?? e.created_at)}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Chip label={CHECKLIST_STATUS_LABELS[e.status]} color={CHECKLIST_STATUS_COLORS[e.status]} size="small" />
|
<Chip label={CHECKLIST_STATUS_LABELS[e.status]} color={CHECKLIST_STATUS_COLORS[e.status]} size="small" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{e.ausgefuehrt_von_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.ausgefuehrt_von_name ?? '–'}</TableCell>
|
||||||
<TableCell>{e.freigegeben_von_name ?? '\u2013'}</TableCell>
|
<TableCell>{e.freigegeben_von_name ?? '–'}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user