diff --git a/src/app/router/index.tsx b/src/app/router/index.tsx index 5167e3f..f850029 100644 --- a/src/app/router/index.tsx +++ b/src/app/router/index.tsx @@ -39,6 +39,7 @@ import { RouteCreatePage, RoutePreview, RouteEditPage, + ArticlePreviewPage, } from "@pages"; import { authStore, createSightStore, editSightStore } from "@shared"; import { Layout } from "@widgets"; @@ -170,7 +171,7 @@ const router = createBrowserRouter([ // { path: "vehicle/:id/edit", element: }, // Article { path: "article", element: }, - // { path: "article/:id", element: }, + { path: "article/:id", element: }, // { path: "media/create", element: }, ], }, diff --git a/src/pages/Article/ArticleCreatePage/index.tsx b/src/pages/Article/ArticleCreatePage/index.tsx index 89dde3c..36c54b6 100644 --- a/src/pages/Article/ArticleCreatePage/index.tsx +++ b/src/pages/Article/ArticleCreatePage/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { useNavigate } from "react-router-dom"; import { ArrowLeft } from "lucide-react"; import { LanguageSwitcher } from "@widgets"; diff --git a/src/pages/Article/ArticleEditPage/index.tsx b/src/pages/Article/ArticleEditPage/index.tsx index 2ab1749..b33980d 100644 --- a/src/pages/Article/ArticleEditPage/index.tsx +++ b/src/pages/Article/ArticleEditPage/index.tsx @@ -1,14 +1,14 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { ArrowLeft } from "lucide-react"; import { LanguageSwitcher } from "@widgets"; -import { articlesStore, languageStore } from "@shared"; +import { articlesStore } from "@shared"; import { observer } from "mobx-react-lite"; const ArticleEditPage: React.FC = observer(() => { const navigate = useNavigate(); const { id } = useParams(); - const { language } = languageStore; + const { articleData, getArticle } = articlesStore; useEffect(() => { diff --git a/src/pages/Article/ArticlePreviewPage/PreviewLeftWidget.tsx b/src/pages/Article/ArticlePreviewPage/PreviewLeftWidget.tsx new file mode 100644 index 0000000..a2c9147 --- /dev/null +++ b/src/pages/Article/ArticlePreviewPage/PreviewLeftWidget.tsx @@ -0,0 +1,85 @@ +import { Paper, Box, Typography } from "@mui/material"; +import { MediaViewer, ReactMarkdownComponent } from "@widgets"; +import { articlesStore, languageStore } from "@shared"; +import { observer } from "mobx-react-lite"; + +export const PreviewLeftWidget = observer(() => { + const { articleMedia, articleData } = articlesStore; + const { language } = languageStore; + + return ( + + + {articleMedia && } + + + + {articleData?.[language]?.heading || "Название информации"} + + + {articleData?.[language]?.body && ( + + + + )} + + ); +}); diff --git a/src/pages/Article/ArticlePreviewPage/PreviewRightWidget.tsx b/src/pages/Article/ArticlePreviewPage/PreviewRightWidget.tsx new file mode 100644 index 0000000..33403e0 --- /dev/null +++ b/src/pages/Article/ArticlePreviewPage/PreviewRightWidget.tsx @@ -0,0 +1,139 @@ +import { Paper, Box, Typography } from "@mui/material"; +import { MediaViewer, ReactMarkdownComponent } from "@widgets"; +import { articlesStore, languageStore } from "@shared"; +import { observer } from "mobx-react-lite"; +import { ImagePlus } from "lucide-react"; + +export const PreviewRightWidget = observer(() => { + const { articleData, articleMedia } = articlesStore; + const { language } = languageStore; + const article = articleData?.[language]; + if (!article) return null; + + return ( + + + {articleMedia ? ( + + + + ) : ( + + + + )} + + + + {article.heading || "Выберите статью"} + + + + + {article.body ? ( + + ) : ( + + Предпросмотр статьи появится здесь + + )} + + + {/* @ts-ignore */} + {articleData?.right && articleData?.right.length > 1 && ( + + {/* @ts-ignore */} + {articleData.right.map((a, idx) => ( + + ))} + + )} + + + ); +}); diff --git a/src/pages/Article/ArticlePreviewPage/index.tsx b/src/pages/Article/ArticlePreviewPage/index.tsx new file mode 100644 index 0000000..bce26ed --- /dev/null +++ b/src/pages/Article/ArticlePreviewPage/index.tsx @@ -0,0 +1,57 @@ +import { useNavigate, useParams } from "react-router-dom"; +import { useEffect } from "react"; +import { Box } from "@mui/material"; +import { PreviewLeftWidget } from "./PreviewLeftWidget"; +import { PreviewRightWidget } from "./PreviewRightWidget"; +import { articlesStore, languageStore } from "@shared"; +import { ArrowLeft } from "lucide-react"; + +export const ArticlePreviewPage = () => { + const navigate = useNavigate(); + const { id } = useParams(); + const { getArticle, getArticleMedia, getArticlePreview } = articlesStore; + const { language } = languageStore; + + useEffect(() => { + const fetchData = async () => { + if (id) { + await getArticle(Number(id), language); + await getArticleMedia(Number(id)); + await getArticlePreview(Number(id)); + } + }; + fetchData(); + }, [id, language]); + + return ( + <> +
+ +
+ + + + + + + + + + + + ); +}; diff --git a/src/pages/Article/index.ts b/src/pages/Article/index.ts index b9a8432..5354497 100644 --- a/src/pages/Article/index.ts +++ b/src/pages/Article/index.ts @@ -1 +1,2 @@ export * from "./ArticleListPage"; +export * from "./ArticlePreviewPage"; diff --git a/src/pages/Carrier/CarrierCreatePage/index.tsx b/src/pages/Carrier/CarrierCreatePage/index.tsx index 93ae0c1..3f0d758 100644 --- a/src/pages/Carrier/CarrierCreatePage/index.tsx +++ b/src/pages/Carrier/CarrierCreatePage/index.tsx @@ -12,9 +12,9 @@ import { ArrowLeft, Save } from "lucide-react"; import { Loader2 } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; -import { carrierStore, cityStore, mediaStore, languageStore } from "@shared"; +import { carrierStore, cityStore, mediaStore } from "@shared"; import { useState, useEffect } from "react"; -import { MediaViewer, ImageUploadCard, LanguageSwitcher } from "@widgets"; +import { ImageUploadCard, LanguageSwitcher } from "@widgets"; import { SelectMediaDialog, UploadMediaDialog, @@ -23,7 +23,7 @@ import { export const CarrierCreatePage = observer(() => { const navigate = useNavigate(); - const { language } = languageStore; + const [fullName, setFullName] = useState(""); const [shortName, setShortName] = useState(""); const [cityId, setCityId] = useState(null); diff --git a/src/pages/Carrier/CarrierEditPage/index.tsx b/src/pages/Carrier/CarrierEditPage/index.tsx index eb40c2e..d7c200b 100644 --- a/src/pages/Carrier/CarrierEditPage/index.tsx +++ b/src/pages/Carrier/CarrierEditPage/index.tsx @@ -12,9 +12,9 @@ import { ArrowLeft, Save } from "lucide-react"; import { Loader2 } from "lucide-react"; import { useNavigate, useParams } from "react-router-dom"; import { toast } from "react-toastify"; -import { carrierStore, cityStore, mediaStore } from "@shared"; +import { carrierStore, cityStore, mediaStore, languageStore } from "@shared"; import { useState, useEffect } from "react"; -import { MediaViewer, ImageUploadCard } from "@widgets"; +import { ImageUploadCard, LanguageSwitcher } from "@widgets"; import { SelectMediaDialog, UploadMediaDialog, @@ -24,8 +24,8 @@ import { export const CarrierEditPage = observer(() => { const navigate = useNavigate(); const { id } = useParams(); - const { carrier, getCarrier, setEditCarrierData, editCarrierData } = - carrierStore; + const { getCarrier, setEditCarrierData, editCarrierData } = carrierStore; + const { language } = languageStore; const [isLoading, setIsLoading] = useState(false); const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false); @@ -41,16 +41,34 @@ export const CarrierEditPage = observer(() => { await cityStore.getCities("ru"); await cityStore.getCities("en"); await cityStore.getCities("zh"); - await getCarrier(Number(id)); + const carrierData = await getCarrier(Number(id)); - setEditCarrierData( - carrier?.[Number(id)]?.full_name as string, - carrier?.[Number(id)]?.short_name as string, - - carrier?.[Number(id)]?.city_id as number, - carrier?.[Number(id)]?.slogan as string, - carrier?.[Number(id)]?.logo as string - ); + if (carrierData) { + setEditCarrierData( + carrierData.ru?.full_name || "", + carrierData.ru?.short_name || "", + carrierData.ru?.city_id || 0, + carrierData.ru?.slogan || "", + carrierData.ru?.logo || "", + "ru" + ); + setEditCarrierData( + carrierData.en?.full_name || "", + carrierData.en?.short_name || "", + carrierData.en?.city_id || 0, + carrierData.en?.slogan || "", + carrierData.en?.logo || "", + "en" + ); + setEditCarrierData( + carrierData.zh?.full_name || "", + carrierData.zh?.short_name || "", + carrierData.zh?.city_id || 0, + carrierData.zh?.slogan || "", + carrierData.zh?.logo || "", + "zh" + ); + } mediaStore.getMedia(); })(); @@ -76,12 +94,12 @@ export const CarrierEditPage = observer(() => { media_type: number; }) => { setEditCarrierData( - editCarrierData.full_name, - editCarrierData.short_name, - + editCarrierData[language].full_name, + editCarrierData[language].short_name, editCarrierData.city_id, - editCarrierData.slogan, - media.id + editCarrierData[language].slogan, + media.id, + language ); }; @@ -91,6 +109,7 @@ export const CarrierEditPage = observer(() => { return ( +
+
+

{editCarrierData.ru.full_name}

+
Город @@ -110,15 +132,16 @@ export const CarrierEditPage = observer(() => { required onChange={(e) => setEditCarrierData( - editCarrierData.full_name, - editCarrierData.short_name, + editCarrierData[language].full_name, + editCarrierData[language].short_name, Number(e.target.value), - editCarrierData.slogan, - editCarrierData.logo + editCarrierData[language].slogan, + editCarrierData.logo, + language ) } > - {cityStore.cities.ru.data?.map((city) => ( + {cityStore.cities[language].data?.map((city) => ( {city.name} @@ -129,16 +152,16 @@ export const CarrierEditPage = observer(() => { setEditCarrierData( e.target.value, - editCarrierData.short_name, - + editCarrierData[language].short_name, editCarrierData.city_id, - editCarrierData.slogan, - editCarrierData.logo + editCarrierData[language].slogan, + editCarrierData.logo, + language ) } /> @@ -146,16 +169,16 @@ export const CarrierEditPage = observer(() => { setEditCarrierData( - editCarrierData.full_name, + editCarrierData[language].full_name, e.target.value, - editCarrierData.city_id, - editCarrierData.slogan, - editCarrierData.logo + editCarrierData[language].slogan, + editCarrierData.logo, + language ) } /> @@ -163,15 +186,15 @@ export const CarrierEditPage = observer(() => { setEditCarrierData( - editCarrierData.full_name, - editCarrierData.short_name, - + editCarrierData[language].full_name, + editCarrierData[language].short_name, editCarrierData.city_id, e.target.value, - editCarrierData.logo + editCarrierData.logo, + language ) } /> @@ -187,12 +210,12 @@ export const CarrierEditPage = observer(() => { }} onDeleteImageClick={() => { setEditCarrierData( - editCarrierData.full_name, - editCarrierData.short_name, - + editCarrierData[language].full_name, + editCarrierData[language].short_name, editCarrierData.city_id, - editCarrierData.slogan, - "" + editCarrierData[language].slogan, + "", + language ); setActiveMenuType(null); }} @@ -214,8 +237,8 @@ export const CarrierEditPage = observer(() => { onClick={handleEdit} disabled={ isLoading || - !editCarrierData.full_name || - !editCarrierData.short_name || + !editCarrierData[language].full_name || + !editCarrierData[language].short_name || !editCarrierData.city_id || !editCarrierData.logo } diff --git a/src/pages/Carrier/CarrierListPage/index.tsx b/src/pages/Carrier/CarrierListPage/index.tsx index e2a93ad..7494e17 100644 --- a/src/pages/Carrier/CarrierListPage/index.tsx +++ b/src/pages/Carrier/CarrierListPage/index.tsx @@ -1,10 +1,10 @@ import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; -import { carrierStore } from "@shared"; +import { carrierStore, languageStore } from "@shared"; import { useEffect, useState } from "react"; import { observer } from "mobx-react-lite"; -import { Eye, Pencil, Trash2, Minus } from "lucide-react"; +import { Pencil, Trash2, Minus } from "lucide-react"; import { useNavigate } from "react-router-dom"; -import { CreateButton, DeleteModal } from "@widgets"; +import { CreateButton, DeleteModal, LanguageSwitcher } from "@widgets"; export const CarrierListPage = observer(() => { const { carriers, getCarriers, deleteCarrier } = carrierStore; @@ -13,10 +13,13 @@ export const CarrierListPage = observer(() => { const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false); const [rowId, setRowId] = useState(null); const [ids, setIds] = useState([]); + const { language } = languageStore; useEffect(() => { - getCarriers(); - }, []); + (async () => { + await getCarriers(language); + })(); + }, [language]); const columns: GridColDef[] = [ { @@ -96,7 +99,7 @@ export const CarrierListPage = observer(() => { }, ]; - const rows = carriers.data?.map((carrier) => ({ + const rows = carriers[language].data?.map((carrier) => ({ id: carrier.id, full_name: carrier.full_name, short_name: carrier.short_name, @@ -105,6 +108,7 @@ export const CarrierListPage = observer(() => { return ( <> +

Перевозчики

@@ -155,7 +159,7 @@ export const CarrierListPage = observer(() => { open={isBulkDeleteModalOpen} onDelete={async () => { await Promise.all(ids.map((id) => deleteCarrier(id))); - getCarriers(); + await getCarriers(language); setIsBulkDeleteModalOpen(false); setIds([]); }} diff --git a/src/pages/Carrier/CarrierPreviewPage/index.tsx b/src/pages/Carrier/CarrierPreviewPage/index.tsx deleted file mode 100644 index 82f98e9..0000000 --- a/src/pages/Carrier/CarrierPreviewPage/index.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { Paper } from "@mui/material"; -import { carrierStore, mediaStore } from "@shared"; -import { MediaViewer } from "@widgets"; -import { observer } from "mobx-react-lite"; -import { ArrowLeft } from "lucide-react"; -import { useEffect } from "react"; -import { useNavigate, useParams } from "react-router-dom"; - -export const CarrierPreviewPage = observer(() => { - const { id } = useParams(); - const { getCarrier, carrier, setEditCarrierData } = carrierStore; - const { oneMedia, getOneMedia } = mediaStore; - const navigate = useNavigate(); - - useEffect(() => { - (async () => { - const carrierResponse = await getCarrier(Number(id)); - setEditCarrierData( - carrierResponse?.full_name as string, - carrierResponse?.short_name as string, - carrierResponse?.city as string, - carrierResponse?.city_id as number, - // carrierResponse?.main_color as string, - // carrierResponse?.left_color as string, - // carrierResponse?.right_color as string, - carrierResponse?.slogan as string, - carrierResponse?.logo as string - ); - console.log(carrierResponse); - await getOneMedia(carrierResponse?.logo as string); - })(); - }, [id]); - - return ( - - {carrier && ( - <> -
- -
-
-
-

Полное имя

-

{carrier[Number(id)]?.full_name}

-
- -
-

Полное имя

-

{carrier[Number(id)]?.full_name}

-
-
-

Город

-

{carrier[Number(id)]?.city}

-
- {/*
-

Основной цвет

-
- {carrier[Number(id)]?.main_color} -
-
-
-

Цвет левого виджета

-
- {carrier[Number(id)]?.left_color} -
-
-
-

Цвет правого виджета

-
- {carrier[Number(id)]?.right_color} -
-
*/} -
-

Краткое имя

-

{carrier[Number(id)]?.short_name}

-
- {oneMedia && ( -
-

Логотип

- - -
- )} -
- - )} -
- ); -}); diff --git a/src/pages/Carrier/index.ts b/src/pages/Carrier/index.ts index f5ffa27..7bcfe37 100644 --- a/src/pages/Carrier/index.ts +++ b/src/pages/Carrier/index.ts @@ -1,4 +1,4 @@ export * from "./CarrierListPage"; -export * from "./CarrierPreviewPage"; + export * from "./CarrierCreatePage"; export * from "./CarrierEditPage"; diff --git a/src/pages/City/CityCreatePage/index.tsx b/src/pages/City/CityCreatePage/index.tsx index 45ccac5..64de3d7 100644 --- a/src/pages/City/CityCreatePage/index.tsx +++ b/src/pages/City/CityCreatePage/index.tsx @@ -6,16 +6,15 @@ import { MenuItem, FormControl, InputLabel, - Box, } from "@mui/material"; import { observer } from "mobx-react-lite"; -import { ArrowLeft, Save, ImagePlus, Minus } from "lucide-react"; +import { ArrowLeft, Save, Minus } from "lucide-react"; import { Loader2 } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import { cityStore, countryStore, languageStore, mediaStore } from "@shared"; import { useState, useEffect } from "react"; -import { LanguageSwitcher, MediaViewer, ImageUploadCard } from "@widgets"; +import { LanguageSwitcher, ImageUploadCard } from "@widgets"; import { SelectMediaDialog, UploadMediaDialog, @@ -91,8 +90,8 @@ export const CityCreatePage = observer(() => {
-
-

Создание города

+
+

{createCityData.ru.name}

{ setEditCityData(zhData.name, zhData.country_code, zhData.arms, "zh"); await getOneMedia(ruData.arms as string); - await getCountries(language); + await getCountries("ru"); await getMedia(); } })(); @@ -108,7 +107,7 @@ export const CityEditPage = observer(() => {
-
+

{editCityData.ru.name}

{ ); }} > - {countryStore.countries[language].data.map((country) => ( + {countryStore.countries.ru.data.map((country) => ( {country.name} diff --git a/src/pages/City/CityListPage/index.tsx b/src/pages/City/CityListPage/index.tsx index e6704b6..5fda47a 100644 --- a/src/pages/City/CityListPage/index.tsx +++ b/src/pages/City/CityListPage/index.tsx @@ -1,5 +1,5 @@ import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; -import { languageStore, cityStore, CashedCities } from "@shared"; +import { languageStore, cityStore } from "@shared"; import { useEffect, useState } from "react"; import { observer } from "mobx-react-lite"; import { Eye, Pencil, Trash2, Minus } from "lucide-react"; diff --git a/src/pages/Country/CountryCreatePage/index.tsx b/src/pages/Country/CountryCreatePage/index.tsx index d063bb3..4601465 100644 --- a/src/pages/Country/CountryCreatePage/index.tsx +++ b/src/pages/Country/CountryCreatePage/index.tsx @@ -41,8 +41,8 @@ export const CountryCreatePage = observer(() => {
-
-

Создание страны

+
+

{createCountryData.ru.name}

{
-
-

{editCountryData.ru.name}

+
+

{editCountryData.ru.name}

{ const [value, setValue] = useState(0); const { sight, getSightInfo, needLeaveAgree } = editSightStore; const { getArticles } = articlesStore; - const { language } = languageStore; + const { id } = useParams(); const { getRuCities } = cityStore; diff --git a/src/pages/MapPage/index.tsx b/src/pages/MapPage/index.tsx index 3df771b..0640bed 100644 --- a/src/pages/MapPage/index.tsx +++ b/src/pages/MapPage/index.tsx @@ -3,7 +3,6 @@ import React, { useRef, useState, useCallback, - ReactNode, useMemo, } from "react"; import { useNavigate } from "react-router-dom"; @@ -33,7 +32,6 @@ import { ArrowRightLeft, Landmark, Pencil, - Lasso, InfoIcon, X, Loader2, @@ -86,9 +84,7 @@ class MapStore { for (const id of routesIds) { const route = await languageInstance("ru").get(`/route/${id}`); this.routes.push({ - id: route.data.id, - route_number: route.data.route_number, - path: route.data.path, + ...route.data, }); } @@ -100,21 +96,14 @@ class MapStore { getStations = async () => { const stations = await languageInstance("ru").get("/station"); this.stations = stations.data.map((station: any) => ({ - id: station.id, - name: station.name, - latitude: station.latitude, - longitude: station.longitude, + ...station, })); }; getSights = async () => { const sights = await languageInstance("ru").get("/sight"); this.sights = sights.data.map((sight: any) => ({ - id: sight.id, - name: sight.name, - description: sight.description, - latitude: sight.latitude, - longitude: sight.longitude, + ...sight, })); }; @@ -194,9 +183,25 @@ class MapStore { throw new Error(`Unknown feature type for update: ${featureType}`); } + let oldData; + if (featureType === "route") { + oldData = this.routes.find((f) => f.id === numericId); + } else if (featureType === "station") { + oldData = this.stations.find((f) => f.id === numericId); + } else if (featureType === "sight") { + oldData = this.sights.find((f) => f.id === numericId); + } + + console.log(oldData); + console.log(data); + const response = await languageInstance("ru").patch( `/${featureType}/${numericId}`, - data + { + ...oldData, + latitude: data.latitude, + longitude: data.longitude, + } ); if (featureType === "route") { @@ -277,6 +282,7 @@ class MapService { private tooltipElement: HTMLElement; private tooltipOverlay: Overlay | null; private mode: string | null; + // @ts-ignore private currentDrawingType: "Point" | "LineString" | null; private currentDrawingFeatureType: FeatureType | null; private currentInteraction: Draw | null; @@ -620,7 +626,7 @@ class MapService { filter: (_: FeatureLike, l: Layer | null) => l === this.vectorLayer, }); - + // @ts-ignore this.modifyInteraction.on("modifystart", (event) => { const geoJSONFormat = new GeoJSON(); if (!this.map) return; @@ -959,7 +965,8 @@ class MapService { feature.set("name", `${baseName} ${maxNumber + 1}`); await this.saveNewFeature(feature); - this.stopDrawing(); + // Убираем вызов stopDrawing, чтобы режим рисования оставался активным + // this.stopDrawing(); }); this.map.addInteraction(this.currentInteraction); @@ -988,7 +995,8 @@ class MapService { this.currentInteraction = null; this.currentDrawingType = null; this.currentDrawingFeatureType = null; - this.activateEditMode(); + // Убираем автоматическое переключение в режим редактирования + // this.activateEditMode(); } public finishDrawing(): void { @@ -1007,6 +1015,10 @@ class MapService { this.currentInteraction instanceof Draw ) { this.finishDrawing(); + // После завершения рисования маршрута, останавливаем режим рисования + if (this.currentDrawingType === "LineString") { + this.stopDrawing(); + } } } @@ -1098,6 +1110,7 @@ class MapService { if (this.mode === "edit") { this.selectInteraction.getFeatures().clear(); this.selectInteraction.getFeatures().push(feature); + // @ts-ignore const selectEvent = new SelectEvent("select", [feature], []); this.selectInteraction.dispatchEvent(selectEvent); } @@ -1332,7 +1345,8 @@ class MapService { feature.set("name", newName); this.updateFeaturesInReact(); - this.selectFeature(newFeatureId); + // Убираем автоматический выбор созданного объекта + // this.selectFeature(newFeatureId); } catch (error) { console.error("Failed to save new feature:", error); toast.error("Не удалось сохранить объект."); @@ -1366,6 +1380,7 @@ interface ControlItem { const MapControls: React.FC = ({ mapService, activeMode, + // @ts-ignore isLassoActive, isUnselectDisabled, }) => { @@ -1473,11 +1488,13 @@ const MapSightbar: React.FC = ({ }, [mapFeatures, searchQuery]); const handleFeatureClick = useCallback( + // @ts-ignore (id) => mapService?.selectFeature(id), [mapService] ); const handleDeleteFeature = useCallback( + // @ts-ignore (id, recourse) => { if ( mapService && @@ -1490,6 +1507,7 @@ const MapSightbar: React.FC = ({ ); const handleCheckboxChange = useCallback( + // @ts-ignore (id) => { if (id === undefined) return; const newSet = new Set(selectedIds); @@ -1512,7 +1530,10 @@ const MapSightbar: React.FC = ({ } }, [mapService, selectedIds, setSelectedIds]); + // @ts-ignore + const handleEditFeature = useCallback( + // @ts-ignore (featureType, fullId) => { if (!featureType || !fullId) return; const numericId = String(fullId).split("-")[1]; @@ -1522,8 +1543,11 @@ const MapSightbar: React.FC = ({ ); const sortFeatures = ( + // @ts-ignore features, + // @ts-ignore currentSelectedIds, + // @ts-ignore currentSelectedFeature ) => { const selectedId = currentSelectedFeature?.getId(); diff --git a/src/pages/Route/RouteCreatePage/index.tsx b/src/pages/Route/RouteCreatePage/index.tsx index 6d8c599..9f4ce7a 100644 --- a/src/pages/Route/RouteCreatePage/index.tsx +++ b/src/pages/Route/RouteCreatePage/index.tsx @@ -6,7 +6,7 @@ import { MenuItem, FormControl, InputLabel, - Typography, + // Typography, Box, } from "@mui/material"; import { LanguageSwitcher } from "@widgets"; @@ -18,6 +18,7 @@ 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 } from "@shared"; export const RouteCreatePage = observer(() => { const navigate = useNavigate(); @@ -33,11 +34,11 @@ export const RouteCreatePage = observer(() => { const [centerLat, setCenterLat] = useState(""); const [centerLng, setCenterLng] = useState(""); const [isLoading, setIsLoading] = useState(false); - + const { language } = languageStore; useEffect(() => { - carrierStore.getCarriers(); + carrierStore.getCarriers(language); articlesStore.getArticleList(); - }, []); + }, [language]); const handleCreateRoute = async () => { try { @@ -65,8 +66,9 @@ export const RouteCreatePage = observer(() => { // Собираем объект маршрута const newRoute: Partial = { carrier: - carrierStore.carriers.data.find((c: any) => c.id === carrier_id) - ?.full_name || "", + 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, @@ -112,16 +114,20 @@ export const RouteCreatePage = observer(() => { value={carrier} label="Выберите перевозчика" onChange={(e) => setCarrier(e.target.value as string)} - disabled={carrierStore.carriers.data.length === 0} + disabled={ + carrierStore.carriers[ + language as keyof typeof carrierStore.carriers + ].data?.length === 0 + } > Не выбрано - {carrierStore.carriers.data.map( - (c: (typeof carrierStore.carriers.data)[number]) => ( - - {c.full_name} - - ) - )} + {carrierStore.carriers[ + language as keyof typeof carrierStore.carriers + ].data?.map((carrier) => ( + + {carrier.full_name} + + ))} { const navigate = useNavigate(); const { id } = useParams(); const { editRouteData } = routeStore; const [isLoading, setIsLoading] = useState(false); - + const { language } = languageStore; useEffect(() => { const fetchData = async () => { const response = await routeStore.getRoute(Number(id)); routeStore.setEditRouteData(response); - carrierStore.getCarriers(); + carrierStore.getCarriers(language); articlesStore.getArticleList(); }; fetchData(); - }, [id]); + }, [id, language]); const handleSave = async () => { setIsLoading(true); @@ -67,21 +68,26 @@ export const RouteEditPage = observer(() => { routeStore.setEditRouteData({ carrier_id: Number(e.target.value), carrier: - carrierStore.carriers.data.find( - (c) => c.id === Number(e.target.value) - )?.full_name || "", + carrierStore.carriers[ + language as keyof typeof carrierStore.carriers + ].data?.find((c) => c.id === Number(e.target.value)) + ?.full_name || "", }) } - disabled={carrierStore.carriers.data.length === 0} + disabled={ + carrierStore.carriers[ + language as keyof typeof carrierStore.carriers + ].data?.length === 0 + } > Не выбрано - {carrierStore.carriers.data.map( - (c: (typeof carrierStore.carriers.data)[number]) => ( - - {c.full_name} - - ) - )} + {carrierStore.carriers[ + language as keyof typeof carrierStore.carriers + ].data?.map((carrier) => ( + + {carrier.full_name} + + ))} {
-
-

Создание станции

+
+

{name}

{ const [type, setType] = useState(""); const [carrierId, setCarrierId] = useState(null); const [isLoading, setIsLoading] = useState(false); + const { language } = languageStore; useEffect(() => { - carrierStore.getCarriers(); - }, []); + carrierStore.getCarriers(language); + }, [language]); const handleCreate = async () => { try { @@ -32,7 +38,8 @@ export const VehicleCreatePage = observer(() => { await vehicleStore.createVehicle( Number(tailNumber), Number(type), - carrierStore.carriers.data.find((c) => c.id === carrierId)?.full_name!, + carrierStore.carriers[language].data?.find((c) => c.id === carrierId) + ?.full_name as string, carrierId! ); toast.success("Транспорт успешно создан"); @@ -88,7 +95,7 @@ export const VehicleCreatePage = observer(() => { required onChange={(e) => setCarrierId(e.target.value as number)} > - {carrierStore.carriers.data.map((carrier) => ( + {carrierStore.carriers[language].data?.map((carrier) => ( {carrier.full_name} diff --git a/src/pages/Vehicle/VehicleEditPage/index.tsx b/src/pages/Vehicle/VehicleEditPage/index.tsx index ca26973..5e3858f 100644 --- a/src/pages/Vehicle/VehicleEditPage/index.tsx +++ b/src/pages/Vehicle/VehicleEditPage/index.tsx @@ -11,7 +11,12 @@ import { observer } from "mobx-react-lite"; import { ArrowLeft, Loader2, Save } from "lucide-react"; import { useNavigate, useParams } from "react-router-dom"; import { useEffect, useState } from "react"; -import { carrierStore, VEHICLE_TYPES, vehicleStore } from "@shared"; +import { + carrierStore, + languageStore, + VEHICLE_TYPES, + vehicleStore, +} from "@shared"; import { toast } from "react-toastify"; export const VehicleEditPage = observer(() => { @@ -25,11 +30,12 @@ export const VehicleEditPage = observer(() => { editVehicle, } = vehicleStore; const { getCarriers } = carrierStore; - + const { language } = languageStore; useEffect(() => { (async () => { await getVehicle(Number(id)); - await getCarriers(); + await getCarriers(language); + setEditVehicleData({ tail_number: vehicle[Number(id)]?.vehicle.tail_number, type: vehicle[Number(id)]?.vehicle.type, @@ -37,7 +43,7 @@ export const VehicleEditPage = observer(() => { carrier_id: vehicle[Number(id)]?.vehicle.carrier_id, }); })(); - }, [id]); + }, [id, language]); const [isLoading, setIsLoading] = useState(false); const handleEdit = async () => { try { @@ -108,7 +114,7 @@ export const VehicleEditPage = observer(() => { }) } > - {carrierStore.carriers.data.map((carrier) => ( + {carrierStore.carriers[language].data?.map((carrier) => ( {carrier.full_name} diff --git a/src/pages/Vehicle/VehicleListPage/index.tsx b/src/pages/Vehicle/VehicleListPage/index.tsx index 1d56929..ecfd37c 100644 --- a/src/pages/Vehicle/VehicleListPage/index.tsx +++ b/src/pages/Vehicle/VehicleListPage/index.tsx @@ -19,7 +19,7 @@ export const VehicleListPage = observer(() => { useEffect(() => { getVehicles(); - getCarriers(); + getCarriers(language); }, [language]); const columns: GridColDef[] = [ @@ -123,7 +123,7 @@ export const VehicleListPage = observer(() => { tail_number: vehicle.vehicle.tail_number, type: vehicle.vehicle.type, carrier: vehicle.vehicle.carrier, - city: carriers.data?.find( + city: carriers[language].data?.find( (carrier) => carrier.id === vehicle.vehicle.carrier_id )?.city, })); diff --git a/src/shared/config/constants.tsx b/src/shared/config/constants.tsx index 938ba7c..f8c7909 100644 --- a/src/shared/config/constants.tsx +++ b/src/shared/config/constants.tsx @@ -8,7 +8,7 @@ import { Earth, Landmark, GitBranch, - Car, + // Car, Table, Notebook, Split, diff --git a/src/shared/store/CarrierStore/index.tsx b/src/shared/store/CarrierStore/index.tsx index 982cb1c..a339811 100644 --- a/src/shared/store/CarrierStore/index.tsx +++ b/src/shared/store/CarrierStore/index.tsx @@ -1,4 +1,10 @@ -import { authInstance, cityStore, languageStore } from "@shared"; +import { + authInstance, + cityStore, + languageStore, + languageInstance, + Language, +} from "@shared"; import { makeAutoObservable, runInAction } from "mobx"; export type Carrier = { @@ -14,17 +20,40 @@ export type Carrier = { // right_color: string; }; -type Carriers = { +type CarrierData = { data: Carrier[]; loaded: boolean; }; -type CashedCarrier = Record; +type Carriers = { + ru: CarrierData; + en: CarrierData; + zh: CarrierData; +}; + +type CashedCarrier = Record< + number, + { + ru: Carrier | null; + en: Carrier | null; + zh: Carrier | null; + } +>; class CarrierStore { carriers: Carriers = { - data: [], - loaded: false, + ru: { + data: [], + loaded: false, + }, + en: { + data: [], + loaded: false, + }, + zh: { + data: [], + loaded: false, + }, }; carrier: CashedCarrier = {}; @@ -32,14 +61,14 @@ class CarrierStore { makeAutoObservable(this); } - getCarriers = async () => { - if (this.carriers.loaded) return; + getCarriers = async (language: Language) => { + if (this.carriers[language as keyof Carriers].loaded) return; const response = await authInstance.get("/carrier"); runInAction(() => { - this.carriers.data = response.data; - this.carriers.loaded = true; + this.carriers[language as keyof Carriers].data = response.data; + this.carriers[language as keyof Carriers].loaded = true; }); }; @@ -47,116 +76,163 @@ class CarrierStore { await authInstance.delete(`/carrier/${id}`); runInAction(() => { - this.carriers.data = this.carriers.data.filter( - (carrier) => carrier.id !== id - ); + for (const language of ["ru", "en", "zh"] as const) { + this.carriers[language].data = this.carriers[language].data.filter( + (carrier: Carrier) => carrier.id !== id + ); + } delete this.carrier[id]; }); }; getCarrier = async (id: number) => { - if (this.carrier[id]) return; - const response = await authInstance.get(`/carrier/${id}`); + if (this.carrier[id]?.ru && this.carrier[id]?.en && this.carrier[id]?.zh) + return; + + const ruResponse = await languageInstance("ru").get(`/carrier/${id}`); + const enResponse = await languageInstance("en").get(`/carrier/${id}`); + const zhResponse = await languageInstance("zh").get(`/carrier/${id}`); runInAction(() => { if (!this.carrier[id]) { this.carrier[id] = { - id: 0, - short_name: "", - full_name: "", - slogan: "", - city: "", - city_id: 0, - logo: "", - // main_color: "", - // left_color: "", - // right_color: "", + ru: null, + en: null, + zh: null, }; } - this.carrier[id] = response.data; + this.carrier[id].ru = ruResponse.data; + this.carrier[id].en = enResponse.data; + this.carrier[id].zh = zhResponse.data; }); - return response.data; + return this.carrier[id]; }; createCarrier = async ( fullName: string, shortName: string, - cityId: number, - // main_color: string, - // left_color: string, - // right_color: string, slogan: string, logoId: string ) => { - const response = await authInstance.post("/carrier", { + const { language } = languageStore; + const cityName = + cityStore.cities[language].data.find((city) => city.id === cityId) + ?.name || ""; + + const response = await languageInstance(language).post("/carrier", { full_name: fullName, short_name: shortName, - city: "", + city: cityName, city_id: cityId, - // main_color, - // left_color, - // right_color, slogan, logo: logoId, }); + + const carrierId = response.data.id; + + // Create translations for other languages + for (const lang of ["ru", "en", "zh"].filter((l) => l !== language)) { + await languageInstance(lang as Language).patch(`/carrier/${carrierId}`, { + full_name: fullName, + short_name: shortName, + city: cityName, + city_id: cityId, + slogan, + logo: logoId, + }); + } + runInAction(() => { - this.carriers.data.push(response.data); + for (const language of ["ru", "en", "zh"] as const) { + this.carriers[language].data.push(response.data); + } }); }; editCarrierData = { - full_name: "", - short_name: "", + ru: { + full_name: "", + short_name: "", + // main_color: "", + // left_color: "", + // right_color: "", + slogan: "", + }, + en: { + full_name: "", + short_name: "", + + // main_color: "", + // left_color: "", + // right_color: "", + slogan: "", + }, city_id: 0, - // main_color: "", - // left_color: "", - // right_color: "", - slogan: "", logo: "", + zh: { + full_name: "", + short_name: "", + + // main_color: "", + // left_color: "", + // right_color: "", + slogan: "", + }, }; setEditCarrierData = ( fullName: string, shortName: string, - cityId: number, // main_color: string, // left_color: string, // right_color: string, slogan: string, - logoId: string + logoId: string, + language: Language ) => { - this.editCarrierData = { + this.editCarrierData.city_id = cityId; + this.editCarrierData.logo = logoId; + this.editCarrierData[language] = { full_name: fullName, short_name: shortName, - - city_id: cityId, // main_color: main_color, // left_color: left_color, // right_color: right_color, slogan: slogan, - logo: logoId, }; }; editCarrier = async (id: number) => { - const { language } = languageStore; - const response = await authInstance.patch(`/carrier/${id}`, { - ...this.editCarrierData, - city: cityStore.cities[language].data.find( + const cityName = + cityStore.cities[languageStore.language].data.find( (city) => city.id === this.editCarrierData.city_id - )?.name, - }); + )?.name || ""; - runInAction(() => { - this.carriers.data = this.carriers.data.map((carrier) => - carrier.id === id ? { ...carrier, ...response.data } : carrier + for (const language of ["ru", "en", "zh"] as const) { + const response = await languageInstance(language).patch( + `/carrier/${id}`, + { + ...this.editCarrierData[language], + city: cityName, + logo: this.editCarrierData.logo, + } ); - this.carrier[id] = response.data; - }); + runInAction(() => { + if (this.carrier[id]) { + this.carrier[id][language] = response.data; + } + for (const language of ["ru", "en", "zh"] as const) { + this.carriers[language].data = this.carriers[language].data.map( + (carrier: Carrier) => + carrier.id === id ? { ...carrier, ...response.data } : carrier + ); + } + }); + } }; } diff --git a/src/widgets/SightTabs/CreateInformationTab/index.tsx b/src/widgets/SightTabs/CreateInformationTab/index.tsx index 2fac2bb..950ec02 100644 --- a/src/widgets/SightTabs/CreateInformationTab/index.tsx +++ b/src/widgets/SightTabs/CreateInformationTab/index.tsx @@ -175,9 +175,10 @@ export const CreateInformationTab = observer( /> city.id === sight.city_id) ?? null + ruCities.data.find((city) => city.id === sight.city_id) ?? + null } getOptionLabel={(option) => option.name} onChange={(_, value) => { diff --git a/src/widgets/SightTabs/CreateLeftTab/index.tsx b/src/widgets/SightTabs/CreateLeftTab/index.tsx index d67874c..ebf7b57 100644 --- a/src/widgets/SightTabs/CreateLeftTab/index.tsx +++ b/src/widgets/SightTabs/CreateLeftTab/index.tsx @@ -19,14 +19,7 @@ import { MediaViewer, DeleteModal, } from "@widgets"; -import { - Trash2, - ImagePlus, - Unlink, - MousePointer, - Plus, - Save, -} from "lucide-react"; +import { Trash2, ImagePlus, Unlink, Plus, Save, Search } from "lucide-react"; import { useState, useCallback } from "react"; import { observer } from "mobx-react-lite"; import { toast } from "react-toastify"; @@ -160,7 +153,7 @@ export const CreateLeftTab = observer( variant="contained" color="primary" size="small" - startIcon={} + startIcon={} onClick={() => setIsSelectArticleDialogOpen(true)} > Выбрать статью diff --git a/src/widgets/SightTabs/CreateRightTab/index.tsx b/src/widgets/SightTabs/CreateRightTab/index.tsx index bf4edde..f32f666 100644 --- a/src/widgets/SightTabs/CreateRightTab/index.tsx +++ b/src/widgets/SightTabs/CreateRightTab/index.tsx @@ -551,6 +551,7 @@ export const CreateRightTab = observer( media={ sight[language].right[activeArticleIndex].media[0] } + fullWidth /> ) : ( diff --git a/src/widgets/SightTabs/LeftWidgetTab/index.tsx b/src/widgets/SightTabs/LeftWidgetTab/index.tsx index f623a76..e336a6c 100644 --- a/src/widgets/SightTabs/LeftWidgetTab/index.tsx +++ b/src/widgets/SightTabs/LeftWidgetTab/index.tsx @@ -18,14 +18,7 @@ import { MediaViewer, DeleteModal, } from "@widgets"; -import { - Trash2, - ImagePlus, - Unlink, - Plus, - MousePointer, - Save, -} from "lucide-react"; +import { Trash2, ImagePlus, Unlink, Plus, Save, Search } from "lucide-react"; import { useState, useCallback } from "react"; import { observer } from "mobx-react-lite"; import { toast } from "react-toastify"; @@ -175,7 +168,7 @@ export const LeftWidgetTab = observer( variant="contained" color="primary" size="small" - startIcon={} + startIcon={} onClick={() => setIsSelectArticleDialogOpen(true)} > Выбрать статью diff --git a/src/widgets/SightTabs/RightWidgetTab/index.tsx b/src/widgets/SightTabs/RightWidgetTab/index.tsx index 96a93b6..2a360fd 100644 --- a/src/widgets/SightTabs/RightWidgetTab/index.tsx +++ b/src/widgets/SightTabs/RightWidgetTab/index.tsx @@ -493,6 +493,7 @@ export const RightWidgetTab = observer( media={ sight[language].right[activeArticleIndex].media[0] } + fullWidth /> ) : ( diff --git a/src/widgets/SightsTable/index.tsx b/src/widgets/SightsTable/index.tsx index 16e05ca..796c471 100644 --- a/src/widgets/SightsTable/index.tsx +++ b/src/widgets/SightsTable/index.tsx @@ -67,7 +67,7 @@ export const SightsTable = observer(() => { - {rows(sights, cities[language])?.map((row) => ( + {rows(sights, cities[language]?.data ?? [])?.map((row) => (