feat(frontend): implement unified design system with 17 reusable template components, skeleton loading states, and golden-ratio-based layouts

This commit is contained in:
Matthias Hochmeister
2026-04-13 10:43:27 +02:00
parent 5acfd7cc4f
commit 43ce1f930c
69 changed files with 3289 additions and 3115 deletions

View File

@@ -27,7 +27,6 @@ import {
AccessTime,
DirectionsCar,
People,
LocationOn,
Description,
PictureAsPdf,
} from '@mui/icons-material';
@@ -44,6 +43,7 @@ import {
} from '../services/incidents';
import { useNotification } from '../contexts/NotificationContext';
import { usePermissionContext } from '../contexts/PermissionContext';
import { PageHeader } from '../components/templates';
// ---------------------------------------------------------------------------
// COLOUR MAPS
@@ -280,94 +280,75 @@ function EinsatzDetail() {
return (
<DashboardLayout>
<Container maxWidth="lg">
{/* Back + Actions */}
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Button
startIcon={<ArrowBack />}
onClick={() => navigate('/einsaetze')}
variant="text"
>
Zurück
</Button>
<Stack direction="row" spacing={1}>
<Tooltip title="PDF exportieren (Vorschau)">
<Button
<PageHeader
title={`Einsatz ${einsatz.einsatz_nr}`}
subtitle={address || undefined}
backTo="/einsaetze"
actions={
<Stack direction="row" spacing={1} alignItems="center">
<Chip
icon={<LocalFireDepartment />}
label={EINSATZ_ART_LABELS[einsatz.einsatz_art]}
color={ART_CHIP_COLOR[einsatz.einsatz_art]}
sx={{ fontWeight: 600 }}
/>
<Chip
label={EINSATZ_STATUS_LABELS[einsatz.status]}
color={STATUS_CHIP_COLOR[einsatz.status]}
variant="outlined"
startIcon={<PictureAsPdf />}
onClick={handleExportPdf}
size="small"
>
PDF Export
</Button>
</Tooltip>
{canWrite && !editing ? (
<Button
variant="contained"
startIcon={<Edit />}
onClick={() => setEditing(true)}
size="small"
>
Bearbeiten
</Button>
) : canWrite && editing ? (
<>
/>
<Tooltip title="PDF exportieren (Vorschau)">
<Button
variant="outlined"
startIcon={<Cancel />}
onClick={handleCancelEdit}
startIcon={<PictureAsPdf />}
onClick={handleExportPdf}
size="small"
disabled={saving}
>
Abbrechen
PDF Export
</Button>
</Tooltip>
{canWrite && !editing ? (
<Button
variant="contained"
color="success"
startIcon={<Save />}
onClick={handleSaveBericht}
startIcon={<Edit />}
onClick={() => setEditing(true)}
size="small"
disabled={saving}
>
{saving ? 'Speichere...' : 'Speichern'}
Bearbeiten
</Button>
</>
) : null}
</Stack>
</Box>
) : canWrite && editing ? (
<>
<Button
variant="outlined"
startIcon={<Cancel />}
onClick={handleCancelEdit}
size="small"
disabled={saving}
>
Abbrechen
</Button>
<Button
variant="contained"
color="success"
startIcon={<Save />}
onClick={handleSaveBericht}
size="small"
disabled={saving}
>
{saving ? 'Speichere...' : 'Speichern'}
</Button>
</>
) : null}
</Stack>
}
/>
{/* HEADER */}
<Box sx={{ mb: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flexWrap: 'wrap', mb: 1 }}>
<Chip
icon={<LocalFireDepartment />}
label={EINSATZ_ART_LABELS[einsatz.einsatz_art]}
color={ART_CHIP_COLOR[einsatz.einsatz_art]}
sx={{ fontWeight: 600 }}
/>
<Chip
label={EINSATZ_STATUS_LABELS[einsatz.status]}
color={STATUS_CHIP_COLOR[einsatz.status]}
variant="outlined"
size="small"
/>
{einsatz.einsatz_stichwort && (
<Typography variant="h6" color="text.secondary">
{einsatz.einsatz_stichwort}
</Typography>
)}
</Box>
<Typography variant="h4" fontWeight={700}>
Einsatz {einsatz.einsatz_nr}
{einsatz.einsatz_stichwort && (
<Typography variant="h6" color="text.secondary" sx={{ mb: 2, mt: -2 }}>
{einsatz.einsatz_stichwort}
</Typography>
{address && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mt: 0.5 }}>
<LocationOn fontSize="small" color="action" />
<Typography variant="body1" color="text.secondary">
{address}
</Typography>
</Box>
)}
</Box>
)}
<Grid container spacing={3}>
{/* LEFT COLUMN: Timeline + Vehicles */}