181 lines
5.2 KiB
TypeScript
181 lines
5.2 KiB
TypeScript
import { Modal, vehicleStore } from "@shared";
|
||
import {
|
||
Box,
|
||
Button,
|
||
CircularProgress,
|
||
Table,
|
||
TableBody,
|
||
TableCell,
|
||
TableContainer,
|
||
TableHead,
|
||
TableRow,
|
||
Typography,
|
||
} from "@mui/material";
|
||
import { observer } from "mobx-react-lite";
|
||
import { useEffect } from "react";
|
||
|
||
interface VehicleSessionsModalProps {
|
||
open: boolean;
|
||
vehicleId: number | null;
|
||
tailNumber?: string | null;
|
||
onClose: () => void;
|
||
}
|
||
|
||
const formatDateTime = (value?: string | null) => {
|
||
if (!value) return "-";
|
||
|
||
const date = new Date(value);
|
||
if (Number.isNaN(date.getTime())) return value;
|
||
|
||
return new Intl.DateTimeFormat("ru-RU", {
|
||
day: "2-digit",
|
||
month: "2-digit",
|
||
year: "numeric",
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
second: "2-digit",
|
||
hour12: false,
|
||
}).format(date);
|
||
};
|
||
|
||
const formatDuration = (durationSeconds?: number | null) => {
|
||
if (durationSeconds == null || !Number.isFinite(durationSeconds) || durationSeconds < 0) {
|
||
return "-";
|
||
}
|
||
|
||
const hours = Math.floor(durationSeconds / 3600);
|
||
const minutes = Math.floor((durationSeconds % 3600) / 60);
|
||
const seconds = durationSeconds % 60;
|
||
|
||
return [hours, minutes, seconds]
|
||
.map((part) => String(part).padStart(2, "0"))
|
||
.join(":");
|
||
};
|
||
|
||
export const VehicleSessionsModal = observer(
|
||
({ open, vehicleId, tailNumber, onClose }: VehicleSessionsModalProps) => {
|
||
const {
|
||
vehicleSessions,
|
||
vehicleSessionsLoading,
|
||
vehicleSessionsError,
|
||
getVehicleSessions,
|
||
} = vehicleStore;
|
||
|
||
useEffect(() => {
|
||
if (!open || vehicleId == null) return;
|
||
|
||
getVehicleSessions(vehicleId).catch(() => undefined);
|
||
}, [open, vehicleId, getVehicleSessions]);
|
||
|
||
const title =
|
||
tailNumber && tailNumber !== ""
|
||
? `Сессии ТО: ${tailNumber}`
|
||
: "Сессии ТО";
|
||
|
||
return (
|
||
<Modal
|
||
open={open}
|
||
onClose={onClose}
|
||
sx={{ width: "min(1080px, 95vw)", p: 3 }}
|
||
>
|
||
<div className="flex flex-col gap-4 max-h-[82vh]">
|
||
<Typography variant="h6" component="h2">
|
||
{title}
|
||
</Typography>
|
||
|
||
<Box sx={{ minHeight: 220 }}>
|
||
{vehicleSessionsLoading && (
|
||
<Box
|
||
sx={{
|
||
minHeight: 220,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}
|
||
>
|
||
<CircularProgress size={24} />
|
||
</Box>
|
||
)}
|
||
|
||
{!vehicleSessionsLoading && vehicleSessionsError && (
|
||
<Box
|
||
sx={{
|
||
minHeight: 220,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
color: "error.main",
|
||
}}
|
||
>
|
||
{vehicleSessionsError}
|
||
</Box>
|
||
)}
|
||
|
||
{!vehicleSessionsLoading &&
|
||
!vehicleSessionsError &&
|
||
vehicleSessions &&
|
||
vehicleSessions.length === 0 && (
|
||
<Box
|
||
sx={{
|
||
minHeight: 220,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
color: "text.secondary",
|
||
}}
|
||
>
|
||
По этому транспорту нет сессий ТО.
|
||
</Box>
|
||
)}
|
||
|
||
{!vehicleSessionsLoading &&
|
||
!vehicleSessionsError &&
|
||
vehicleSessions &&
|
||
vehicleSessions.length > 0 && (
|
||
<TableContainer
|
||
sx={{ maxHeight: "60vh", border: 1, borderColor: "divider" }}
|
||
>
|
||
<Table stickyHeader size="small">
|
||
<TableHead>
|
||
<TableRow>
|
||
<TableCell>ID</TableCell>
|
||
<TableCell>Начало</TableCell>
|
||
<TableCell>Окончание</TableCell>
|
||
<TableCell align="right">Длительность</TableCell>
|
||
</TableRow>
|
||
</TableHead>
|
||
<TableBody>
|
||
{vehicleSessions.map((session) => (
|
||
<TableRow key={session.id} hover>
|
||
<TableCell>{session.id}</TableCell>
|
||
<TableCell>
|
||
{formatDateTime(session.started_at)}
|
||
</TableCell>
|
||
<TableCell>
|
||
{session.ended_at ? formatDateTime(session.ended_at) : "В процессе"}
|
||
</TableCell>
|
||
<TableCell align="right">
|
||
{session.ended_at ? formatDuration(session.duration_seconds) : "—"}
|
||
</TableCell>
|
||
</TableRow>
|
||
))}
|
||
</TableBody>
|
||
</Table>
|
||
</TableContainer>
|
||
)}
|
||
</Box>
|
||
|
||
<Button
|
||
variant="outlined"
|
||
color="inherit"
|
||
onClick={onClose}
|
||
fullWidth
|
||
>
|
||
Закрыть
|
||
</Button>
|
||
</div>
|
||
</Modal>
|
||
);
|
||
},
|
||
);
|