fix: add checklisten sidebar sub-items and replace unicode escapes with proper umlauts

This commit is contained in:
Matthias Hochmeister
2026-03-28 16:27:32 +01:00
parent b171c3e921
commit 692093cc85
4 changed files with 62 additions and 51 deletions

View File

@@ -30,7 +30,7 @@ import type { CreateFahrzeugItemPayload } from '../../types/checklist.types';
// ── Helpers ──
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
@@ -76,8 +76,8 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
const addItemMutation = useMutation({
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'); },
onError: () => showError('Fehler beim Hinzuf\u00fcgen'),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['checklisten-fahrzeug-items', fahrzeugId] }); setNewItem({ bezeichnung: '', pflicht: false, sort_order: 0 }); showSuccess('Item hinzugefügt'); },
onError: () => showError('Fehler beim Hinzufügen'),
});
const deleteItemMutation = useMutation({
@@ -120,7 +120,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
vehicleItems.map((item) => (
<TableRow key={item.id}>
<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">
<Switch
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 }} />
<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)}>
Hinzuf\u00fcgen
Hinzufügen
</Button>
</Box>
</>
@@ -159,7 +159,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
{templatesLoading ? (
<CircularProgress size={24} />
) : 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">
<Table size="small">
@@ -167,7 +167,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
<TableRow>
<TableCell>Vorlage</TableCell>
<TableCell>Intervall</TableCell>
<TableCell>N\u00e4chste F\u00e4lligkeit</TableCell>
<TableCell>Nächste lligkeit</TableCell>
<TableCell align="right">Aktion</TableCell>
</TableRow>
</TableHead>
@@ -178,11 +178,11 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
<TableRow key={t.id}>
<TableCell>{t.name}</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>
{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>
)}
@@ -195,7 +195,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
startIcon={<PlayArrow />}
onClick={() => navigate(`/checklisten/ausfuehrung/new?fahrzeug=${fahrzeugId}&vorlage=${t.id}`)}
>
Ausf\u00fchren
Ausführen
</Button>
)}
</TableCell>
@@ -210,12 +210,12 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
{/* Section 3: Recent executions */}
<Box>
<Typography variant="h6" sx={{ mb: 1.5 }}>Letzte Ausf\u00fchrungen</Typography>
<Typography variant="h6" sx={{ mb: 1.5 }}>Letzte Ausführungen</Typography>
{executionsLoading ? (
<CircularProgress size={24} />
) : 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">
<Table size="small">
@@ -224,7 +224,7 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
<TableCell>Datum</TableCell>
<TableCell>Vorlage</TableCell>
<TableCell>Status</TableCell>
<TableCell>Ausgef\u00fchrt von</TableCell>
<TableCell>Ausgeführt von</TableCell>
<TableCell>Freigegeben von</TableCell>
</TableRow>
</TableHead>
@@ -232,12 +232,12 @@ const FahrzeugChecklistTab: React.FC<FahrzeugChecklistTabProps> = ({ fahrzeugId
{executions.slice(0, 20).map((e) => (
<TableRow key={e.id} hover sx={{ cursor: 'pointer' }} onClick={() => navigate(`/checklisten/ausfuehrung/${e.id}`)}>
<TableCell>{formatDate(e.ausgefuehrt_am ?? e.created_at)}</TableCell>
<TableCell>{e.vorlage_name ?? '\u2013'}</TableCell>
<TableCell>{e.vorlage_name ?? ''}</TableCell>
<TableCell>
<Chip label={CHECKLIST_STATUS_LABELS[e.status]} color={CHECKLIST_STATUS_COLORS[e.status]} size="small" />
</TableCell>
<TableCell>{e.ausgefuehrt_von_name ?? '\u2013'}</TableCell>
<TableCell>{e.freigegeben_von_name ?? '\u2013'}</TableCell>
<TableCell>{e.ausgefuehrt_von_name ?? ''}</TableCell>
<TableCell>{e.freigegeben_von_name ?? ''}</TableCell>
</TableRow>
))}
</TableBody>

View File

@@ -228,6 +228,16 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
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
.map((item) => {
if (item.path === '/fahrzeuge') return fahrzeugeItem;
@@ -239,6 +249,7 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
return { ...item, subItems: ausruestungSubItems, permission: canSeeAusruestung ? undefined : 'ausruestungsanfrage:view' };
}
if (item.path === '/issues') return { ...item, subItems: issuesSubItems };
if (item.path === '/checklisten') return { ...item, subItems: checklistenSubItems };
return item;
})
.filter((item) => !item.permission || hasPermission(item.permission));