feat: Add carriers translation on 3 languages
				
					
				
			This commit is contained in:
		| @@ -39,6 +39,7 @@ import { | |||||||
|   RouteCreatePage, |   RouteCreatePage, | ||||||
|   RoutePreview, |   RoutePreview, | ||||||
|   RouteEditPage, |   RouteEditPage, | ||||||
|  |   ArticlePreviewPage, | ||||||
| } from "@pages"; | } from "@pages"; | ||||||
| import { authStore, createSightStore, editSightStore } from "@shared"; | import { authStore, createSightStore, editSightStore } from "@shared"; | ||||||
| import { Layout } from "@widgets"; | import { Layout } from "@widgets"; | ||||||
| @@ -170,7 +171,7 @@ const router = createBrowserRouter([ | |||||||
|       // { path: "vehicle/:id/edit", element: <VehicleEditPage /> }, |       // { path: "vehicle/:id/edit", element: <VehicleEditPage /> }, | ||||||
|       // Article |       // Article | ||||||
|       { path: "article", element: <ArticleListPage /> }, |       { path: "article", element: <ArticleListPage /> }, | ||||||
|       // { path: "article/:id", element: <ArticlePreviewPage /> }, |       { path: "article/:id", element: <ArticlePreviewPage /> }, | ||||||
|       // { path: "media/create", element: <CreateMediaPage /> }, |       // { path: "media/create", element: <CreateMediaPage /> }, | ||||||
|     ], |     ], | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React, { useState } from "react"; | import React from "react"; | ||||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||||
| import { ArrowLeft } from "lucide-react"; | import { ArrowLeft } from "lucide-react"; | ||||||
| import { LanguageSwitcher } from "@widgets"; | import { LanguageSwitcher } from "@widgets"; | ||||||
|   | |||||||
| @@ -1,14 +1,14 @@ | |||||||
| import React, { useEffect, useState } from "react"; | import React, { useEffect } from "react"; | ||||||
| import { useNavigate, useParams } from "react-router-dom"; | import { useNavigate, useParams } from "react-router-dom"; | ||||||
| import { ArrowLeft } from "lucide-react"; | import { ArrowLeft } from "lucide-react"; | ||||||
| import { LanguageSwitcher } from "@widgets"; | import { LanguageSwitcher } from "@widgets"; | ||||||
| import { articlesStore, languageStore } from "@shared"; | import { articlesStore } from "@shared"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
|  |  | ||||||
| const ArticleEditPage: React.FC = observer(() => { | const ArticleEditPage: React.FC = observer(() => { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|   const { language } = languageStore; |  | ||||||
|   const { articleData, getArticle } = articlesStore; |   const { articleData, getArticle } = articlesStore; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|   | |||||||
							
								
								
									
										85
									
								
								src/pages/Article/ArticlePreviewPage/PreviewLeftWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/pages/Article/ArticlePreviewPage/PreviewLeftWidget.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -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 ( | ||||||
|  |     <Paper | ||||||
|  |       elevation={3} | ||||||
|  |       sx={{ | ||||||
|  |         width: "100%", | ||||||
|  |         minWidth: 320, | ||||||
|  |         background: | ||||||
|  |           "#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)", | ||||||
|  |         overflowY: "auto", | ||||||
|  |         display: "flex", | ||||||
|  |         flexDirection: "column", | ||||||
|  |         borderRadius: "10px", | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       <Box | ||||||
|  |         sx={{ | ||||||
|  |           overflow: "hidden", | ||||||
|  |           width: "100%", | ||||||
|  |           minHeight: 100, | ||||||
|  |           padding: "3px", | ||||||
|  |           display: "flex", | ||||||
|  |           alignItems: "center", | ||||||
|  |           justifyContent: "center", | ||||||
|  |           "& img": { | ||||||
|  |             borderTopLeftRadius: "10px", | ||||||
|  |             borderTopRightRadius: "10px", | ||||||
|  |             width: "100%", | ||||||
|  |             height: "auto", | ||||||
|  |             objectFit: "contain", | ||||||
|  |           }, | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         {articleMedia && <MediaViewer media={articleMedia} fullWidth />} | ||||||
|  |       </Box> | ||||||
|  |       <Box | ||||||
|  |         sx={{ | ||||||
|  |           background: | ||||||
|  |             "#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)", | ||||||
|  |           color: "white", | ||||||
|  |           margin: "5px 0px 5px 0px", | ||||||
|  |           display: "flex", | ||||||
|  |           flexDirection: "column", | ||||||
|  |           gap: 1, | ||||||
|  |           padding: 1, | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <Typography | ||||||
|  |           variant="h5" | ||||||
|  |           component="h2" | ||||||
|  |           sx={{ | ||||||
|  |             wordBreak: "break-word", | ||||||
|  |             fontSize: "24px", | ||||||
|  |             fontWeight: 700, | ||||||
|  |             lineHeight: "120%", | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           {articleData?.[language]?.heading || "Название информации"} | ||||||
|  |         </Typography> | ||||||
|  |       </Box> | ||||||
|  |       {articleData?.[language]?.body && ( | ||||||
|  |         <Box | ||||||
|  |           sx={{ | ||||||
|  |             padding: 1, | ||||||
|  |             maxHeight: "300px", | ||||||
|  |             overflowY: "scroll", | ||||||
|  |             background: | ||||||
|  |               "#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)", | ||||||
|  |             flexGrow: 1, | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           <ReactMarkdownComponent value={articleData?.[language]?.body} /> | ||||||
|  |         </Box> | ||||||
|  |       )} | ||||||
|  |     </Paper> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
							
								
								
									
										139
									
								
								src/pages/Article/ArticlePreviewPage/PreviewRightWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/pages/Article/ArticlePreviewPage/PreviewRightWidget.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -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 ( | ||||||
|  |     <Paper | ||||||
|  |       className="flex-1 flex flex-col max-w-[500px]" | ||||||
|  |       sx={{ | ||||||
|  |         borderRadius: "10px", | ||||||
|  |         overflow: "hidden", | ||||||
|  |       }} | ||||||
|  |       elevation={2} | ||||||
|  |     > | ||||||
|  |       <Box | ||||||
|  |         className="overflow-hidden" | ||||||
|  |         sx={{ | ||||||
|  |           width: "100%", | ||||||
|  |           background: "#877361", | ||||||
|  |           borderColor: "grey.300", | ||||||
|  |           display: "flex", | ||||||
|  |           flexDirection: "column", | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         {articleMedia ? ( | ||||||
|  |           <Box | ||||||
|  |             sx={{ | ||||||
|  |               overflow: "hidden", | ||||||
|  |               width: "100%", | ||||||
|  |               padding: "2px 2px 0px 2px", | ||||||
|  |               "& img": { | ||||||
|  |                 borderTopLeftRadius: "10px", | ||||||
|  |                 borderTopRightRadius: "10px", | ||||||
|  |                 width: "100%", | ||||||
|  |                 height: "auto", | ||||||
|  |                 objectFit: "contain", | ||||||
|  |               }, | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             <MediaViewer media={articleMedia} fullWidth /> | ||||||
|  |           </Box> | ||||||
|  |         ) : ( | ||||||
|  |           <Box | ||||||
|  |             sx={{ | ||||||
|  |               width: "100%", | ||||||
|  |               height: 200, | ||||||
|  |               flexShrink: 0, | ||||||
|  |               backgroundColor: "rgba(0,0,0,0.1)", | ||||||
|  |               display: "flex", | ||||||
|  |               alignItems: "center", | ||||||
|  |               justifyContent: "center", | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             <ImagePlus size={48} color="white" /> | ||||||
|  |           </Box> | ||||||
|  |         )} | ||||||
|  |  | ||||||
|  |         <Box | ||||||
|  |           sx={{ | ||||||
|  |             p: 1, | ||||||
|  |             wordBreak: "break-word", | ||||||
|  |             fontSize: "24px", | ||||||
|  |             fontWeight: 700, | ||||||
|  |             lineHeight: "120%", | ||||||
|  |             backdropFilter: "blur(12px)", | ||||||
|  |             borderBottom: "1px solid #A89F90", | ||||||
|  |             boxShadow: "inset 4px 4px 12px 0 rgba(255,255,255,0.12)", | ||||||
|  |             background: | ||||||
|  |               "#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)", | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           <Typography variant="h6" color="white"> | ||||||
|  |             {article.heading || "Выберите статью"} | ||||||
|  |           </Typography> | ||||||
|  |         </Box> | ||||||
|  |  | ||||||
|  |         <Box | ||||||
|  |           sx={{ | ||||||
|  |             padding: 1, | ||||||
|  |             minHeight: "200px", | ||||||
|  |             maxHeight: "300px", | ||||||
|  |             overflowY: "scroll", | ||||||
|  |             background: | ||||||
|  |               "rgba(179, 165, 152, 0.4), linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.2) 100%)", | ||||||
|  |             flexGrow: 1, | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           {article.body ? ( | ||||||
|  |             <ReactMarkdownComponent value={article.body} /> | ||||||
|  |           ) : ( | ||||||
|  |             <Typography | ||||||
|  |               color="rgba(255,255,255,0.7)" | ||||||
|  |               sx={{ textAlign: "center", mt: 4 }} | ||||||
|  |             > | ||||||
|  |               Предпросмотр статьи появится здесь | ||||||
|  |             </Typography> | ||||||
|  |           )} | ||||||
|  |         </Box> | ||||||
|  |  | ||||||
|  |         {/* @ts-ignore */} | ||||||
|  |         {articleData?.right && articleData?.right.length > 1 && ( | ||||||
|  |           <Box | ||||||
|  |             sx={{ | ||||||
|  |               p: 2, | ||||||
|  |               display: "flex", | ||||||
|  |               justifyContent: "space-between", | ||||||
|  |               fontSize: "24px", | ||||||
|  |               fontWeight: 700, | ||||||
|  |               lineHeight: "120%", | ||||||
|  |               flexWrap: "wrap", | ||||||
|  |               gap: 1, | ||||||
|  |               backdropFilter: "blur(12px)", | ||||||
|  |               boxShadow: "inset 4px 4px 12px 0 rgba(255,255,255,0.12)", | ||||||
|  |               background: | ||||||
|  |                 "#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)", | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             {/* @ts-ignore */} | ||||||
|  |             {articleData.right.map((a, idx) => ( | ||||||
|  |               <button | ||||||
|  |                 key={idx} | ||||||
|  |                 className="inline-block text-left text-xs text-white" | ||||||
|  |               > | ||||||
|  |                 {a.heading} | ||||||
|  |               </button> | ||||||
|  |             ))} | ||||||
|  |           </Box> | ||||||
|  |         )} | ||||||
|  |       </Box> | ||||||
|  |     </Paper> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
							
								
								
									
										57
									
								
								src/pages/Article/ArticlePreviewPage/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/pages/Article/ArticlePreviewPage/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -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 ( | ||||||
|  |     <> | ||||||
|  |       <div className="flex items-center gap-4 mb-10"> | ||||||
|  |         <button | ||||||
|  |           className="flex items-center gap-2" | ||||||
|  |           onClick={() => navigate(-1)} | ||||||
|  |         > | ||||||
|  |           <ArrowLeft size={20} /> | ||||||
|  |           Назад | ||||||
|  |         </button> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <Box | ||||||
|  |         sx={{ | ||||||
|  |           display: "flex", | ||||||
|  |           gap: 2, | ||||||
|  |           p: 2, | ||||||
|  |           justifyContent: "center", | ||||||
|  |           margin: "0 auto", | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <Box sx={{ width: "320px" }}> | ||||||
|  |           <PreviewLeftWidget /> | ||||||
|  |         </Box> | ||||||
|  |  | ||||||
|  |         <Box sx={{ width: "500px" }}> | ||||||
|  |           <PreviewRightWidget /> | ||||||
|  |         </Box> | ||||||
|  |       </Box> | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @@ -1 +1,2 @@ | |||||||
| export * from "./ArticleListPage"; | export * from "./ArticleListPage"; | ||||||
|  | export * from "./ArticlePreviewPage"; | ||||||
|   | |||||||
| @@ -12,9 +12,9 @@ import { ArrowLeft, Save } from "lucide-react"; | |||||||
| import { Loader2 } from "lucide-react"; | import { Loader2 } from "lucide-react"; | ||||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
| import { carrierStore, cityStore, mediaStore, languageStore } from "@shared"; | import { carrierStore, cityStore, mediaStore } from "@shared"; | ||||||
| import { useState, useEffect } from "react"; | import { useState, useEffect } from "react"; | ||||||
| import { MediaViewer, ImageUploadCard, LanguageSwitcher } from "@widgets"; | import { ImageUploadCard, LanguageSwitcher } from "@widgets"; | ||||||
| import { | import { | ||||||
|   SelectMediaDialog, |   SelectMediaDialog, | ||||||
|   UploadMediaDialog, |   UploadMediaDialog, | ||||||
| @@ -23,7 +23,7 @@ import { | |||||||
|  |  | ||||||
| export const CarrierCreatePage = observer(() => { | export const CarrierCreatePage = observer(() => { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const { language } = languageStore; |  | ||||||
|   const [fullName, setFullName] = useState(""); |   const [fullName, setFullName] = useState(""); | ||||||
|   const [shortName, setShortName] = useState(""); |   const [shortName, setShortName] = useState(""); | ||||||
|   const [cityId, setCityId] = useState<number | null>(null); |   const [cityId, setCityId] = useState<number | null>(null); | ||||||
|   | |||||||
| @@ -12,9 +12,9 @@ import { ArrowLeft, Save } from "lucide-react"; | |||||||
| import { Loader2 } from "lucide-react"; | import { Loader2 } from "lucide-react"; | ||||||
| import { useNavigate, useParams } from "react-router-dom"; | import { useNavigate, useParams } from "react-router-dom"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
| import { carrierStore, cityStore, mediaStore } from "@shared"; | import { carrierStore, cityStore, mediaStore, languageStore } from "@shared"; | ||||||
| import { useState, useEffect } from "react"; | import { useState, useEffect } from "react"; | ||||||
| import { MediaViewer, ImageUploadCard } from "@widgets"; | import { ImageUploadCard, LanguageSwitcher } from "@widgets"; | ||||||
| import { | import { | ||||||
|   SelectMediaDialog, |   SelectMediaDialog, | ||||||
|   UploadMediaDialog, |   UploadMediaDialog, | ||||||
| @@ -24,8 +24,8 @@ import { | |||||||
| export const CarrierEditPage = observer(() => { | export const CarrierEditPage = observer(() => { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|   const { carrier, getCarrier, setEditCarrierData, editCarrierData } = |   const { getCarrier, setEditCarrierData, editCarrierData } = carrierStore; | ||||||
|     carrierStore; |   const { language } = languageStore; | ||||||
|  |  | ||||||
|   const [isLoading, setIsLoading] = useState(false); |   const [isLoading, setIsLoading] = useState(false); | ||||||
|   const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false); |   const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false); | ||||||
| @@ -41,16 +41,34 @@ export const CarrierEditPage = observer(() => { | |||||||
|       await cityStore.getCities("ru"); |       await cityStore.getCities("ru"); | ||||||
|       await cityStore.getCities("en"); |       await cityStore.getCities("en"); | ||||||
|       await cityStore.getCities("zh"); |       await cityStore.getCities("zh"); | ||||||
|       await getCarrier(Number(id)); |       const carrierData = await getCarrier(Number(id)); | ||||||
|  |  | ||||||
|  |       if (carrierData) { | ||||||
|         setEditCarrierData( |         setEditCarrierData( | ||||||
|         carrier?.[Number(id)]?.full_name as string, |           carrierData.ru?.full_name || "", | ||||||
|         carrier?.[Number(id)]?.short_name as string, |           carrierData.ru?.short_name || "", | ||||||
|  |           carrierData.ru?.city_id || 0, | ||||||
|         carrier?.[Number(id)]?.city_id as number, |           carrierData.ru?.slogan || "", | ||||||
|         carrier?.[Number(id)]?.slogan as string, |           carrierData.ru?.logo || "", | ||||||
|         carrier?.[Number(id)]?.logo as string |           "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(); |       mediaStore.getMedia(); | ||||||
|     })(); |     })(); | ||||||
| @@ -76,12 +94,12 @@ export const CarrierEditPage = observer(() => { | |||||||
|     media_type: number; |     media_type: number; | ||||||
|   }) => { |   }) => { | ||||||
|     setEditCarrierData( |     setEditCarrierData( | ||||||
|       editCarrierData.full_name, |       editCarrierData[language].full_name, | ||||||
|       editCarrierData.short_name, |       editCarrierData[language].short_name, | ||||||
|  |  | ||||||
|       editCarrierData.city_id, |       editCarrierData.city_id, | ||||||
|       editCarrierData.slogan, |       editCarrierData[language].slogan, | ||||||
|       media.id |       media.id, | ||||||
|  |       language | ||||||
|     ); |     ); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -91,6 +109,7 @@ export const CarrierEditPage = observer(() => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Paper className="w-full h-full p-3 flex flex-col gap-10"> |     <Paper className="w-full h-full p-3 flex flex-col gap-10"> | ||||||
|  |       <LanguageSwitcher /> | ||||||
|       <div className="flex items-center gap-4"> |       <div className="flex items-center gap-4"> | ||||||
|         <button |         <button | ||||||
|           className="flex items-center gap-2" |           className="flex items-center gap-2" | ||||||
| @@ -101,6 +120,9 @@ export const CarrierEditPage = observer(() => { | |||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|  |       <div className="flex gap-10 items-center mb-5 max-w-[80%] self-start"> | ||||||
|  |         <h1 className="text-3xl break-words">{editCarrierData.ru.full_name}</h1> | ||||||
|  |       </div> | ||||||
|       <div className="flex flex-col gap-10 w-full items-end"> |       <div className="flex flex-col gap-10 w-full items-end"> | ||||||
|         <FormControl fullWidth> |         <FormControl fullWidth> | ||||||
|           <InputLabel>Город</InputLabel> |           <InputLabel>Город</InputLabel> | ||||||
| @@ -110,15 +132,16 @@ export const CarrierEditPage = observer(() => { | |||||||
|             required |             required | ||||||
|             onChange={(e) => |             onChange={(e) => | ||||||
|               setEditCarrierData( |               setEditCarrierData( | ||||||
|                 editCarrierData.full_name, |                 editCarrierData[language].full_name, | ||||||
|                 editCarrierData.short_name, |                 editCarrierData[language].short_name, | ||||||
|                 Number(e.target.value), |                 Number(e.target.value), | ||||||
|                 editCarrierData.slogan, |                 editCarrierData[language].slogan, | ||||||
|                 editCarrierData.logo |                 editCarrierData.logo, | ||||||
|  |                 language | ||||||
|               ) |               ) | ||||||
|             } |             } | ||||||
|           > |           > | ||||||
|             {cityStore.cities.ru.data?.map((city) => ( |             {cityStore.cities[language].data?.map((city) => ( | ||||||
|               <MenuItem key={city.id} value={city.id}> |               <MenuItem key={city.id} value={city.id}> | ||||||
|                 {city.name} |                 {city.name} | ||||||
|               </MenuItem> |               </MenuItem> | ||||||
| @@ -129,16 +152,16 @@ export const CarrierEditPage = observer(() => { | |||||||
|         <TextField |         <TextField | ||||||
|           fullWidth |           fullWidth | ||||||
|           label="Полное название" |           label="Полное название" | ||||||
|           value={editCarrierData.full_name} |           value={editCarrierData[language].full_name} | ||||||
|           required |           required | ||||||
|           onChange={(e) => |           onChange={(e) => | ||||||
|             setEditCarrierData( |             setEditCarrierData( | ||||||
|               e.target.value, |               e.target.value, | ||||||
|               editCarrierData.short_name, |               editCarrierData[language].short_name, | ||||||
|  |  | ||||||
|               editCarrierData.city_id, |               editCarrierData.city_id, | ||||||
|               editCarrierData.slogan, |               editCarrierData[language].slogan, | ||||||
|               editCarrierData.logo |               editCarrierData.logo, | ||||||
|  |               language | ||||||
|             ) |             ) | ||||||
|           } |           } | ||||||
|         /> |         /> | ||||||
| @@ -146,16 +169,16 @@ export const CarrierEditPage = observer(() => { | |||||||
|         <TextField |         <TextField | ||||||
|           fullWidth |           fullWidth | ||||||
|           label="Короткое название" |           label="Короткое название" | ||||||
|           value={editCarrierData.short_name} |           value={editCarrierData[language].short_name} | ||||||
|           required |           required | ||||||
|           onChange={(e) => |           onChange={(e) => | ||||||
|             setEditCarrierData( |             setEditCarrierData( | ||||||
|               editCarrierData.full_name, |               editCarrierData[language].full_name, | ||||||
|               e.target.value, |               e.target.value, | ||||||
|  |  | ||||||
|               editCarrierData.city_id, |               editCarrierData.city_id, | ||||||
|               editCarrierData.slogan, |               editCarrierData[language].slogan, | ||||||
|               editCarrierData.logo |               editCarrierData.logo, | ||||||
|  |               language | ||||||
|             ) |             ) | ||||||
|           } |           } | ||||||
|         /> |         /> | ||||||
| @@ -163,15 +186,15 @@ export const CarrierEditPage = observer(() => { | |||||||
|         <TextField |         <TextField | ||||||
|           fullWidth |           fullWidth | ||||||
|           label="Слоган" |           label="Слоган" | ||||||
|           value={editCarrierData.slogan} |           value={editCarrierData[language].slogan} | ||||||
|           onChange={(e) => |           onChange={(e) => | ||||||
|             setEditCarrierData( |             setEditCarrierData( | ||||||
|               editCarrierData.full_name, |               editCarrierData[language].full_name, | ||||||
|               editCarrierData.short_name, |               editCarrierData[language].short_name, | ||||||
|  |  | ||||||
|               editCarrierData.city_id, |               editCarrierData.city_id, | ||||||
|               e.target.value, |               e.target.value, | ||||||
|               editCarrierData.logo |               editCarrierData.logo, | ||||||
|  |               language | ||||||
|             ) |             ) | ||||||
|           } |           } | ||||||
|         /> |         /> | ||||||
| @@ -187,12 +210,12 @@ export const CarrierEditPage = observer(() => { | |||||||
|             }} |             }} | ||||||
|             onDeleteImageClick={() => { |             onDeleteImageClick={() => { | ||||||
|               setEditCarrierData( |               setEditCarrierData( | ||||||
|                 editCarrierData.full_name, |                 editCarrierData[language].full_name, | ||||||
|                 editCarrierData.short_name, |                 editCarrierData[language].short_name, | ||||||
|  |  | ||||||
|                 editCarrierData.city_id, |                 editCarrierData.city_id, | ||||||
|                 editCarrierData.slogan, |                 editCarrierData[language].slogan, | ||||||
|                 "" |                 "", | ||||||
|  |                 language | ||||||
|               ); |               ); | ||||||
|               setActiveMenuType(null); |               setActiveMenuType(null); | ||||||
|             }} |             }} | ||||||
| @@ -214,8 +237,8 @@ export const CarrierEditPage = observer(() => { | |||||||
|           onClick={handleEdit} |           onClick={handleEdit} | ||||||
|           disabled={ |           disabled={ | ||||||
|             isLoading || |             isLoading || | ||||||
|             !editCarrierData.full_name || |             !editCarrierData[language].full_name || | ||||||
|             !editCarrierData.short_name || |             !editCarrierData[language].short_name || | ||||||
|             !editCarrierData.city_id || |             !editCarrierData.city_id || | ||||||
|             !editCarrierData.logo |             !editCarrierData.logo | ||||||
|           } |           } | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; | import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; | ||||||
| import { carrierStore } from "@shared"; | import { carrierStore, languageStore } from "@shared"; | ||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| import { observer } from "mobx-react-lite"; | 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 { useNavigate } from "react-router-dom"; | ||||||
| import { CreateButton, DeleteModal } from "@widgets"; | import { CreateButton, DeleteModal, LanguageSwitcher } from "@widgets"; | ||||||
|  |  | ||||||
| export const CarrierListPage = observer(() => { | export const CarrierListPage = observer(() => { | ||||||
|   const { carriers, getCarriers, deleteCarrier } = carrierStore; |   const { carriers, getCarriers, deleteCarrier } = carrierStore; | ||||||
| @@ -13,10 +13,13 @@ export const CarrierListPage = observer(() => { | |||||||
|   const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false); |   const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false); | ||||||
|   const [rowId, setRowId] = useState<number | null>(null); |   const [rowId, setRowId] = useState<number | null>(null); | ||||||
|   const [ids, setIds] = useState<number[]>([]); |   const [ids, setIds] = useState<number[]>([]); | ||||||
|  |   const { language } = languageStore; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     getCarriers(); |     (async () => { | ||||||
|   }, []); |       await getCarriers(language); | ||||||
|  |     })(); | ||||||
|  |   }, [language]); | ||||||
|  |  | ||||||
|   const columns: GridColDef[] = [ |   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, |     id: carrier.id, | ||||||
|     full_name: carrier.full_name, |     full_name: carrier.full_name, | ||||||
|     short_name: carrier.short_name, |     short_name: carrier.short_name, | ||||||
| @@ -105,6 +108,7 @@ export const CarrierListPage = observer(() => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|  |       <LanguageSwitcher /> | ||||||
|       <div className="w-full"> |       <div className="w-full"> | ||||||
|         <div className="flex justify-between items-center mb-10"> |         <div className="flex justify-between items-center mb-10"> | ||||||
|           <h1 className="text-2xl">Перевозчики</h1> |           <h1 className="text-2xl">Перевозчики</h1> | ||||||
| @@ -155,7 +159,7 @@ export const CarrierListPage = observer(() => { | |||||||
|         open={isBulkDeleteModalOpen} |         open={isBulkDeleteModalOpen} | ||||||
|         onDelete={async () => { |         onDelete={async () => { | ||||||
|           await Promise.all(ids.map((id) => deleteCarrier(id))); |           await Promise.all(ids.map((id) => deleteCarrier(id))); | ||||||
|           getCarriers(); |           await getCarriers(language); | ||||||
|           setIsBulkDeleteModalOpen(false); |           setIsBulkDeleteModalOpen(false); | ||||||
|           setIds([]); |           setIds([]); | ||||||
|         }} |         }} | ||||||
|   | |||||||
| @@ -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 ( |  | ||||||
|     <Paper className="w-full h-full p-3 flex flex-col gap-10"> |  | ||||||
|       {carrier && ( |  | ||||||
|         <> |  | ||||||
|           <div className="flex justify-between items-center"> |  | ||||||
|             <button |  | ||||||
|               className="flex items-center gap-2" |  | ||||||
|               onClick={() => navigate(-1)} |  | ||||||
|             > |  | ||||||
|               <ArrowLeft size={20} /> |  | ||||||
|               Назад |  | ||||||
|             </button> |  | ||||||
|           </div> |  | ||||||
|           <div className="flex flex-col gap-10 w-full"> |  | ||||||
|             <div className="flex flex-col gap-2"> |  | ||||||
|               <h1 className="text-lg font-bold">Полное имя</h1> |  | ||||||
|               <p>{carrier[Number(id)]?.full_name}</p> |  | ||||||
|             </div> |  | ||||||
|  |  | ||||||
|             <div className="flex flex-col gap-2"> |  | ||||||
|               <h1 className="text-lg font-bold">Полное имя</h1> |  | ||||||
|               <p>{carrier[Number(id)]?.full_name}</p> |  | ||||||
|             </div> |  | ||||||
|             <div className="flex flex-col gap-2"> |  | ||||||
|               <h1 className="text-lg font-bold">Город</h1> |  | ||||||
|               <p>{carrier[Number(id)]?.city}</p> |  | ||||||
|             </div> |  | ||||||
|             {/* <div className="flex flex-col gap-2 "> |  | ||||||
|               <h1 className="text-lg font-bold">Основной цвет</h1> |  | ||||||
|               <div |  | ||||||
|                 className="w-min" |  | ||||||
|                 style={{ |  | ||||||
|                   backgroundColor: `${carrier[Number(id)]?.main_color}90`, |  | ||||||
|                 }} |  | ||||||
|               > |  | ||||||
|                 {carrier[Number(id)]?.main_color} |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|             <div className="flex flex-col gap-2"> |  | ||||||
|               <h1 className="text-lg font-bold">Цвет левого виджета</h1> |  | ||||||
|               <div |  | ||||||
|                 className="w-min" |  | ||||||
|                 style={{ |  | ||||||
|                   backgroundColor: `${carrier[Number(id)]?.left_color}90`, |  | ||||||
|                 }} |  | ||||||
|               > |  | ||||||
|                 {carrier[Number(id)]?.left_color} |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|             <div className="flex flex-col gap-2"> |  | ||||||
|               <h1 className="text-lg font-bold">Цвет правого виджета</h1> |  | ||||||
|               <div |  | ||||||
|                 className="w-min" |  | ||||||
|                 style={{ |  | ||||||
|                   backgroundColor: `${carrier[Number(id)]?.right_color}90`, |  | ||||||
|                 }} |  | ||||||
|               > |  | ||||||
|                 {carrier[Number(id)]?.right_color} |  | ||||||
|               </div> |  | ||||||
|             </div> */} |  | ||||||
|             <div className="flex flex-col gap-2"> |  | ||||||
|               <h1 className="text-lg font-bold">Краткое имя</h1> |  | ||||||
|               <p>{carrier[Number(id)]?.short_name}</p> |  | ||||||
|             </div> |  | ||||||
|             {oneMedia && ( |  | ||||||
|               <div className="flex flex-col gap-2"> |  | ||||||
|                 <h1 className="text-lg font-bold">Логотип</h1> |  | ||||||
|  |  | ||||||
|                 <MediaViewer |  | ||||||
|                   media={{ |  | ||||||
|                     id: oneMedia?.id as string, |  | ||||||
|                     media_type: oneMedia?.media_type as number, |  | ||||||
|                     filename: oneMedia?.filename, |  | ||||||
|                   }} |  | ||||||
|                 /> |  | ||||||
|               </div> |  | ||||||
|             )} |  | ||||||
|           </div> |  | ||||||
|         </> |  | ||||||
|       )} |  | ||||||
|     </Paper> |  | ||||||
|   ); |  | ||||||
| }); |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| export * from "./CarrierListPage"; | export * from "./CarrierListPage"; | ||||||
| export * from "./CarrierPreviewPage"; |  | ||||||
| export * from "./CarrierCreatePage"; | export * from "./CarrierCreatePage"; | ||||||
| export * from "./CarrierEditPage"; | export * from "./CarrierEditPage"; | ||||||
|   | |||||||
| @@ -6,16 +6,15 @@ import { | |||||||
|   MenuItem, |   MenuItem, | ||||||
|   FormControl, |   FormControl, | ||||||
|   InputLabel, |   InputLabel, | ||||||
|   Box, |  | ||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
| import { observer } from "mobx-react-lite"; | 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 { Loader2 } from "lucide-react"; | ||||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
| import { cityStore, countryStore, languageStore, mediaStore } from "@shared"; | import { cityStore, countryStore, languageStore, mediaStore } from "@shared"; | ||||||
| import { useState, useEffect } from "react"; | import { useState, useEffect } from "react"; | ||||||
| import { LanguageSwitcher, MediaViewer, ImageUploadCard } from "@widgets"; | import { LanguageSwitcher, ImageUploadCard } from "@widgets"; | ||||||
| import { | import { | ||||||
|   SelectMediaDialog, |   SelectMediaDialog, | ||||||
|   UploadMediaDialog, |   UploadMediaDialog, | ||||||
| @@ -91,8 +90,8 @@ export const CityCreatePage = observer(() => { | |||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <div className="flex flex-col gap-10 w-full items-end"> |       <div className="flex flex-col gap-10 w-full items-end"> | ||||||
|         <div className="flex gap-10 items-center mb-5 max-w-[80%]"> |         <div className="flex gap-10 items-center mb-5 max-w-[80%] self-start"> | ||||||
|           <h1 className="text-3xl break-words">Создание города</h1> |           <h1 className="text-3xl break-words">{createCityData.ru.name}</h1> | ||||||
|         </div> |         </div> | ||||||
|         <TextField |         <TextField | ||||||
|           fullWidth |           fullWidth | ||||||
|   | |||||||
| @@ -6,10 +6,9 @@ import { | |||||||
|   MenuItem, |   MenuItem, | ||||||
|   FormControl, |   FormControl, | ||||||
|   InputLabel, |   InputLabel, | ||||||
|   Box, |  | ||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
| import { ArrowLeft, Save, ImagePlus } from "lucide-react"; | import { ArrowLeft, Save } from "lucide-react"; | ||||||
| import { Loader2 } from "lucide-react"; | import { Loader2 } from "lucide-react"; | ||||||
| import { useNavigate, useParams } from "react-router-dom"; | import { useNavigate, useParams } from "react-router-dom"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
| @@ -21,7 +20,7 @@ import { | |||||||
|   CashedCities, |   CashedCities, | ||||||
| } from "@shared"; | } from "@shared"; | ||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| import { LanguageSwitcher, MediaViewer, ImageUploadCard } from "@widgets"; | import { LanguageSwitcher, ImageUploadCard } from "@widgets"; | ||||||
| import { | import { | ||||||
|   SelectMediaDialog, |   SelectMediaDialog, | ||||||
|   UploadMediaDialog, |   UploadMediaDialog, | ||||||
| @@ -70,7 +69,7 @@ export const CityEditPage = observer(() => { | |||||||
|         setEditCityData(zhData.name, zhData.country_code, zhData.arms, "zh"); |         setEditCityData(zhData.name, zhData.country_code, zhData.arms, "zh"); | ||||||
|  |  | ||||||
|         await getOneMedia(ruData.arms as string); |         await getOneMedia(ruData.arms as string); | ||||||
|         await getCountries(language); |         await getCountries("ru"); | ||||||
|         await getMedia(); |         await getMedia(); | ||||||
|       } |       } | ||||||
|     })(); |     })(); | ||||||
| @@ -108,7 +107,7 @@ export const CityEditPage = observer(() => { | |||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <div className="flex flex-col gap-10 w-full items-end"> |       <div className="flex flex-col gap-10 w-full items-end"> | ||||||
|         <div className="flex gap-10 items-center mb-5 max-w-[80%]"> |         <div className="flex gap-10 items-center mb-5 max-w-[80%] self-start    "> | ||||||
|           <h1 className="text-3xl break-words">{editCityData.ru.name}</h1> |           <h1 className="text-3xl break-words">{editCityData.ru.name}</h1> | ||||||
|         </div> |         </div> | ||||||
|         <TextField |         <TextField | ||||||
| @@ -141,7 +140,7 @@ export const CityEditPage = observer(() => { | |||||||
|               ); |               ); | ||||||
|             }} |             }} | ||||||
|           > |           > | ||||||
|             {countryStore.countries[language].data.map((country) => ( |             {countryStore.countries.ru.data.map((country) => ( | ||||||
|               <MenuItem key={country.code} value={country.code}> |               <MenuItem key={country.code} value={country.code}> | ||||||
|                 {country.name} |                 {country.name} | ||||||
|               </MenuItem> |               </MenuItem> | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; | 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 { useEffect, useState } from "react"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
| import { Eye, Pencil, Trash2, Minus } from "lucide-react"; | import { Eye, Pencil, Trash2, Minus } from "lucide-react"; | ||||||
|   | |||||||
| @@ -41,8 +41,8 @@ export const CountryCreatePage = observer(() => { | |||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <div className="flex flex-col gap-10 w-full items-end"> |       <div className="flex flex-col gap-10 w-full items-end"> | ||||||
|         <div className="flex gap-10 items-center mb-5 max-w-[80%]"> |         <div className="flex gap-10 items-center mb-5 max-w-[80%] self-start"> | ||||||
|           <h1 className="text-3xl break-words">Создание страны</h1> |           <h1 className="text-3xl break-words">{createCountryData.ru.name}</h1> | ||||||
|         </div> |         </div> | ||||||
|         <TextField |         <TextField | ||||||
|           fullWidth |           fullWidth | ||||||
|   | |||||||
| @@ -58,8 +58,8 @@ export const CountryEditPage = observer(() => { | |||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <div className="flex flex-col gap-10 w-full items-end"> |       <div className="flex flex-col gap-10 w-full items-end"> | ||||||
|         <div className="flex gap-10 items-center mb-5 max-w-[80%]"> |         <div className="flex gap-10 items-center mb-5 max-w-[80%] self-start"> | ||||||
|           <h1 className="text-3xl break-words">{editCountryData.ru.name}</h1> |           <h1 className="text-3xl break-words t">{editCountryData.ru.name}</h1> | ||||||
|         </div> |         </div> | ||||||
|         <TextField |         <TextField | ||||||
|           fullWidth |           fullWidth | ||||||
|   | |||||||
| @@ -3,12 +3,7 @@ import { InformationTab, LeaveAgree, RightWidgetTab } from "@widgets"; | |||||||
| import { LeftWidgetTab } from "@widgets"; | import { LeftWidgetTab } from "@widgets"; | ||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
| import { | import { articlesStore, cityStore, editSightStore } from "@shared"; | ||||||
|   articlesStore, |  | ||||||
|   cityStore, |  | ||||||
|   editSightStore, |  | ||||||
|   languageStore, |  | ||||||
| } from "@shared"; |  | ||||||
| import { useBlocker, useParams } from "react-router-dom"; | import { useBlocker, useParams } from "react-router-dom"; | ||||||
|  |  | ||||||
| function a11yProps(index: number) { | function a11yProps(index: number) { | ||||||
| @@ -22,7 +17,7 @@ export const EditSightPage = observer(() => { | |||||||
|   const [value, setValue] = useState(0); |   const [value, setValue] = useState(0); | ||||||
|   const { sight, getSightInfo, needLeaveAgree } = editSightStore; |   const { sight, getSightInfo, needLeaveAgree } = editSightStore; | ||||||
|   const { getArticles } = articlesStore; |   const { getArticles } = articlesStore; | ||||||
|   const { language } = languageStore; |  | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|   const { getRuCities } = cityStore; |   const { getRuCities } = cityStore; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ import React, { | |||||||
|   useRef, |   useRef, | ||||||
|   useState, |   useState, | ||||||
|   useCallback, |   useCallback, | ||||||
|   ReactNode, |  | ||||||
|   useMemo, |   useMemo, | ||||||
| } from "react"; | } from "react"; | ||||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||||
| @@ -33,7 +32,6 @@ import { | |||||||
|   ArrowRightLeft, |   ArrowRightLeft, | ||||||
|   Landmark, |   Landmark, | ||||||
|   Pencil, |   Pencil, | ||||||
|   Lasso, |  | ||||||
|   InfoIcon, |   InfoIcon, | ||||||
|   X, |   X, | ||||||
|   Loader2, |   Loader2, | ||||||
| @@ -86,9 +84,7 @@ class MapStore { | |||||||
|     for (const id of routesIds) { |     for (const id of routesIds) { | ||||||
|       const route = await languageInstance("ru").get(`/route/${id}`); |       const route = await languageInstance("ru").get(`/route/${id}`); | ||||||
|       this.routes.push({ |       this.routes.push({ | ||||||
|         id: route.data.id, |         ...route.data, | ||||||
|         route_number: route.data.route_number, |  | ||||||
|         path: route.data.path, |  | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -100,21 +96,14 @@ class MapStore { | |||||||
|   getStations = async () => { |   getStations = async () => { | ||||||
|     const stations = await languageInstance("ru").get("/station"); |     const stations = await languageInstance("ru").get("/station"); | ||||||
|     this.stations = stations.data.map((station: any) => ({ |     this.stations = stations.data.map((station: any) => ({ | ||||||
|       id: station.id, |       ...station, | ||||||
|       name: station.name, |  | ||||||
|       latitude: station.latitude, |  | ||||||
|       longitude: station.longitude, |  | ||||||
|     })); |     })); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   getSights = async () => { |   getSights = async () => { | ||||||
|     const sights = await languageInstance("ru").get("/sight"); |     const sights = await languageInstance("ru").get("/sight"); | ||||||
|     this.sights = sights.data.map((sight: any) => ({ |     this.sights = sights.data.map((sight: any) => ({ | ||||||
|       id: sight.id, |       ...sight, | ||||||
|       name: sight.name, |  | ||||||
|       description: sight.description, |  | ||||||
|       latitude: sight.latitude, |  | ||||||
|       longitude: sight.longitude, |  | ||||||
|     })); |     })); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -194,9 +183,25 @@ class MapStore { | |||||||
|       throw new Error(`Unknown feature type for update: ${featureType}`); |       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( |     const response = await languageInstance("ru").patch( | ||||||
|       `/${featureType}/${numericId}`, |       `/${featureType}/${numericId}`, | ||||||
|       data |       { | ||||||
|  |         ...oldData, | ||||||
|  |         latitude: data.latitude, | ||||||
|  |         longitude: data.longitude, | ||||||
|  |       } | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     if (featureType === "route") { |     if (featureType === "route") { | ||||||
| @@ -277,6 +282,7 @@ class MapService { | |||||||
|   private tooltipElement: HTMLElement; |   private tooltipElement: HTMLElement; | ||||||
|   private tooltipOverlay: Overlay | null; |   private tooltipOverlay: Overlay | null; | ||||||
|   private mode: string | null; |   private mode: string | null; | ||||||
|  |   // @ts-ignore | ||||||
|   private currentDrawingType: "Point" | "LineString" | null; |   private currentDrawingType: "Point" | "LineString" | null; | ||||||
|   private currentDrawingFeatureType: FeatureType | null; |   private currentDrawingFeatureType: FeatureType | null; | ||||||
|   private currentInteraction: Draw | null; |   private currentInteraction: Draw | null; | ||||||
| @@ -620,7 +626,7 @@ class MapService { | |||||||
|       filter: (_: FeatureLike, l: Layer<Source, any> | null) => |       filter: (_: FeatureLike, l: Layer<Source, any> | null) => | ||||||
|         l === this.vectorLayer, |         l === this.vectorLayer, | ||||||
|     }); |     }); | ||||||
|  |     // @ts-ignore | ||||||
|     this.modifyInteraction.on("modifystart", (event) => { |     this.modifyInteraction.on("modifystart", (event) => { | ||||||
|       const geoJSONFormat = new GeoJSON(); |       const geoJSONFormat = new GeoJSON(); | ||||||
|       if (!this.map) return; |       if (!this.map) return; | ||||||
| @@ -959,7 +965,8 @@ class MapService { | |||||||
|       feature.set("name", `${baseName} ${maxNumber + 1}`); |       feature.set("name", `${baseName} ${maxNumber + 1}`); | ||||||
|  |  | ||||||
|       await this.saveNewFeature(feature); |       await this.saveNewFeature(feature); | ||||||
|       this.stopDrawing(); |       // Убираем вызов stopDrawing, чтобы режим рисования оставался активным | ||||||
|  |       // this.stopDrawing(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     this.map.addInteraction(this.currentInteraction); |     this.map.addInteraction(this.currentInteraction); | ||||||
| @@ -988,7 +995,8 @@ class MapService { | |||||||
|     this.currentInteraction = null; |     this.currentInteraction = null; | ||||||
|     this.currentDrawingType = null; |     this.currentDrawingType = null; | ||||||
|     this.currentDrawingFeatureType = null; |     this.currentDrawingFeatureType = null; | ||||||
|     this.activateEditMode(); |     // Убираем автоматическое переключение в режим редактирования | ||||||
|  |     // this.activateEditMode(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public finishDrawing(): void { |   public finishDrawing(): void { | ||||||
| @@ -1007,6 +1015,10 @@ class MapService { | |||||||
|       this.currentInteraction instanceof Draw |       this.currentInteraction instanceof Draw | ||||||
|     ) { |     ) { | ||||||
|       this.finishDrawing(); |       this.finishDrawing(); | ||||||
|  |       // После завершения рисования маршрута, останавливаем режим рисования | ||||||
|  |       if (this.currentDrawingType === "LineString") { | ||||||
|  |         this.stopDrawing(); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1098,6 +1110,7 @@ class MapService { | |||||||
|     if (this.mode === "edit") { |     if (this.mode === "edit") { | ||||||
|       this.selectInteraction.getFeatures().clear(); |       this.selectInteraction.getFeatures().clear(); | ||||||
|       this.selectInteraction.getFeatures().push(feature); |       this.selectInteraction.getFeatures().push(feature); | ||||||
|  |       // @ts-ignore | ||||||
|       const selectEvent = new SelectEvent("select", [feature], []); |       const selectEvent = new SelectEvent("select", [feature], []); | ||||||
|       this.selectInteraction.dispatchEvent(selectEvent); |       this.selectInteraction.dispatchEvent(selectEvent); | ||||||
|     } |     } | ||||||
| @@ -1332,7 +1345,8 @@ class MapService { | |||||||
|       feature.set("name", newName); |       feature.set("name", newName); | ||||||
|  |  | ||||||
|       this.updateFeaturesInReact(); |       this.updateFeaturesInReact(); | ||||||
|       this.selectFeature(newFeatureId); |       // Убираем автоматический выбор созданного объекта | ||||||
|  |       // this.selectFeature(newFeatureId); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       console.error("Failed to save new feature:", error); |       console.error("Failed to save new feature:", error); | ||||||
|       toast.error("Не удалось сохранить объект."); |       toast.error("Не удалось сохранить объект."); | ||||||
| @@ -1366,6 +1380,7 @@ interface ControlItem { | |||||||
| const MapControls: React.FC<MapControlsProps> = ({ | const MapControls: React.FC<MapControlsProps> = ({ | ||||||
|   mapService, |   mapService, | ||||||
|   activeMode, |   activeMode, | ||||||
|  |   // @ts-ignore | ||||||
|   isLassoActive, |   isLassoActive, | ||||||
|   isUnselectDisabled, |   isUnselectDisabled, | ||||||
| }) => { | }) => { | ||||||
| @@ -1473,11 +1488,13 @@ const MapSightbar: React.FC<MapSightbarProps> = ({ | |||||||
|   }, [mapFeatures, searchQuery]); |   }, [mapFeatures, searchQuery]); | ||||||
|  |  | ||||||
|   const handleFeatureClick = useCallback( |   const handleFeatureClick = useCallback( | ||||||
|  |     // @ts-ignore | ||||||
|     (id) => mapService?.selectFeature(id), |     (id) => mapService?.selectFeature(id), | ||||||
|     [mapService] |     [mapService] | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const handleDeleteFeature = useCallback( |   const handleDeleteFeature = useCallback( | ||||||
|  |     // @ts-ignore | ||||||
|     (id, recourse) => { |     (id, recourse) => { | ||||||
|       if ( |       if ( | ||||||
|         mapService && |         mapService && | ||||||
| @@ -1490,6 +1507,7 @@ const MapSightbar: React.FC<MapSightbarProps> = ({ | |||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const handleCheckboxChange = useCallback( |   const handleCheckboxChange = useCallback( | ||||||
|  |     // @ts-ignore | ||||||
|     (id) => { |     (id) => { | ||||||
|       if (id === undefined) return; |       if (id === undefined) return; | ||||||
|       const newSet = new Set(selectedIds); |       const newSet = new Set(selectedIds); | ||||||
| @@ -1512,7 +1530,10 @@ const MapSightbar: React.FC<MapSightbarProps> = ({ | |||||||
|     } |     } | ||||||
|   }, [mapService, selectedIds, setSelectedIds]); |   }, [mapService, selectedIds, setSelectedIds]); | ||||||
|  |  | ||||||
|  |   // @ts-ignore | ||||||
|  |  | ||||||
|   const handleEditFeature = useCallback( |   const handleEditFeature = useCallback( | ||||||
|  |     // @ts-ignore | ||||||
|     (featureType, fullId) => { |     (featureType, fullId) => { | ||||||
|       if (!featureType || !fullId) return; |       if (!featureType || !fullId) return; | ||||||
|       const numericId = String(fullId).split("-")[1]; |       const numericId = String(fullId).split("-")[1]; | ||||||
| @@ -1522,8 +1543,11 @@ const MapSightbar: React.FC<MapSightbarProps> = ({ | |||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const sortFeatures = ( |   const sortFeatures = ( | ||||||
|  |     // @ts-ignore | ||||||
|     features, |     features, | ||||||
|  |     // @ts-ignore | ||||||
|     currentSelectedIds, |     currentSelectedIds, | ||||||
|  |     // @ts-ignore | ||||||
|     currentSelectedFeature |     currentSelectedFeature | ||||||
|   ) => { |   ) => { | ||||||
|     const selectedId = currentSelectedFeature?.getId(); |     const selectedId = currentSelectedFeature?.getId(); | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ import { | |||||||
|   MenuItem, |   MenuItem, | ||||||
|   FormControl, |   FormControl, | ||||||
|   InputLabel, |   InputLabel, | ||||||
|   Typography, |   // Typography, | ||||||
|   Box, |   Box, | ||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
| import { LanguageSwitcher } from "@widgets"; | import { LanguageSwitcher } from "@widgets"; | ||||||
| @@ -18,6 +18,7 @@ import { toast } from "react-toastify"; | |||||||
| import { carrierStore } from "../../../shared/store/CarrierStore"; | import { carrierStore } from "../../../shared/store/CarrierStore"; | ||||||
| import { articlesStore } from "../../../shared/store/ArticlesStore"; | import { articlesStore } from "../../../shared/store/ArticlesStore"; | ||||||
| import { Route, routeStore } from "../../../shared/store/RouteStore"; | import { Route, routeStore } from "../../../shared/store/RouteStore"; | ||||||
|  | import { languageStore } from "@shared"; | ||||||
|  |  | ||||||
| export const RouteCreatePage = observer(() => { | export const RouteCreatePage = observer(() => { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
| @@ -33,11 +34,11 @@ export const RouteCreatePage = observer(() => { | |||||||
|   const [centerLat, setCenterLat] = useState(""); |   const [centerLat, setCenterLat] = useState(""); | ||||||
|   const [centerLng, setCenterLng] = useState(""); |   const [centerLng, setCenterLng] = useState(""); | ||||||
|   const [isLoading, setIsLoading] = useState(false); |   const [isLoading, setIsLoading] = useState(false); | ||||||
|  |   const { language } = languageStore; | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     carrierStore.getCarriers(); |     carrierStore.getCarriers(language); | ||||||
|     articlesStore.getArticleList(); |     articlesStore.getArticleList(); | ||||||
|   }, []); |   }, [language]); | ||||||
|  |  | ||||||
|   const handleCreateRoute = async () => { |   const handleCreateRoute = async () => { | ||||||
|     try { |     try { | ||||||
| @@ -65,8 +66,9 @@ export const RouteCreatePage = observer(() => { | |||||||
|       // Собираем объект маршрута |       // Собираем объект маршрута | ||||||
|       const newRoute: Partial<Route> = { |       const newRoute: Partial<Route> = { | ||||||
|         carrier: |         carrier: | ||||||
|           carrierStore.carriers.data.find((c: any) => c.id === carrier_id) |           carrierStore.carriers[ | ||||||
|             ?.full_name || "", |             language as keyof typeof carrierStore.carriers | ||||||
|  |           ].data?.find((c: any) => c.id === carrier_id)?.full_name || "", | ||||||
|         carrier_id, |         carrier_id, | ||||||
|         route_number: routeNumber, |         route_number: routeNumber, | ||||||
|         route_sys_number: govRouteNumber, |         route_sys_number: govRouteNumber, | ||||||
| @@ -112,16 +114,20 @@ export const RouteCreatePage = observer(() => { | |||||||
|               value={carrier} |               value={carrier} | ||||||
|               label="Выберите перевозчика" |               label="Выберите перевозчика" | ||||||
|               onChange={(e) => setCarrier(e.target.value as string)} |               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 | ||||||
|  |               } | ||||||
|             > |             > | ||||||
|               <MenuItem value="">Не выбрано</MenuItem> |               <MenuItem value="">Не выбрано</MenuItem> | ||||||
|               {carrierStore.carriers.data.map( |               {carrierStore.carriers[ | ||||||
|                 (c: (typeof carrierStore.carriers.data)[number]) => ( |                 language as keyof typeof carrierStore.carriers | ||||||
|                   <MenuItem key={c.id} value={c.id}> |               ].data?.map((carrier) => ( | ||||||
|                     {c.full_name} |                 <MenuItem key={carrier.id} value={carrier.id}> | ||||||
|  |                   {carrier.full_name} | ||||||
|                 </MenuItem> |                 </MenuItem> | ||||||
|                 ) |               ))} | ||||||
|               )} |  | ||||||
|             </Select> |             </Select> | ||||||
|           </FormControl> |           </FormControl> | ||||||
|           <TextField |           <TextField | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ import { | |||||||
|   MenuItem, |   MenuItem, | ||||||
|   FormControl, |   FormControl, | ||||||
|   InputLabel, |   InputLabel, | ||||||
|   Typography, |   // Typography, | ||||||
|   Box, |   Box, | ||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
| import { LanguageSwitcher } from "@widgets"; | import { LanguageSwitcher } from "@widgets"; | ||||||
| @@ -19,22 +19,23 @@ import { carrierStore } from "../../../shared/store/CarrierStore"; | |||||||
| import { articlesStore } from "../../../shared/store/ArticlesStore"; | import { articlesStore } from "../../../shared/store/ArticlesStore"; | ||||||
| import { routeStore } from "../../../shared/store/RouteStore"; | import { routeStore } from "../../../shared/store/RouteStore"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
|  | import { languageStore } from "@shared"; | ||||||
|  |  | ||||||
| export const RouteEditPage = observer(() => { | export const RouteEditPage = observer(() => { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|   const { editRouteData } = routeStore; |   const { editRouteData } = routeStore; | ||||||
|   const [isLoading, setIsLoading] = useState(false); |   const [isLoading, setIsLoading] = useState(false); | ||||||
|  |   const { language } = languageStore; | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const fetchData = async () => { |     const fetchData = async () => { | ||||||
|       const response = await routeStore.getRoute(Number(id)); |       const response = await routeStore.getRoute(Number(id)); | ||||||
|       routeStore.setEditRouteData(response); |       routeStore.setEditRouteData(response); | ||||||
|       carrierStore.getCarriers(); |       carrierStore.getCarriers(language); | ||||||
|       articlesStore.getArticleList(); |       articlesStore.getArticleList(); | ||||||
|     }; |     }; | ||||||
|     fetchData(); |     fetchData(); | ||||||
|   }, [id]); |   }, [id, language]); | ||||||
|  |  | ||||||
|   const handleSave = async () => { |   const handleSave = async () => { | ||||||
|     setIsLoading(true); |     setIsLoading(true); | ||||||
| @@ -67,21 +68,26 @@ export const RouteEditPage = observer(() => { | |||||||
|                 routeStore.setEditRouteData({ |                 routeStore.setEditRouteData({ | ||||||
|                   carrier_id: Number(e.target.value), |                   carrier_id: Number(e.target.value), | ||||||
|                   carrier: |                   carrier: | ||||||
|                     carrierStore.carriers.data.find( |                     carrierStore.carriers[ | ||||||
|                       (c) => c.id === Number(e.target.value) |                       language as keyof typeof carrierStore.carriers | ||||||
|                     )?.full_name || "", |                     ].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 | ||||||
|  |               } | ||||||
|             > |             > | ||||||
|               <MenuItem value="">Не выбрано</MenuItem> |               <MenuItem value="">Не выбрано</MenuItem> | ||||||
|               {carrierStore.carriers.data.map( |               {carrierStore.carriers[ | ||||||
|                 (c: (typeof carrierStore.carriers.data)[number]) => ( |                 language as keyof typeof carrierStore.carriers | ||||||
|                   <MenuItem key={c.id} value={c.id}> |               ].data?.map((carrier) => ( | ||||||
|                     {c.full_name} |                 <MenuItem key={carrier.id} value={carrier.id}> | ||||||
|  |                   {carrier.full_name} | ||||||
|                 </MenuItem> |                 </MenuItem> | ||||||
|                 ) |               ))} | ||||||
|               )} |  | ||||||
|             </Select> |             </Select> | ||||||
|           </FormControl> |           </FormControl> | ||||||
|           <TextField |           <TextField | ||||||
|   | |||||||
| @@ -48,8 +48,8 @@ export const StationCreatePage = observer(() => { | |||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
|       <div className="flex flex-col gap-10 w-full items-end"> |       <div className="flex flex-col gap-10 w-full items-end"> | ||||||
|         <div className="flex gap-10 items-center mb-5 max-w-[80%]"> |         <div className="flex gap-10 items-center mb-5 max-w-[80%] self-start"> | ||||||
|           <h1 className="text-3xl break-words">Создание станции</h1> |           <h1 className="text-3xl break-words">{name}</h1> | ||||||
|         </div> |         </div> | ||||||
|         <TextField |         <TextField | ||||||
|           className="w-full" |           className="w-full" | ||||||
|   | |||||||
| @@ -7,7 +7,12 @@ import { | |||||||
|   FormControl, |   FormControl, | ||||||
|   InputLabel, |   InputLabel, | ||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
| import { vehicleStore, VEHICLE_TYPES, carrierStore } from "@shared"; | import { | ||||||
|  |   vehicleStore, | ||||||
|  |   VEHICLE_TYPES, | ||||||
|  |   carrierStore, | ||||||
|  |   languageStore, | ||||||
|  | } from "@shared"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
| import { ArrowLeft, Save } from "lucide-react"; | import { ArrowLeft, Save } from "lucide-react"; | ||||||
| import { Loader2 } from "lucide-react"; | import { Loader2 } from "lucide-react"; | ||||||
| @@ -21,10 +26,11 @@ export const VehicleCreatePage = observer(() => { | |||||||
|   const [type, setType] = useState(""); |   const [type, setType] = useState(""); | ||||||
|   const [carrierId, setCarrierId] = useState<number | null>(null); |   const [carrierId, setCarrierId] = useState<number | null>(null); | ||||||
|   const [isLoading, setIsLoading] = useState(false); |   const [isLoading, setIsLoading] = useState(false); | ||||||
|  |   const { language } = languageStore; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     carrierStore.getCarriers(); |     carrierStore.getCarriers(language); | ||||||
|   }, []); |   }, [language]); | ||||||
|  |  | ||||||
|   const handleCreate = async () => { |   const handleCreate = async () => { | ||||||
|     try { |     try { | ||||||
| @@ -32,7 +38,8 @@ export const VehicleCreatePage = observer(() => { | |||||||
|       await vehicleStore.createVehicle( |       await vehicleStore.createVehicle( | ||||||
|         Number(tailNumber), |         Number(tailNumber), | ||||||
|         Number(type), |         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! |         carrierId! | ||||||
|       ); |       ); | ||||||
|       toast.success("Транспорт успешно создан"); |       toast.success("Транспорт успешно создан"); | ||||||
| @@ -88,7 +95,7 @@ export const VehicleCreatePage = observer(() => { | |||||||
|             required |             required | ||||||
|             onChange={(e) => setCarrierId(e.target.value as number)} |             onChange={(e) => setCarrierId(e.target.value as number)} | ||||||
|           > |           > | ||||||
|             {carrierStore.carriers.data.map((carrier) => ( |             {carrierStore.carriers[language].data?.map((carrier) => ( | ||||||
|               <MenuItem key={carrier.id} value={carrier.id}> |               <MenuItem key={carrier.id} value={carrier.id}> | ||||||
|                 {carrier.full_name} |                 {carrier.full_name} | ||||||
|               </MenuItem> |               </MenuItem> | ||||||
|   | |||||||
| @@ -11,7 +11,12 @@ import { observer } from "mobx-react-lite"; | |||||||
| import { ArrowLeft, Loader2, Save } from "lucide-react"; | import { ArrowLeft, Loader2, Save } from "lucide-react"; | ||||||
| import { useNavigate, useParams } from "react-router-dom"; | import { useNavigate, useParams } from "react-router-dom"; | ||||||
| import { useEffect, useState } from "react"; | 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"; | import { toast } from "react-toastify"; | ||||||
|  |  | ||||||
| export const VehicleEditPage = observer(() => { | export const VehicleEditPage = observer(() => { | ||||||
| @@ -25,11 +30,12 @@ export const VehicleEditPage = observer(() => { | |||||||
|     editVehicle, |     editVehicle, | ||||||
|   } = vehicleStore; |   } = vehicleStore; | ||||||
|   const { getCarriers } = carrierStore; |   const { getCarriers } = carrierStore; | ||||||
|  |   const { language } = languageStore; | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     (async () => { |     (async () => { | ||||||
|       await getVehicle(Number(id)); |       await getVehicle(Number(id)); | ||||||
|       await getCarriers(); |       await getCarriers(language); | ||||||
|  |  | ||||||
|       setEditVehicleData({ |       setEditVehicleData({ | ||||||
|         tail_number: vehicle[Number(id)]?.vehicle.tail_number, |         tail_number: vehicle[Number(id)]?.vehicle.tail_number, | ||||||
|         type: vehicle[Number(id)]?.vehicle.type, |         type: vehicle[Number(id)]?.vehicle.type, | ||||||
| @@ -37,7 +43,7 @@ export const VehicleEditPage = observer(() => { | |||||||
|         carrier_id: vehicle[Number(id)]?.vehicle.carrier_id, |         carrier_id: vehicle[Number(id)]?.vehicle.carrier_id, | ||||||
|       }); |       }); | ||||||
|     })(); |     })(); | ||||||
|   }, [id]); |   }, [id, language]); | ||||||
|   const [isLoading, setIsLoading] = useState(false); |   const [isLoading, setIsLoading] = useState(false); | ||||||
|   const handleEdit = async () => { |   const handleEdit = async () => { | ||||||
|     try { |     try { | ||||||
| @@ -108,7 +114,7 @@ export const VehicleEditPage = observer(() => { | |||||||
|               }) |               }) | ||||||
|             } |             } | ||||||
|           > |           > | ||||||
|             {carrierStore.carriers.data.map((carrier) => ( |             {carrierStore.carriers[language].data?.map((carrier) => ( | ||||||
|               <MenuItem key={carrier.id} value={carrier.id}> |               <MenuItem key={carrier.id} value={carrier.id}> | ||||||
|                 {carrier.full_name} |                 {carrier.full_name} | ||||||
|               </MenuItem> |               </MenuItem> | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ export const VehicleListPage = observer(() => { | |||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     getVehicles(); |     getVehicles(); | ||||||
|     getCarriers(); |     getCarriers(language); | ||||||
|   }, [language]); |   }, [language]); | ||||||
|  |  | ||||||
|   const columns: GridColDef[] = [ |   const columns: GridColDef[] = [ | ||||||
| @@ -123,7 +123,7 @@ export const VehicleListPage = observer(() => { | |||||||
|     tail_number: vehicle.vehicle.tail_number, |     tail_number: vehicle.vehicle.tail_number, | ||||||
|     type: vehicle.vehicle.type, |     type: vehicle.vehicle.type, | ||||||
|     carrier: vehicle.vehicle.carrier, |     carrier: vehicle.vehicle.carrier, | ||||||
|     city: carriers.data?.find( |     city: carriers[language].data?.find( | ||||||
|       (carrier) => carrier.id === vehicle.vehicle.carrier_id |       (carrier) => carrier.id === vehicle.vehicle.carrier_id | ||||||
|     )?.city, |     )?.city, | ||||||
|   })); |   })); | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import { | |||||||
|   Earth, |   Earth, | ||||||
|   Landmark, |   Landmark, | ||||||
|   GitBranch, |   GitBranch, | ||||||
|   Car, |   // Car, | ||||||
|   Table, |   Table, | ||||||
|   Notebook, |   Notebook, | ||||||
|   Split, |   Split, | ||||||
|   | |||||||
| @@ -1,4 +1,10 @@ | |||||||
| import { authInstance, cityStore, languageStore } from "@shared"; | import { | ||||||
|  |   authInstance, | ||||||
|  |   cityStore, | ||||||
|  |   languageStore, | ||||||
|  |   languageInstance, | ||||||
|  |   Language, | ||||||
|  | } from "@shared"; | ||||||
| import { makeAutoObservable, runInAction } from "mobx"; | import { makeAutoObservable, runInAction } from "mobx"; | ||||||
|  |  | ||||||
| export type Carrier = { | export type Carrier = { | ||||||
| @@ -14,17 +20,40 @@ export type Carrier = { | |||||||
|   // right_color: string; |   // right_color: string; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| type Carriers = { | type CarrierData = { | ||||||
|   data: Carrier[]; |   data: Carrier[]; | ||||||
|   loaded: boolean; |   loaded: boolean; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| type CashedCarrier = Record<number, Carrier>; | type Carriers = { | ||||||
|  |   ru: CarrierData; | ||||||
|  |   en: CarrierData; | ||||||
|  |   zh: CarrierData; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type CashedCarrier = Record< | ||||||
|  |   number, | ||||||
|  |   { | ||||||
|  |     ru: Carrier | null; | ||||||
|  |     en: Carrier | null; | ||||||
|  |     zh: Carrier | null; | ||||||
|  |   } | ||||||
|  | >; | ||||||
|  |  | ||||||
| class CarrierStore { | class CarrierStore { | ||||||
|   carriers: Carriers = { |   carriers: Carriers = { | ||||||
|  |     ru: { | ||||||
|       data: [], |       data: [], | ||||||
|       loaded: false, |       loaded: false, | ||||||
|  |     }, | ||||||
|  |     en: { | ||||||
|  |       data: [], | ||||||
|  |       loaded: false, | ||||||
|  |     }, | ||||||
|  |     zh: { | ||||||
|  |       data: [], | ||||||
|  |       loaded: false, | ||||||
|  |     }, | ||||||
|   }; |   }; | ||||||
|   carrier: CashedCarrier = {}; |   carrier: CashedCarrier = {}; | ||||||
|  |  | ||||||
| @@ -32,14 +61,14 @@ class CarrierStore { | |||||||
|     makeAutoObservable(this); |     makeAutoObservable(this); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getCarriers = async () => { |   getCarriers = async (language: Language) => { | ||||||
|     if (this.carriers.loaded) return; |     if (this.carriers[language as keyof Carriers].loaded) return; | ||||||
|  |  | ||||||
|     const response = await authInstance.get("/carrier"); |     const response = await authInstance.get("/carrier"); | ||||||
|  |  | ||||||
|     runInAction(() => { |     runInAction(() => { | ||||||
|       this.carriers.data = response.data; |       this.carriers[language as keyof Carriers].data = response.data; | ||||||
|       this.carriers.loaded = true; |       this.carriers[language as keyof Carriers].loaded = true; | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -47,116 +76,163 @@ class CarrierStore { | |||||||
|     await authInstance.delete(`/carrier/${id}`); |     await authInstance.delete(`/carrier/${id}`); | ||||||
|  |  | ||||||
|     runInAction(() => { |     runInAction(() => { | ||||||
|       this.carriers.data = this.carriers.data.filter( |       for (const language of ["ru", "en", "zh"] as const) { | ||||||
|         (carrier) => carrier.id !== id |         this.carriers[language].data = this.carriers[language].data.filter( | ||||||
|  |           (carrier: Carrier) => carrier.id !== id | ||||||
|         ); |         ); | ||||||
|  |       } | ||||||
|       delete this.carrier[id]; |       delete this.carrier[id]; | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   getCarrier = async (id: number) => { |   getCarrier = async (id: number) => { | ||||||
|     if (this.carrier[id]) return; |     if (this.carrier[id]?.ru && this.carrier[id]?.en && this.carrier[id]?.zh) | ||||||
|     const response = await authInstance.get(`/carrier/${id}`); |       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(() => { |     runInAction(() => { | ||||||
|       if (!this.carrier[id]) { |       if (!this.carrier[id]) { | ||||||
|         this.carrier[id] = { |         this.carrier[id] = { | ||||||
|           id: 0, |           ru: null, | ||||||
|           short_name: "", |           en: null, | ||||||
|           full_name: "", |           zh: null, | ||||||
|           slogan: "", |  | ||||||
|           city: "", |  | ||||||
|           city_id: 0, |  | ||||||
|           logo: "", |  | ||||||
|           // main_color: "", |  | ||||||
|           // left_color: "", |  | ||||||
|           // right_color: "", |  | ||||||
|         }; |         }; | ||||||
|       } |       } | ||||||
|       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 ( |   createCarrier = async ( | ||||||
|     fullName: string, |     fullName: string, | ||||||
|     shortName: string, |     shortName: string, | ||||||
|  |  | ||||||
|     cityId: number, |     cityId: number, | ||||||
|     // main_color: string, |  | ||||||
|     // left_color: string, |  | ||||||
|     // right_color: string, |  | ||||||
|     slogan: string, |     slogan: string, | ||||||
|     logoId: 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, |       full_name: fullName, | ||||||
|       short_name: shortName, |       short_name: shortName, | ||||||
|       city: "", |       city: cityName, | ||||||
|       city_id: cityId, |       city_id: cityId, | ||||||
|       // main_color, |  | ||||||
|       // left_color, |  | ||||||
|       // right_color, |  | ||||||
|       slogan, |       slogan, | ||||||
|       logo: logoId, |       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(() => { |     runInAction(() => { | ||||||
|       this.carriers.data.push(response.data); |       for (const language of ["ru", "en", "zh"] as const) { | ||||||
|  |         this.carriers[language].data.push(response.data); | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   editCarrierData = { |   editCarrierData = { | ||||||
|  |     ru: { | ||||||
|       full_name: "", |       full_name: "", | ||||||
|       short_name: "", |       short_name: "", | ||||||
|  |  | ||||||
|     city_id: 0, |  | ||||||
|       // main_color: "", |       // main_color: "", | ||||||
|       // left_color: "", |       // left_color: "", | ||||||
|       // right_color: "", |       // right_color: "", | ||||||
|       slogan: "", |       slogan: "", | ||||||
|  |     }, | ||||||
|  |     en: { | ||||||
|  |       full_name: "", | ||||||
|  |       short_name: "", | ||||||
|  |  | ||||||
|  |       // main_color: "", | ||||||
|  |       // left_color: "", | ||||||
|  |       // right_color: "", | ||||||
|  |       slogan: "", | ||||||
|  |     }, | ||||||
|  |     city_id: 0, | ||||||
|     logo: "", |     logo: "", | ||||||
|  |     zh: { | ||||||
|  |       full_name: "", | ||||||
|  |       short_name: "", | ||||||
|  |  | ||||||
|  |       // main_color: "", | ||||||
|  |       // left_color: "", | ||||||
|  |       // right_color: "", | ||||||
|  |       slogan: "", | ||||||
|  |     }, | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   setEditCarrierData = ( |   setEditCarrierData = ( | ||||||
|     fullName: string, |     fullName: string, | ||||||
|     shortName: string, |     shortName: string, | ||||||
|  |  | ||||||
|     cityId: number, |     cityId: number, | ||||||
|     // main_color: string, |     // main_color: string, | ||||||
|     // left_color: string, |     // left_color: string, | ||||||
|     // right_color: string, |     // right_color: string, | ||||||
|     slogan: 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, |       full_name: fullName, | ||||||
|       short_name: shortName, |       short_name: shortName, | ||||||
|  |  | ||||||
|       city_id: cityId, |  | ||||||
|       // main_color: main_color, |       // main_color: main_color, | ||||||
|       // left_color: left_color, |       // left_color: left_color, | ||||||
|       // right_color: right_color, |       // right_color: right_color, | ||||||
|       slogan: slogan, |       slogan: slogan, | ||||||
|       logo: logoId, |  | ||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   editCarrier = async (id: number) => { |   editCarrier = async (id: number) => { | ||||||
|     const { language } = languageStore; |     const cityName = | ||||||
|     const response = await authInstance.patch(`/carrier/${id}`, { |       cityStore.cities[languageStore.language].data.find( | ||||||
|       ...this.editCarrierData, |  | ||||||
|       city: cityStore.cities[language].data.find( |  | ||||||
|         (city) => city.id === this.editCarrierData.city_id |         (city) => city.id === this.editCarrierData.city_id | ||||||
|       )?.name, |       )?.name || ""; | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     runInAction(() => { |     for (const language of ["ru", "en", "zh"] as const) { | ||||||
|       this.carriers.data = this.carriers.data.map((carrier) => |       const response = await languageInstance(language).patch( | ||||||
|         carrier.id === id ? { ...carrier, ...response.data } : carrier |         `/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 | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|       }); |       }); | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -175,9 +175,10 @@ export const CreateInformationTab = observer( | |||||||
|                 /> |                 /> | ||||||
|  |  | ||||||
|                 <Autocomplete |                 <Autocomplete | ||||||
|                   options={ruCities ?? []} |                   options={ruCities.data ?? []} | ||||||
|                   value={ |                   value={ | ||||||
|                     ruCities.find((city) => city.id === sight.city_id) ?? null |                     ruCities.data.find((city) => city.id === sight.city_id) ?? | ||||||
|  |                     null | ||||||
|                   } |                   } | ||||||
|                   getOptionLabel={(option) => option.name} |                   getOptionLabel={(option) => option.name} | ||||||
|                   onChange={(_, value) => { |                   onChange={(_, value) => { | ||||||
|   | |||||||
| @@ -19,14 +19,7 @@ import { | |||||||
|   MediaViewer, |   MediaViewer, | ||||||
|   DeleteModal, |   DeleteModal, | ||||||
| } from "@widgets"; | } from "@widgets"; | ||||||
| import { | import { Trash2, ImagePlus, Unlink, Plus, Save, Search } from "lucide-react"; | ||||||
|   Trash2, |  | ||||||
|   ImagePlus, |  | ||||||
|   Unlink, |  | ||||||
|   MousePointer, |  | ||||||
|   Plus, |  | ||||||
|   Save, |  | ||||||
| } from "lucide-react"; |  | ||||||
| import { useState, useCallback } from "react"; | import { useState, useCallback } from "react"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
| @@ -160,7 +153,7 @@ export const CreateLeftTab = observer( | |||||||
|                     variant="contained" |                     variant="contained" | ||||||
|                     color="primary" |                     color="primary" | ||||||
|                     size="small" |                     size="small" | ||||||
|                     startIcon={<MousePointer color="white" size={18} />} |                     startIcon={<Search color="white" size={18} />} | ||||||
|                     onClick={() => setIsSelectArticleDialogOpen(true)} |                     onClick={() => setIsSelectArticleDialogOpen(true)} | ||||||
|                   > |                   > | ||||||
|                     Выбрать статью |                     Выбрать статью | ||||||
|   | |||||||
| @@ -551,6 +551,7 @@ export const CreateRightTab = observer( | |||||||
|                           media={ |                           media={ | ||||||
|                             sight[language].right[activeArticleIndex].media[0] |                             sight[language].right[activeArticleIndex].media[0] | ||||||
|                           } |                           } | ||||||
|  |                           fullWidth | ||||||
|                         /> |                         /> | ||||||
|                       </Box> |                       </Box> | ||||||
|                     ) : ( |                     ) : ( | ||||||
|   | |||||||
| @@ -18,14 +18,7 @@ import { | |||||||
|   MediaViewer, |   MediaViewer, | ||||||
|   DeleteModal, |   DeleteModal, | ||||||
| } from "@widgets"; | } from "@widgets"; | ||||||
| import { | import { Trash2, ImagePlus, Unlink, Plus, Save, Search } from "lucide-react"; | ||||||
|   Trash2, |  | ||||||
|   ImagePlus, |  | ||||||
|   Unlink, |  | ||||||
|   Plus, |  | ||||||
|   MousePointer, |  | ||||||
|   Save, |  | ||||||
| } from "lucide-react"; |  | ||||||
| import { useState, useCallback } from "react"; | import { useState, useCallback } from "react"; | ||||||
| import { observer } from "mobx-react-lite"; | import { observer } from "mobx-react-lite"; | ||||||
| import { toast } from "react-toastify"; | import { toast } from "react-toastify"; | ||||||
| @@ -175,7 +168,7 @@ export const LeftWidgetTab = observer( | |||||||
|                       variant="contained" |                       variant="contained" | ||||||
|                       color="primary" |                       color="primary" | ||||||
|                       size="small" |                       size="small" | ||||||
|                       startIcon={<MousePointer color="white" size={18} />} |                       startIcon={<Search color="white" size={18} />} | ||||||
|                       onClick={() => setIsSelectArticleDialogOpen(true)} |                       onClick={() => setIsSelectArticleDialogOpen(true)} | ||||||
|                     > |                     > | ||||||
|                       Выбрать статью |                       Выбрать статью | ||||||
|   | |||||||
| @@ -493,6 +493,7 @@ export const RightWidgetTab = observer( | |||||||
|                             media={ |                             media={ | ||||||
|                               sight[language].right[activeArticleIndex].media[0] |                               sight[language].right[activeArticleIndex].media[0] | ||||||
|                             } |                             } | ||||||
|  |                             fullWidth | ||||||
|                           /> |                           /> | ||||||
|                         </Box> |                         </Box> | ||||||
|                       ) : ( |                       ) : ( | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ export const SightsTable = observer(() => { | |||||||
|             </TableRow> |             </TableRow> | ||||||
|           </TableHead> |           </TableHead> | ||||||
|           <TableBody> |           <TableBody> | ||||||
|             {rows(sights, cities[language])?.map((row) => ( |             {rows(sights, cities[language]?.data ?? [])?.map((row) => ( | ||||||
|               <TableRow |               <TableRow | ||||||
|                 key={row?.id} |                 key={row?.id} | ||||||
|                 sx={{ "&:last-child td, &:last-child th": { border: 0 } }} |                 sx={{ "&:last-child td, &:last-child th": { border: 0 } }} | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user