'use client'; import { Dialog, Button, TextField, Typography, Box, Divider, Chip, Paper, IconButton, Stack, useMediaQuery, CircularProgress, } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { useState, useEffect, Suspense, lazy, useMemo } from 'react'; import { Tool, User } from '../types'; import { useReservationStore } from '../services/reservation-store'; import { useI18n } from '../services/i18n'; import { Calendar, Info, ShieldCheck, X, Check, MapPin, Clock } from 'lucide-react'; const AvailabilityCalendar = lazy(() => import('./AvailabilityCalendar')); interface ReservationModalProps { tool: Tool | null; user: User; open: boolean; onClose: () => void; } const pillSx = { height: 22, fontWeight: 600, fontSize: '0.7rem', borderRadius: 1.25, '& .MuiChip-icon': { marginLeft: '6px', marginRight: '-2px' }, '& .MuiChip-label': { px: 0.75 }, } as const; export default function ReservationModal({ tool, user, open, onClose, }: ReservationModalProps) { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const { t } = useI18n(); const reservations = useReservationStore((s) => s.reservations); const addReservation = useReservationStore((s) => s.addReservation); const today = useMemo(() => new Date().toISOString().split('T')[0], []); const [startDate, setStartDate] = useState(today); const [endDate, setEndDate] = useState(''); const [submitted, setSubmitted] = useState(false); useEffect(() => { if (open) { setStartDate(today); setEndDate(''); setSubmitted(false); } }, [open, today]); if (!tool) return null; const toolEvents = reservations .filter((r) => r.toolId === tool.id && r.status !== 'completed') .map((r) => ({ title: 'Belegt', start: r.startDate, end: r.endDate, display: 'background' as const, color: '#F59E0B', })); const duration = (() => { if (!startDate || !endDate) return 0; const ms = new Date(endDate).getTime() - new Date(startDate).getTime(); return Math.max(0, Math.round(ms / 86_400_000) + 1); })(); const exceedsMax = duration > tool.maxRentalDays; const invalidRange = Boolean(endDate) && endDate < startDate; const hasTraining = !tool.trainingRequired || user.trainings.includes(tool.requiredTrainingId!); const canReserve = !!startDate && !!endDate && !exceedsMax && !invalidRange && (hasTraining || user.role === 'admin'); const handleSubmit = () => { if (!canReserve) return; addReservation({ userId: user.id, toolId: tool.id, startDate, endDate }); setSubmitted(true); setTimeout(onClose, 1100); }; return ( {/* Top bar */} Reservierung {/* Body */} {/* Form panel */} {tool.name} } label={hasTraining ? 'Zertifiziert' : 'Einweisung fehlt'} color={hasTraining ? 'success' : 'warning'} size="small" sx={pillSx} /> } label={tool.location} size="small" variant="outlined" sx={pillSx} /> } label={`max. ${tool.maxRentalDays} Tage`} size="small" variant="outlined" sx={pillSx} /> {tool.description} setStartDate(e.target.value)} InputLabelProps={{ shrink: true }} inputProps={{ min: today }} fullWidth size="small" /> setEndDate(e.target.value)} InputLabelProps={{ shrink: true }} inputProps={{ min: startDate || today }} fullWidth size="small" error={Boolean(invalidRange) || exceedsMax} helperText={ invalidRange ? 'Enddatum liegt vor Startdatum.' : exceedsMax ? `Maximale Leihdauer: ${tool.maxRentalDays} Tage.` : duration > 0 ? `${duration} ${duration === 1 ? 'Tag' : 'Tage'} gesamt` : ' ' } /> Maximale Ausleihdauer: {tool.maxRentalDays} Tage. Bitte bringe das Werkzeug rechtzeitig und unbeschädigt zurück. {/* Sticky action bar */} {/* Calendar panel */} Verfügbarkeit } > ); } function LegendDot({ color, label, outline, }: { color?: string; label: string; outline?: boolean; }) { return ( {label} ); }