featur add fahrmeister
This commit is contained in:
@@ -62,6 +62,7 @@ import {
|
||||
WartungslogArt,
|
||||
PruefungErgebnis,
|
||||
} from '../types/vehicle.types';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
|
||||
// ── Tab Panel ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -114,9 +115,10 @@ function inspectionBadgeColor(tage: number | null): 'success' | 'warning' | 'err
|
||||
interface UebersichtTabProps {
|
||||
vehicle: FahrzeugDetail;
|
||||
onStatusUpdated: () => void;
|
||||
canChangeStatus: boolean;
|
||||
}
|
||||
|
||||
const UebersichtTab: React.FC<UebersichtTabProps> = ({ vehicle, onStatusUpdated }) => {
|
||||
const UebersichtTab: React.FC<UebersichtTabProps> = ({ vehicle, onStatusUpdated, canChangeStatus }) => {
|
||||
const [statusDialogOpen, setStatusDialogOpen] = useState(false);
|
||||
const [newStatus, setNewStatus] = useState<FahrzeugStatus>(vehicle.status);
|
||||
const [bemerkung, setBemerkung] = useState(vehicle.status_bemerkung ?? '');
|
||||
@@ -177,6 +179,7 @@ const UebersichtTab: React.FC<UebersichtTabProps> = ({ vehicle, onStatusUpdated
|
||||
setBemerkung(vehicle.status_bemerkung ?? '');
|
||||
setStatusDialogOpen(true);
|
||||
}}
|
||||
sx={{ display: canChangeStatus ? undefined : 'none' }}
|
||||
>
|
||||
Status ändern
|
||||
</Button>
|
||||
@@ -195,6 +198,8 @@ const UebersichtTab: React.FC<UebersichtTabProps> = ({ vehicle, onStatusUpdated
|
||||
{ label: 'Typ (DIN 14502)', value: vehicle.typ_schluessel },
|
||||
{ label: 'Besatzung (Soll)', value: vehicle.besatzung_soll },
|
||||
{ label: 'Standort', value: vehicle.standort },
|
||||
{ label: '§57a fällig am', value: fmtDate(vehicle.paragraph57a_faellig_am) !== '—' ? fmtDate(vehicle.paragraph57a_faellig_am) : null },
|
||||
{ label: 'Nächste Wartung', value: fmtDate(vehicle.naechste_wartung_am) !== '—' ? fmtDate(vehicle.naechste_wartung_am) : null },
|
||||
].map(({ label, value }) => (
|
||||
<Grid item xs={12} sm={6} md={4} key={label}>
|
||||
<Typography variant="caption" color="text.secondary" textTransform="uppercase">
|
||||
@@ -311,6 +316,7 @@ interface PruefungenTabProps {
|
||||
fahrzeugId: string;
|
||||
pruefungen: FahrzeugPruefung[];
|
||||
onAdded: () => void;
|
||||
canWrite: boolean;
|
||||
}
|
||||
|
||||
const ERGEBNIS_LABELS: Record<PruefungErgebnis, string> = {
|
||||
@@ -327,7 +333,7 @@ const ERGEBNIS_COLORS: Record<PruefungErgebnis, 'success' | 'warning' | 'error'
|
||||
ausstehend: 'default',
|
||||
};
|
||||
|
||||
const PruefungenTab: React.FC<PruefungenTabProps> = ({ fahrzeugId, pruefungen, onAdded }) => {
|
||||
const PruefungenTab: React.FC<PruefungenTabProps> = ({ fahrzeugId, pruefungen, onAdded, canWrite }) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
@@ -431,15 +437,17 @@ const PruefungenTab: React.FC<PruefungenTabProps> = ({ fahrzeugId, pruefungen, o
|
||||
)}
|
||||
|
||||
{/* FAB */}
|
||||
<Fab
|
||||
color="primary"
|
||||
size="small"
|
||||
aria-label="Prüfung hinzufügen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: 32 }}
|
||||
onClick={() => { setForm(emptyForm); setDialogOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
{canWrite && (
|
||||
<Fab
|
||||
color="primary"
|
||||
size="small"
|
||||
aria-label="Prüfung hinzufügen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: 32 }}
|
||||
onClick={() => { setForm(emptyForm); setDialogOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
)}
|
||||
|
||||
{/* Add inspection dialog */}
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
@@ -549,6 +557,7 @@ interface WartungTabProps {
|
||||
fahrzeugId: string;
|
||||
wartungslog: FahrzeugWartungslog[];
|
||||
onAdded: () => void;
|
||||
canWrite: boolean;
|
||||
}
|
||||
|
||||
const WARTUNG_ART_ICONS: Record<string, React.ReactElement> = {
|
||||
@@ -559,7 +568,7 @@ const WARTUNG_ART_ICONS: Record<string, React.ReactElement> = {
|
||||
default: <Build color="action" />,
|
||||
};
|
||||
|
||||
const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdded }) => {
|
||||
const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdded, canWrite }) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
@@ -634,15 +643,17 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Fab
|
||||
color="primary"
|
||||
size="small"
|
||||
aria-label="Wartung eintragen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: 32 }}
|
||||
onClick={() => { setForm(emptyForm); setDialogOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
{canWrite && (
|
||||
<Fab
|
||||
color="primary"
|
||||
size="small"
|
||||
aria-label="Wartung eintragen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: 32 }}
|
||||
onClick={() => { setForm(emptyForm); setDialogOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
)}
|
||||
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>Wartung / Service eintragen</DialogTitle>
|
||||
@@ -746,6 +757,7 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
function FahrzeugDetail() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { isAdmin, canChangeStatus } = usePermissions();
|
||||
|
||||
const [vehicle, setVehicle] = useState<FahrzeugDetail | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -860,7 +872,7 @@ function FahrzeugDetail() {
|
||||
|
||||
{/* Tab content */}
|
||||
<TabPanel value={activeTab} index={0}>
|
||||
<UebersichtTab vehicle={vehicle} onStatusUpdated={fetchVehicle} />
|
||||
<UebersichtTab vehicle={vehicle} onStatusUpdated={fetchVehicle} canChangeStatus={canChangeStatus} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={activeTab} index={1}>
|
||||
@@ -868,6 +880,7 @@ function FahrzeugDetail() {
|
||||
fahrzeugId={vehicle.id}
|
||||
pruefungen={vehicle.pruefungen}
|
||||
onAdded={fetchVehicle}
|
||||
canWrite={isAdmin}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
@@ -876,6 +889,7 @@ function FahrzeugDetail() {
|
||||
fahrzeugId={vehicle.id}
|
||||
wartungslog={vehicle.wartungslog}
|
||||
onAdded={fetchVehicle}
|
||||
canWrite={isAdmin}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
PruefungArt,
|
||||
PruefungArtLabel,
|
||||
} from '../types/vehicle.types';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
|
||||
// ── Status chip config ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -87,10 +88,8 @@ const VehicleCard: React.FC<VehicleCardProps> = ({ vehicle, onClick }) => {
|
||||
|
||||
// Collect inspection badges (only for types where a faellig_am exists)
|
||||
const inspBadges: { art: string; tage: number | null; faelligAm: string | null }[] = [
|
||||
{ art: 'HU', tage: vehicle.hu_tage_bis_faelligkeit, faelligAm: vehicle.hu_faellig_am },
|
||||
{ art: 'AU', tage: vehicle.au_tage_bis_faelligkeit, faelligAm: vehicle.au_faellig_am },
|
||||
{ art: 'UVV', tage: vehicle.uvv_tage_bis_faelligkeit, faelligAm: vehicle.uvv_faellig_am },
|
||||
{ art: 'Leiter', tage: vehicle.leiter_tage_bis_faelligkeit, faelligAm: vehicle.leiter_faellig_am },
|
||||
{ art: '§57a', tage: vehicle.paragraph57a_tage_bis_faelligkeit, faelligAm: vehicle.paragraph57a_faellig_am },
|
||||
{ art: 'Wartung', tage: vehicle.wartung_tage_bis_faelligkeit, faelligAm: vehicle.naechste_wartung_am },
|
||||
].filter((b) => b.faelligAm !== null);
|
||||
|
||||
return (
|
||||
@@ -218,6 +217,7 @@ const VehicleCard: React.FC<VehicleCardProps> = ({ vehicle, onClick }) => {
|
||||
|
||||
function Fahrzeuge() {
|
||||
const navigate = useNavigate();
|
||||
const { isAdmin } = usePermissions();
|
||||
const [vehicles, setVehicles] = useState<FahrzeugListItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -347,14 +347,16 @@ function Fahrzeuge() {
|
||||
)}
|
||||
|
||||
{/* FAB — add vehicle (shown to write-role users only; role check done server-side) */}
|
||||
<Fab
|
||||
color="primary"
|
||||
aria-label="Fahrzeug hinzufügen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: 32 }}
|
||||
onClick={() => navigate('/fahrzeuge/neu')}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
{isAdmin && (
|
||||
<Fab
|
||||
color="primary"
|
||||
aria-label="Fahrzeug hinzufügen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: 32 }}
|
||||
onClick={() => navigate('/fahrzeuge/neu')}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
)}
|
||||
</Container>
|
||||
</DashboardLayout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user