import { Button, Paper, TextField, Select, MenuItem, FormControl, InputLabel, Typography, Box, Dialog, DialogTitle, DialogContent, DialogActions, } from "@mui/material"; import { MediaViewer, VideoPreviewCard } from "@widgets"; import { observer } from "mobx-react-lite"; import { ArrowLeft, Loader2, Save, Plus } from "lucide-react"; import { useEffect, useState, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import { carrierStore } from "../../../shared/store/CarrierStore"; import { articlesStore } from "../../../shared/store/ArticlesStore"; import { Route, routeStore } from "../../../shared/store/RouteStore"; import { languageStore, ArticleSelectOrCreateDialog, SelectMediaDialog, selectedCityStore, UploadMediaDialog, } from "@shared"; export const RouteCreatePage = observer(() => { const navigate = useNavigate(); const [carrier, setCarrier] = useState(""); const [routeNumber, setRouteNumber] = useState(""); const [routeCoords, setRouteCoords] = useState(""); const [govRouteNumber, setGovRouteNumber] = useState(""); const [governorAppeal, setGovernorAppeal] = useState(""); const [direction, setDirection] = useState("backward"); const [scaleMin, setScaleMin] = useState("10"); const [scaleMax, setScaleMax] = useState("100"); const [routeName, setRouteName] = useState(""); const [turn, setTurn] = useState(""); const [centerLat, setCenterLat] = useState(""); const [centerLng, setCenterLng] = useState(""); const [videoPreview, setVideoPreview] = useState(""); const [isLoading, setIsLoading] = useState(false); const [isSelectArticleDialogOpen, setIsSelectArticleDialogOpen] = useState(false); const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false); const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false); const [isUploadVideoDialogOpen, setIsUploadVideoDialogOpen] = useState(false); const [fileToUpload, setFileToUpload] = useState(null); const { language } = languageStore; useEffect(() => { carrierStore.getCarriers(language); articlesStore.getArticleList(); }, [language]); const filteredCarriers = useMemo(() => { const carriers = carrierStore.carriers[language as keyof typeof carrierStore.carriers] .data || []; if (!selectedCityStore.selectedCityId) { return carriers; } return carriers.filter( (carrier: any) => carrier.city_id === selectedCityStore.selectedCityId ); }, [carrierStore.carriers, language, selectedCityStore.selectedCityId]); const validateCoordinates = (value: string) => { try { const lines = value.trim().split("\n"); const coordinates = lines.map((line) => { const [lat, lon] = line .trim() .split(/[\s,]+/) .map(Number); return [lat, lon]; }); if (coordinates.length === 0) { return "Введите хотя бы одну пару координат"; } if ( !coordinates.every( (point) => Array.isArray(point) && point.length === 2 ) ) { return "Каждая строка должна содержать две координаты"; } if ( !coordinates.every((point) => point.every((coord) => !isNaN(coord) && typeof coord === "number") ) ) { return "Координаты должны быть числами"; } return true; } catch { return "Неверный формат координат"; } }; const handleArticleSelect = (articleId: number) => { setGovernorAppeal(articleId.toString()); setIsSelectArticleDialogOpen(false); articlesStore.getArticleList(); }; const handleVideoSelect = (media: { id: string; filename: string; media_name?: string; media_type: number; }) => { setVideoPreview(media.id); setIsSelectVideoDialogOpen(false); }; const handleVideoFileSelect = (file?: File) => { if (file) { setFileToUpload(file); setIsUploadVideoDialogOpen(true); } else { setIsSelectVideoDialogOpen(true); } }; const handleVideoUpload = (media: { id: string; filename: string; media_name?: string; media_type: number; }) => { setVideoPreview(media.id); setIsUploadVideoDialogOpen(false); setFileToUpload(null); }; const handleVideoPreviewClick = () => { setIsVideoPreviewOpen(true); }; const handleCreateRoute = async () => { try { setIsLoading(true); if (!routeName.trim()) { toast.error("Заполните название маршрута"); setIsLoading(false); return; } if (!carrier) { toast.error("Выберите перевозчика"); setIsLoading(false); return; } if (!routeNumber.trim()) { toast.error("Заполните номер маршрута"); setIsLoading(false); return; } if (!govRouteNumber.trim()) { toast.error("Заполните номер маршрута в Говорящем Городе"); setIsLoading(false); return; } if (!governorAppeal) { toast.error("Выберите статью для обращения к пассажирам"); setIsLoading(false); return; } const validationResult = validateCoordinates(routeCoords); if (validationResult !== true) { toast.error(validationResult); setIsLoading(false); return; } const scale_min = scaleMin ? Number(scaleMin) : null; const scale_max = scaleMax ? Number(scaleMax) : null; if ( scale_min === 0 || scale_max === 0 || scale_min === null || scale_max === null ) { toast.error("Масштабы не могут быть равны 0"); setIsLoading(false); return; } if ( scale_min !== null && scale_max !== null && scale_max !== undefined && scale_min > scale_max ) { toast.error("Максимальный масштаб не может быть меньше минимального"); setIsLoading(false); return; } const carrier_id = Number(carrier); const governor_appeal = Number(governorAppeal); const rotate = turn ? Number(turn) : undefined; const center_latitude = centerLat ? Number(centerLat) : undefined; const center_longitude = centerLng ? Number(centerLng) : undefined; const route_direction = direction === "forward"; const path = routeCoords .trim() .split("\n") .map((line) => { const [lat, lon] = line .trim() .split(/[\s,]+/) .map(Number); return [lat, lon]; }); const newRoute: Partial = { carrier: carrierStore.carriers[ language as keyof typeof carrierStore.carriers ].data?.find((c: any) => c.id === carrier_id)?.full_name || "", carrier_id, route_number: routeNumber, route_sys_number: govRouteNumber, governor_appeal, route_name: routeName, route_direction, scale_min: scale_min !== null ? scale_min : 0, scale_max: scale_max !== null ? scale_max : 0, rotate, center_latitude, center_longitude, path, video_preview: videoPreview && videoPreview !== "" ? videoPreview : undefined, }; await routeStore.createRoute(newRoute); toast.success("Маршрут успешно создан"); navigate(-1); } catch (error) { console.error(error); toast.error("Произошла ошибка при создании маршрута"); } finally { setIsLoading(false); } }; const selectedArticle = articlesStore.articleList.ru.data.find( (article) => article.id === Number(governorAppeal) ); return (
setRouteName(e.target.value)} /> Выберите перевозчика setRouteNumber(e.target.value)} /> { const newValue = e.target.value; setRouteCoords(newValue); }} onKeyDown={(e) => { if (e.key === "Enter") { const lines = routeCoords.split("\n"); const lastLine = lines[lines.length - 1]; if (lastLine && lastLine.trim()) { e.preventDefault(); const newValue = routeCoords + "\n"; setRouteCoords(newValue); } } }} error={validateCoordinates(routeCoords) !== true} helperText={ typeof validateCoordinates(routeCoords) === "string" ? validateCoordinates(routeCoords) : "Формат: широта долгота" } placeholder="55.7558 37.6173 55.7539 37.6208" sx={{ "& .MuiInputBase-root": { maxHeight: "500px", overflow: "auto", }, "& .MuiInputBase-input": { fontFamily: "monospace", fontSize: "0.8rem", lineHeight: "1.2", padding: "8px 12px", }, "& .MuiFormHelperText-root": { fontSize: "0.75rem", marginTop: "2px", }, }} /> setGovRouteNumber(e.target.value)} /> Обращение к пассажирам { setVideoPreview(""); }} onSelectVideoClick={handleVideoFileSelect} className="w-full" /> Прямой/обратный маршрут { let value = e.target.value; if (Number(value) > 297) { value = "297"; } if (Number(value) < 10) { value = "10"; } setScaleMin(value); if (value && scaleMax && Number(value) > Number(scaleMax)) { setScaleMax(value); } }} error={ scaleMin !== "" && scaleMax !== "" && Number(scaleMin) > Number(scaleMax) } required helperText={ scaleMin !== "" && scaleMax !== "" && Number(scaleMin) > Number(scaleMax) ? "Минимальный масштаб не может быть больше максимального" : "" } /> { if (Number(e.target.value) > 300) { e.target.value = "300"; } const value = e.target.value; setScaleMax(value); }} /> setTurn(e.target.value)} /> setCenterLat(e.target.value)} /> setCenterLng(e.target.value)} />
setIsSelectArticleDialogOpen(false)} onSelectArticle={handleArticleSelect} /> setIsSelectVideoDialogOpen(false)} onSelectMedia={handleVideoSelect} mediaType={2} /> {videoPreview && videoPreview !== "" && ( setIsVideoPreviewOpen(false)} maxWidth="md" fullWidth > Предпросмотр видео )} { setIsUploadVideoDialogOpen(false); setFileToUpload(null); }} hardcodeType="video_preview" contextObjectName={routeName || "Маршрут"} contextType="sight" initialFile={fileToUpload || undefined} afterUpload={handleVideoUpload} />
); });