diff --git a/public/Emblem.svg b/public/Emblem.svg new file mode 100644 index 0000000..724f0ee --- /dev/null +++ b/public/Emblem.svg @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/GET.png b/public/GET.png new file mode 100644 index 0000000..d224f3f Binary files /dev/null and b/public/GET.png differ diff --git a/public/SightIcon.png b/public/SightIcon.png new file mode 100644 index 0000000..02e8adb Binary files /dev/null and b/public/SightIcon.png differ diff --git a/public/favicon_ship.png b/public/favicon_ship.png new file mode 100644 index 0000000..3487dd6 Binary files /dev/null and b/public/favicon_ship.png differ diff --git a/src/app/router/index.tsx b/src/app/router/index.tsx index 8f77f1b..da3525a 100644 --- a/src/app/router/index.tsx +++ b/src/app/router/index.tsx @@ -38,6 +38,7 @@ import { StationEditPage, RouteCreatePage, RoutePreview, + RouteEditPage, } from "@pages"; import { authStore, createSightStore, editSightStore } from "@shared"; import { Layout } from "@widgets"; @@ -98,6 +99,7 @@ const router = createBrowserRouter([ ), }, + { path: "route-preview/:id", element: }, { path: "/", element: ( @@ -141,7 +143,7 @@ const router = createBrowserRouter([ // Route { path: "route", element: }, { path: "route/create", element: }, - { path: "route-preview/:id", element: }, + { path: "route/:id/edit", element: }, // User { path: "user", element: }, diff --git a/src/pages/MapPage/index.tsx b/src/pages/MapPage/index.tsx index 995f75c..005e0d9 100644 --- a/src/pages/MapPage/index.tsx +++ b/src/pages/MapPage/index.tsx @@ -35,6 +35,7 @@ import { Pencil, Save, Plus, + Loader2, } from "lucide-react"; import { toast } from "react-toastify"; import { singleClick, doubleClick } from "ol/events/condition"; @@ -1061,7 +1062,7 @@ const MapControls: React.FC = ({ }, { mode: "drawing-sight", - title: "Место", + title: "Достопримечательность", longTitle: "Добавить достопримечательность", icon: , action: () => mapService.startDrawingSight(), @@ -1109,6 +1110,7 @@ const MapSightbar: React.FC = ({ }) => { const [activeSection, setActiveSection] = useState("layers"); const navigate = useNavigate(); + const [isLoading, setIsLoading] = useState(false); const toggleSection = (id: string) => setActiveSection(activeSection === id ? null : id); @@ -1124,7 +1126,9 @@ const MapSightbar: React.FC = ({ (id: string | number | undefined, recourse: string) => { if ( mapService && - window.confirm("Вы действительно хотите удалить этот объект?") + window.confirm( + "Вы действительно хотите удалить этот объект? Утерянные данные не могут быть восстановлены." + ) ) mapService.deleteFeature(id, recourse); }, @@ -1144,8 +1148,16 @@ const MapSightbar: React.FC = ({ [navigate] ); - const handleSave = useCallback(() => { - mapStore.handleSave(mapService?.getAllFeaturesAsGeoJSON() || ""); + const handleSave = useCallback(async () => { + const geoJSON = mapService?.getAllFeaturesAsGeoJSON(); + if (geoJSON) { + setIsLoading(true); + await mapStore.handleSave(geoJSON); + toast.success("Данные успешно сохранены"); + } else { + toast.error("Ошибка при сохранении данных"); + } + setIsLoading(false); }, [mapService]); const stations = mapFeatures.filter( @@ -1470,10 +1482,15 @@ const MapSightbar: React.FC = ({ ); diff --git a/src/pages/MapPage/mapStore.ts b/src/pages/MapPage/mapStore.ts index 6ac6063..4e7bf28 100644 --- a/src/pages/MapPage/mapStore.ts +++ b/src/pages/MapPage/mapStore.ts @@ -34,13 +34,27 @@ class MapStore { getRoutes = async () => { const routes = await languageInstance("ru").get("/route"); - const mappedRoutes = routes.data.map((route: any) => ({ - id: route.id, - route_number: route.route_number, - path: route.path, - })); + const routedIds = routes.data.map((route: any) => route.id); - this.routes = mappedRoutes; + const mappedRoutes: ApiRoute[] = []; + for (const routeId of routedIds) { + const responseSoloRoute = await languageInstance("ru").get( + `/route/${routeId}` + ); + const route = responseSoloRoute.data; + + const mappedRoute = { + id: route.id, + route_number: route.route_number, + path: route.path, + }; + + mappedRoutes.push(mappedRoute); + } + + this.routes = mappedRoutes.sort((a, b) => + a.route_number.localeCompare(b.route_number) + ); }; getStations = async () => { diff --git a/src/pages/Media/MediaListPage/index.tsx b/src/pages/Media/MediaListPage/index.tsx index 53a4137..5bc131c 100644 --- a/src/pages/Media/MediaListPage/index.tsx +++ b/src/pages/Media/MediaListPage/index.tsx @@ -43,7 +43,7 @@ export const MediaListPage = observer(() => { { field: "actions", headerName: "Действия", - flex: 1, + width: 200, align: "center", headerAlign: "center", @@ -75,8 +75,6 @@ export const MediaListPage = observer(() => { return ( <> - -

Медиа

diff --git a/src/pages/Route/RouteEditPage/index.tsx b/src/pages/Route/RouteEditPage/index.tsx new file mode 100644 index 0000000..887e010 --- /dev/null +++ b/src/pages/Route/RouteEditPage/index.tsx @@ -0,0 +1,225 @@ +import { + Button, + Paper, + TextField, + Select, + MenuItem, + FormControl, + InputLabel, + Typography, + Box, +} from "@mui/material"; + +import { observer } from "mobx-react-lite"; +import { ArrowLeft, Save } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; + +import { carrierStore } from "../../../shared/store/CarrierStore"; +import { articlesStore } from "../../../shared/store/ArticlesStore"; +import { routeStore } from "../../../shared/store/RouteStore"; +import { toast } from "react-toastify"; + +export const RouteEditPage = observer(() => { + const navigate = useNavigate(); + const { id } = useParams(); + const { editRouteData } = routeStore; + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const fetchData = async () => { + const response = await routeStore.getRoute(Number(id)); + routeStore.setEditRouteData(response); + carrierStore.getCarriers(); + articlesStore.getArticleList(); + }; + fetchData(); + }, [id]); + + const handleSave = async () => { + setIsLoading(true); + await routeStore.editRoute(Number(id)); + toast.success("Маршрут успешно сохранен"); + setIsLoading(false); + }; + + return ( + +
+ +
+ + Редактировать маршрут + + + + Выберите перевозчика + + + + routeStore.setEditRouteData({ + route_number: e.target.value, + }) + } + /> + p.join(" ")).join("\n") || ""} + onChange={(e) => + routeStore.setEditRouteData({ + path: e.target.value + .split("\n") + .map((line) => line.split(" ").map(Number)), + }) + } + /> + + routeStore.setEditRouteData({ + route_sys_number: e.target.value, + }) + } + /> + + Обращение губернатора + + + + Прямой/обратный маршрут + + + + routeStore.setEditRouteData({ + scale_min: Number(e.target.value), + }) + } + /> + + routeStore.setEditRouteData({ + scale_max: Number(e.target.value), + }) + } + /> + + routeStore.setEditRouteData({ + rotate: Number(e.target.value), + }) + } + /> + + routeStore.setEditRouteData({ + center_latitude: Number(e.target.value), + }) + } + /> + + routeStore.setEditRouteData({ + center_longitude: Number(e.target.value), + }) + } + /> + +
+ +
+
+ ); +}); diff --git a/src/pages/Route/RouteListPage/index.tsx b/src/pages/Route/RouteListPage/index.tsx index d3bd299..dab1c0a 100644 --- a/src/pages/Route/RouteListPage/index.tsx +++ b/src/pages/Route/RouteListPage/index.tsx @@ -2,7 +2,7 @@ import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; import { languageStore, routeStore } from "@shared"; import { useEffect, useState } from "react"; import { observer } from "mobx-react-lite"; -import { Eye, Trash2 } from "lucide-react"; +import { Eye, Map, Pencil, Trash2 } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { CreateButton, DeleteModal, LanguageSwitcher } from "@widgets"; @@ -49,15 +49,21 @@ export const RouteListPage = observer(() => { { field: "actions", headerName: "Действия", - width: 140, + width: 250, align: "center", headerAlign: "center", renderCell: (params: GridRenderCellParams) => { return (
- + + {/* */}