diff --git a/src/components/CustomDataGrid.tsx b/src/components/CustomDataGrid.tsx index 88594cf..888dfc9 100644 --- a/src/components/CustomDataGrid.tsx +++ b/src/components/CustomDataGrid.tsx @@ -3,17 +3,20 @@ import { type DataGridProps, type GridColumnVisibilityModel, } from "@mui/x-data-grid"; -import { Stack, Button, Typography } from "@mui/material"; +import { Stack, Button, Typography, Box } from "@mui/material"; import { ExportButton } from "@refinedev/mui"; import { useExport } from "@refinedev/core"; import React, { useState, useEffect, useMemo } from "react"; import Cookies from "js-cookie"; import { localeText } from "../locales/ru/localeText"; +import { languageStore } from "../store/LanguageStore"; +import { LanguageSwitch } from "./LanguageSwitch"; interface CustomDataGridProps extends DataGridProps { hasCoordinates?: boolean; resource?: string; // Add this prop + languageEnabled?: boolean; } const DEV_FIELDS = [ @@ -46,6 +49,7 @@ const DEV_FIELDS = [ ] as const; export const CustomDataGrid = ({ + languageEnabled = false, hasCoordinates = false, columns = [], resource, @@ -130,6 +134,9 @@ export const CustomDataGrid = ({ return ( + + + - {hasCoordinates && ( diff --git a/src/components/LanguageSwitch/index.tsx b/src/components/LanguageSwitch/index.tsx new file mode 100644 index 0000000..c216097 --- /dev/null +++ b/src/components/LanguageSwitch/index.tsx @@ -0,0 +1,70 @@ +import { Box } from "@mui/material"; +import { languageStore } from "../../store/LanguageStore"; +import { observer } from "mobx-react-lite"; + +export const LanguageSwitch = observer(({ action }: any) => { + const { language, setLanguageAction } = languageStore; + + const handleLanguageChange = (lang: string) => { + if (action) { + action(); + } + setLanguageAction(lang); + }; + + return ( + + handleLanguageChange("ru")} + > + RU + + handleLanguageChange("en")} + > + EN + + handleLanguageChange("zh")} + > + ZH + + + ); +}); diff --git a/src/components/LinkedItems.tsx b/src/components/LinkedItems.tsx index 614eb3f..bf25d84 100644 --- a/src/components/LinkedItems.tsx +++ b/src/components/LinkedItems.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react"; +import { Close } from "@mui/icons-material"; import { Stack, Typography, @@ -20,14 +21,19 @@ import { Paper, TableBody, IconButton, + Collapse, + Modal, } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import DragIndicatorIcon from "@mui/icons-material/DragIndicator"; import { axiosInstance } from "../providers/data"; import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; -import axios from "axios"; +import { articleStore } from "../store/ArticleStore"; +import { ArticleEditModal } from "./modals/ArticleEditModal"; +import { StationEditModal } from "./modals/StationEditModal"; +import { stationStore } from "../store/StationStore"; function insertAtPosition(arr: T[], pos: number, value: T): T[] { const index = pos - 1; if (index >= arr.length) { @@ -82,41 +88,8 @@ export const LinkedItems = ({ type, onSave, }: LinkedItemsProps) => { - const [articleLanguages, setArticleLanguages] = useState< - Record - >({}); - - const handleArticleLanguageChange = ( - articleId: number, - languageCode: string - ) => { - setArticleLanguages((prev) => ({ ...prev, [articleId]: languageCode })); - console.log(articleId, languageCode); - // Отправка запроса на сервер для сохранения языка - axios - .get( - `${import.meta.env.VITE_KRBL_API}/article/${articleId}/`, // Пример эндпоинта - { - headers: { - Authorization: `Bearer ${localStorage.getItem("refine-auth")}`, - "X-language": languageCode.toLowerCase(), - }, - } - ) - .then((response) => { - setLinkedItems( - linkedItems.map((item) => { - if (item.id == articleId) { - console.log(response.data); - return { ...response.data, language: languageCode }; - } else { - return item; - } - }) - ); - }); - }; - + const { setArticleModalOpenAction, setArticleIdAction } = articleStore; + const { setStationModalOpenAction, setStationIdAction } = stationStore; const [position, setPosition] = useState(1); const [items, setItems] = useState([]); const [linkedItems, setLinkedItems] = useState([]); @@ -143,22 +116,6 @@ export const LinkedItems = ({ } }, [linkedItems, setItemsParent]); - useEffect(() => { - // При загрузке linkedItems можно запросить текущие языки для статей - if (childResource === "article" && linkedItems.length > 0) { - const initialLanguages: Record = {}; - linkedItems.forEach((article) => { - // Предполагается, что у объекта article есть свойство language - if (article.language) { - initialLanguages[article.id] = article.language; - } else { - initialLanguages[article.id] = "RU"; // Или другой язык по умолчанию - } - }); - setArticleLanguages(initialLanguages); - } - }, [linkedItems, childResource]); - const onDragEnd = (result: any) => { if (!result.destination) return; @@ -294,223 +251,247 @@ export const LinkedItems = ({ }; return ( - - } - sx={{ - background: theme.palette.background.paper, - borderBottom: `1px solid ${theme.palette.divider}`, - }} - > - - Привязанные {title} - - + <> + + } + sx={{ + background: theme.palette.background.paper, + borderBottom: `1px solid ${theme.palette.divider}`, + }} + > + + Привязанные {title} + + - - - - - - - - {type === "edit" && dragAllowed && ( - - )} - - {fields.map((field) => ( - - {field.label} - - ))} - - {type === "edit" && ( - Действие - )} - - - - - {(provided) => ( - - {linkedItems.map((item, index) => ( - - {(provided) => ( - - {type === "edit" && dragAllowed && ( - - - - - - )} - - {index + 1} - - {fields.map((field, index) => ( - - {field.render - ? field.render(item[field.data]) - : item[field.data]} - - ))} - - {type === "edit" && ( - - - - )} - - )} - + + + + +
+ + + {type === "edit" && dragAllowed && ( + + )} + + {fields.map((field) => ( + + {field.label} + ))} - {provided.placeholder} - + + {type === "edit" && ( + Действие + )} + + + + + {(provided) => ( + + {linkedItems.map((item, index) => ( + + {(provided) => ( + <> + { + if (childResource === "article") { + setArticleModalOpenAction(true); + setArticleIdAction(item.id); + } + if (childResource === "station") { + setStationModalOpenAction(true); + setStationIdAction(item.id); + } + }} + ref={provided.innerRef} + {...provided.draggableProps} + {...provided.dragHandleProps} + hover + > + {type === "edit" && dragAllowed && ( + + + + + + )} + + {index + 1} + + {fields.map((field, index) => ( + + {field.render + ? field.render(item[field.data]) + : item[field.data]} + + ))} + + {type === "edit" && ( + + + + )} + + + )} + + ))} + + {provided.placeholder} + + )} + +
+
+
+ + {linkedItems.length === 0 && !isLoading && ( + + {title} не найдены + + )} + + {type === "edit" && ( + + Добавить {title} + item.id === selectedItemId + ) || null + } + onChange={(_, newValue) => + setSelectedItemId(newValue?.id || null) + } + options={availableItems} + getOptionLabel={(item) => String(item[fields[0].data])} + renderInput={(params) => ( + )} - - - - - - {linkedItems.length === 0 && !isLoading && ( - - {title} не найдены - - )} - - {type === "edit" && ( - - Добавить {title} - item.id === selectedItemId) || - null - } - onChange={(_, newValue) => - setSelectedItemId(newValue?.id || null) - } - options={availableItems} - getOptionLabel={(item) => String(item[fields[0].data])} - renderInput={(params) => ( - - )} - isOptionEqualToValue={(option, value) => - option.id === value?.id - } - filterOptions={(options, { inputValue }) => { - const searchWords = inputValue - .toLowerCase() - .split(" ") - .filter((word) => word.length > 0); - return options.filter((option) => { - const optionWords = String(option[fields[0].data]) + isOptionEqualToValue={(option, value) => + option.id === value?.id + } + filterOptions={(options, { inputValue }) => { + const searchWords = inputValue .toLowerCase() - .split(" "); - return searchWords.every((searchWord) => - optionWords.some((word) => word.startsWith(searchWord)) - ); - }); - }} - renderOption={(props, option) => ( -
  • - {String(option[fields[0].data])} -
  • - )} - /> - - {childResource === "article" && ( - - { - const newValue = Number(e.target.value); - const minValue = linkedItems.length + 1; - setPageNum(newValue < minValue ? minValue : newValue); - }} - fullWidth - InputLabelProps={{ shrink: true }} - /> - - )} - - {childResource === "media" && ( - - { - const newValue = Number(e.target.value); - const maxValue = linkedItems.length + 1; - const value = Math.max(1, Math.min(newValue, maxValue)); - setMediaOrder(value); - }} - fullWidth - InputLabelProps={{ shrink: true }} - /> - - )} - - - {childResource == "station" && ( - { - const newValue = Number(e.target.value); - setPosition( - newValue > linkedItems.length + 1 - ? linkedItems.length + 1 - : newValue - ); + .split(" ") + .filter((word) => word.length > 0); + return options.filter((option) => { + const optionWords = String(option[fields[0].data]) + .toLowerCase() + .split(" "); + return searchWords.every((searchWord) => + optionWords.some((word) => word.startsWith(searchWord)) + ); + }); }} - > - )} -
    - )} -
    -
    -
    + renderOption={(props, option) => ( +
  • + {String(option[fields[0].data])} +
  • + )} + /> + + {childResource === "article" && ( + + { + const newValue = Number(e.target.value); + const minValue = linkedItems.length + 1; + setPageNum(newValue < minValue ? minValue : newValue); + }} + fullWidth + InputLabelProps={{ shrink: true }} + /> + + )} + + {childResource === "media" && ( + + { + const newValue = Number(e.target.value); + const maxValue = linkedItems.length + 1; + const value = Math.max(1, Math.min(newValue, maxValue)); + setMediaOrder(value); + }} + fullWidth + InputLabelProps={{ shrink: true }} + /> + + )} + + + {childResource == "station" && ( + { + const newValue = Number(e.target.value); + setPosition( + newValue > linkedItems.length + 1 + ? linkedItems.length + 1 + : newValue + ); + }} + > + )} +
    + )} +
    + + + + + ); }; diff --git a/src/components/index.ts b/src/components/index.ts deleted file mode 100644 index 7c8ead2..0000000 --- a/src/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {Header} from './header' diff --git a/src/components/modals/ArticleEditModal/index.tsx b/src/components/modals/ArticleEditModal/index.tsx new file mode 100644 index 0000000..a843dc7 --- /dev/null +++ b/src/components/modals/ArticleEditModal/index.tsx @@ -0,0 +1,171 @@ +import { Modal, Box, Button, TextField, Typography } from "@mui/material"; +import { articleStore } from "../../../store/ArticleStore"; +import { observer } from "mobx-react-lite"; +import { useForm } from "@refinedev/react-hook-form"; +import { Controller } from "react-hook-form"; +import "easymde/dist/easymde.min.css"; +import { memo, useMemo, useEffect } from "react"; +import { MarkdownEditor } from "../../MarkdownEditor"; +import { Edit } from "@refinedev/mui"; +import { languageStore } from "../../../store/LanguageStore"; +import { LanguageSwitch } from "../../LanguageSwitch/index"; +import { useNavigate } from "react-router"; +import { useState } from "react"; + +const MemoizedSimpleMDE = memo(MarkdownEditor); + +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "60%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 4, +}; + +export const ArticleEditModal = observer(() => { + const [articleData, setArticleData] = useState({ + ru: { + heading: "", + body: "", + }, + en: { + heading: "", + body: "", + }, + zh: { + heading: "", + body: "", + }, + }); + const { articleModalOpen, setArticleModalOpenAction, selectedArticleId } = + articleStore; + const { language } = languageStore; + + useEffect(() => { + return () => { + setArticleModalOpenAction(false); + }; + }, []); + + const { + register, + control, + formState: { errors }, + saveButtonProps, + reset, + setValue, + watch, + } = useForm({ + refineCoreProps: { + resource: "article", + id: selectedArticleId ?? undefined, + action: "edit", + redirect: false, + + onMutationSuccess: () => { + setArticleModalOpenAction(false); + reset(); + window.location.reload(); + }, + meta: { + headers: { + "Accept-Language": language, + }, + }, + }, + }); + + useEffect(() => { + if (articleData[language as keyof typeof articleData]?.heading) { + setValue( + "heading", + articleData[language as keyof typeof articleData]?.heading || "" + ); + } + if (articleData[language as keyof typeof articleData]?.body) { + setValue( + "body", + articleData[language as keyof typeof articleData]?.body || "" + ); + } + }, [language, articleData, setValue]); + + const handleLanguageChange = () => { + setArticleData((prevData) => ({ + ...prevData, + [language]: { + heading: watch("heading") || "", + body: watch("body") || "", + }, + })); + }; + + const simpleMDEOptions = useMemo( + () => ({ + placeholder: "Введите контент в формате Markdown...", + spellChecker: false, + }), + [] + ); + + return ( + setArticleModalOpenAction(false)} + aria-labelledby="modal-modal-title" + aria-describedby="modal-modal-description" + > + + Редактирование статьи} + headerProps={{ + sx: { + fontSize: "50px", + }, + }} + saveButtonProps={saveButtonProps} + > + + + + + ( + + )} + /> + + + + + ); +}); diff --git a/src/components/modals/StationEditModal/index.tsx b/src/components/modals/StationEditModal/index.tsx new file mode 100644 index 0000000..473a421 --- /dev/null +++ b/src/components/modals/StationEditModal/index.tsx @@ -0,0 +1,164 @@ +import { + Modal, + Box, + Button, + TextField, + Typography, + Grid, + Paper, +} from "@mui/material"; + +import { observer } from "mobx-react-lite"; +import { useForm } from "@refinedev/react-hook-form"; +import { Controller } from "react-hook-form"; +import "easymde/dist/easymde.min.css"; +import { memo, useMemo, useEffect } from "react"; +import { MarkdownEditor } from "../../MarkdownEditor"; +import { Edit } from "@refinedev/mui"; +import { languageStore } from "../../../store/LanguageStore"; +import { LanguageSwitch } from "../../LanguageSwitch/index"; + +import { useState } from "react"; +import { stationStore } from "../../../store/StationStore"; +const MemoizedSimpleMDE = memo(MarkdownEditor); + +const TRANSFER_FIELDS = [ + { name: "bus", label: "Автобус" }, + { name: "metro_blue", label: "Метро (синяя)" }, + { name: "metro_green", label: "Метро (зеленая)" }, + { name: "metro_orange", label: "Метро (оранжевая)" }, + { name: "metro_purple", label: "Метро (фиолетовая)" }, + { name: "metro_red", label: "Метро (красная)" }, + { name: "train", label: "Электричка" }, + { name: "tram", label: "Трамвай" }, + { name: "trolleybus", label: "Троллейбус" }, +]; + +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "60%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 4, +}; + +export const StationEditModal = observer(() => { + const { stationModalOpen, setStationModalOpenAction, selectedStationId } = + stationStore; + const { language } = languageStore; + + useEffect(() => { + return () => { + setStationModalOpenAction(false); + }; + }, []); + + const { + register, + control, + formState: { errors }, + saveButtonProps, + reset, + setValue, + watch, + } = useForm({ + refineCoreProps: { + resource: "station", + id: selectedStationId ?? undefined, + action: "edit", + redirect: false, + + onMutationSuccess: () => { + setStationModalOpenAction(false); + reset(); + window.location.reload(); + }, + meta: { + headers: { + "Accept-Language": language, + }, + }, + }, + }); + + return ( + setStationModalOpenAction(false)} + aria-labelledby="modal-modal-title" + aria-describedby="modal-modal-description" + > + + Редактирование станции} + saveButtonProps={saveButtonProps} + > + + parseFloat(value), + })} + error={!!(errors as any)?.offset_x} + helperText={(errors as any)?.offset_x?.message} + margin="normal" + fullWidth + InputLabelProps={{ shrink: true }} + type="number" + label={"Смещение (X)"} + name="offset_x" + /> + + parseFloat(value), + })} + error={!!(errors as any)?.offset_y} + helperText={(errors as any)?.offset_y?.message} + margin="normal" + fullWidth + InputLabelProps={{ shrink: true }} + type="number" + label={"Смещение (Y)"} + name="offset_y" + /> + + {/* Группа полей пересадок */} + + + Пересадки + + + {TRANSFER_FIELDS.map((field) => ( + + + + ))} + + + + + + + ); +}); diff --git a/src/pages/article/edit.tsx b/src/pages/article/edit.tsx index 6dde00f..bbf4204 100644 --- a/src/pages/article/edit.tsx +++ b/src/pages/article/edit.tsx @@ -58,18 +58,22 @@ export const ArticleEdit = observer(() => { }); useEffect(() => { - setValue( - "heading", - articleData[language as keyof typeof articleData]?.heading || "" - ); - setValue( - "body", - articleData[language as keyof typeof articleData]?.body || "" - ); - setPreview(articleData[language as keyof typeof articleData]?.body || ""); - setHeadingPreview( - articleData[language as keyof typeof articleData]?.heading || "" - ); + if (articleData[language as keyof typeof articleData]?.heading) { + setValue( + "heading", + articleData[language as keyof typeof articleData]?.heading + ); + setHeadingPreview( + articleData[language as keyof typeof articleData]?.heading || "" + ); + } + if (articleData[language as keyof typeof articleData]?.body) { + setValue( + "body", + articleData[language as keyof typeof articleData]?.body || "" + ); + setPreview(articleData[language as keyof typeof articleData]?.body || ""); + } }, [language, articleData, setValue]); const handleLanguageChange = (lang: string) => { diff --git a/src/pages/article/list.tsx b/src/pages/article/list.tsx index 16b9d2b..168a63f 100644 --- a/src/pages/article/list.tsx +++ b/src/pages/article/list.tsx @@ -1,34 +1,49 @@ -import {type GridColDef} from '@mui/x-data-grid' -import {CustomDataGrid} from '../../components/CustomDataGrid' -import {DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui' -import React from 'react' +import { type GridColDef } from "@mui/x-data-grid"; +import { CustomDataGrid } from "../../components/CustomDataGrid"; +import { + DeleteButton, + EditButton, + List, + ShowButton, + useDataGrid, +} from "@refinedev/mui"; +import React, { useEffect } from "react"; -import {localeText} from '../../locales/ru/localeText' +import { localeText } from "../../locales/ru/localeText"; +import { observer } from "mobx-react-lite"; +import { languageStore } from "../../store/LanguageStore"; -export const ArticleList = () => { - const {dataGridProps} = useDataGrid({ - resource: 'article/', - }) +export const ArticleList = observer(() => { + const { language } = languageStore; + + const { dataGridProps } = useDataGrid({ + resource: "article/", + meta: { + headers: { + "Accept-Language": language, + }, + }, + }); const columns = React.useMemo( () => [ { - field: 'id', - headerName: 'ID', - type: 'number', + field: "id", + headerName: "ID", + type: "number", minWidth: 70, - display: 'flex', - align: 'left', - headerAlign: 'left', + display: "flex", + align: "left", + headerAlign: "left", }, { - field: 'heading', - headerName: 'Заголовок', - type: 'string', + field: "heading", + headerName: "Заголовок", + type: "string", minWidth: 300, - display: 'flex', - align: 'left', - headerAlign: 'left', + display: "flex", + align: "left", + headerAlign: "left", flex: 1, }, // { @@ -41,32 +56,38 @@ export const ArticleList = () => { // flex: 1, // }, { - field: 'actions', - headerName: 'Действия', - align: 'right', - headerAlign: 'center', + field: "actions", + headerName: "Действия", + align: "right", + headerAlign: "center", minWidth: 120, - display: 'flex', + display: "flex", sortable: false, filterable: false, disableColumnMenu: true, - renderCell: function render({row}) { + renderCell: function render({ row }) { return ( <> - ) + ); }, }, ], - [], - ) + [] + ); return ( - row.id} /> + row.id} + /> - ) -} + ); +}); diff --git a/src/pages/carrier/create.tsx b/src/pages/carrier/create.tsx index fc692c9..891ea3e 100644 --- a/src/pages/carrier/create.tsx +++ b/src/pages/carrier/create.tsx @@ -1,172 +1,202 @@ -import {Autocomplete, Box, TextField} from '@mui/material' -import {Create, useAutocomplete} from '@refinedev/mui' -import {useForm} from '@refinedev/react-hook-form' -import {Controller} from 'react-hook-form' - -export const CarrierCreate = () => { +import { Autocomplete, Box, TextField } from "@mui/material"; +import { Create, useAutocomplete } from "@refinedev/mui"; +import { useForm } from "@refinedev/react-hook-form"; +import { Controller } from "react-hook-form"; +import { observer } from "mobx-react-lite"; +import { languageStore } from "../../store/LanguageStore"; +export const CarrierCreate = observer(() => { + const { language } = languageStore; const { saveButtonProps, - refineCore: {formLoading}, + refineCore: { formLoading }, register, control, - formState: {errors}, - } = useForm({}) + formState: { errors }, + } = useForm({ + refineCoreProps: { + meta: { + headers: { + "Accept-Language": language, + }, + }, + }, + }); - const {autocompleteProps: cityAutocompleteProps} = useAutocomplete({ - resource: 'city', + const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({ + resource: "city", onSearch: (value) => [ { - field: 'name', - operator: 'contains', + field: "name", + operator: "contains", value, }, ], - }) + }); - const {autocompleteProps: mediaAutocompleteProps} = useAutocomplete({ - resource: 'media', + const { autocompleteProps: mediaAutocompleteProps } = useAutocomplete({ + resource: "media", onSearch: (value) => [ { - field: 'media_name', - operator: 'contains', + field: "media_name", + operator: "contains", value, }, ], - }) + }); return ( - + ( + render={({ field }) => ( option.id === field.value) || null} + value={ + cityAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } onChange={(_, value) => { - field.onChange(value?.id || '') + field.onChange(value?.id || ""); }} getOptionLabel={(item) => { - return item ? item.name : '' + return item ? item.name : ""; }} isOptionEqualToValue={(option, value) => { - return option.id === value?.id + return option.id === value?.id; }} - filterOptions={(options, {inputValue}) => { - return options.filter((option) => option.name.toLowerCase().includes(inputValue.toLowerCase())) + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.name.toLowerCase().includes(inputValue.toLowerCase()) + ); }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> )} /> @@ -175,27 +205,44 @@ export const CarrierCreate = () => { name="logo" // rules={{required: 'Это поле является обязательным'}} defaultValue={null} - render={({field}) => ( + render={({ field }) => ( option.id === field.value) || null} + value={ + mediaAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } onChange={(_, value) => { - field.onChange(value?.id || '') + field.onChange(value?.id || ""); }} getOptionLabel={(item) => { - return item ? item.media_name : '' + return item ? item.media_name : ""; }} isOptionEqualToValue={(option, value) => { - return option.id === value?.id + return option.id === value?.id; }} - filterOptions={(options, {inputValue}) => { - return options.filter((option) => option.media_name.toLowerCase().includes(inputValue.toLowerCase())) + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> )} /> - ) -} + ); +}); diff --git a/src/pages/carrier/list.tsx b/src/pages/carrier/list.tsx index 2eddc30..bc9b0eb 100644 --- a/src/pages/carrier/list.tsx +++ b/src/pages/carrier/list.tsx @@ -10,11 +10,19 @@ import { import React from "react"; import { observer } from "mobx-react-lite"; import { cityStore } from "../../store/CityStore"; +import { languageStore } from "../../store/LanguageStore"; export const CarrierList = observer(() => { const { city_id } = cityStore; + const { language } = languageStore; + const { dataGridProps } = useDataGrid({ resource: "carrier", + meta: { + headers: { + "Accept-Language": language, + }, + }, filters: { permanent: [ { @@ -167,7 +175,7 @@ export const CarrierList = observer(() => { return ( - + ); }); diff --git a/src/pages/city/list.tsx b/src/pages/city/list.tsx index d477288..32682a7 100644 --- a/src/pages/city/list.tsx +++ b/src/pages/city/list.tsx @@ -1,78 +1,100 @@ -import {type GridColDef} from '@mui/x-data-grid' -import {CustomDataGrid} from '../../components/CustomDataGrid' -import {DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui' -import React from 'react' +import { type GridColDef } from "@mui/x-data-grid"; +import { CustomDataGrid } from "../../components/CustomDataGrid"; +import { + DeleteButton, + EditButton, + List, + ShowButton, + useDataGrid, +} from "@refinedev/mui"; +import React, { useEffect } from "react"; -export const CityList = () => { - const {dataGridProps} = useDataGrid({}) +import { observer } from "mobx-react-lite"; +import { languageStore } from "../../store/LanguageStore"; + +export const CityList = observer(() => { + const { language } = languageStore; + + const { dataGridProps } = useDataGrid({ + resource: "city", + meta: { + headers: { + "Accept-Language": language, + }, + }, + }); const columns = React.useMemo( () => [ { - field: 'id', - headerName: 'ID', - type: 'number', + field: "id", + headerName: "ID", + type: "number", minWidth: 50, - align: 'left', - headerAlign: 'left', + align: "left", + headerAlign: "left", }, { - field: 'country_code', - headerName: 'Код страны', - type: 'string', + field: "country_code", + headerName: "Код страны", + type: "string", minWidth: 150, - align: 'left', - headerAlign: 'left', + align: "left", + headerAlign: "left", }, { - field: 'country', - headerName: 'Cтрана', - type: 'string', + field: "country", + headerName: "Cтрана", + type: "string", minWidth: 150, - align: 'left', - headerAlign: 'left', + align: "left", + headerAlign: "left", }, { - field: 'name', - headerName: 'Название', - type: 'string', + field: "name", + headerName: "Название", + type: "string", minWidth: 150, flex: 1, }, { - field: 'arms', - headerName: 'Герб', - type: 'string', + field: "arms", + headerName: "Герб", + type: "string", flex: 1, }, { - field: 'actions', - headerName: 'Действия', - cellClassName: 'city-actions', + field: "actions", + headerName: "Действия", + cellClassName: "city-actions", minWidth: 120, - display: 'flex', - align: 'right', - headerAlign: 'center', + display: "flex", + align: "right", + headerAlign: "center", sortable: false, filterable: false, disableColumnMenu: true, - renderCell: function render({row}) { + renderCell: function render({ row }) { return ( <> - + - ) + ); }, }, ], - [], - ) + [] + ); return ( - + - ) -} + ); +}); diff --git a/src/pages/country/list.tsx b/src/pages/country/list.tsx index 7bfe487..a592e95 100644 --- a/src/pages/country/list.tsx +++ b/src/pages/country/list.tsx @@ -1,56 +1,82 @@ -import {type GridColDef} from '@mui/x-data-grid' -import {CustomDataGrid} from '../../components/CustomDataGrid' -import {DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui' -import React from 'react' +import { type GridColDef } from "@mui/x-data-grid"; +import { CustomDataGrid } from "../../components/CustomDataGrid"; +import { + DeleteButton, + EditButton, + List, + ShowButton, + useDataGrid, +} from "@refinedev/mui"; +import React from "react"; +import { languageStore } from "../../store/LanguageStore"; +import { observer } from "mobx-react-lite"; -export const CountryList = () => { - const {dataGridProps} = useDataGrid({}) +export const CountryList = observer(() => { + const { language } = languageStore; + + const { dataGridProps } = useDataGrid({ + resource: "country", + meta: { + headers: { + "Accept-Language": language, + }, + }, + }); const columns = React.useMemo( () => [ { - field: 'code', - headerName: 'Код', - type: 'string', + field: "code", + headerName: "Код", + type: "string", minWidth: 150, - align: 'left', - headerAlign: 'left', + align: "left", + headerAlign: "left", }, { - field: 'name', - headerName: 'Название', - type: 'string', + field: "name", + headerName: "Название", + type: "string", minWidth: 150, flex: 1, }, { - field: 'actions', - headerName: 'Действия', - cellClassName: 'country-actions', + field: "actions", + headerName: "Действия", + cellClassName: "country-actions", minWidth: 120, - display: 'flex', - align: 'right', - headerAlign: 'center', + display: "flex", + align: "right", + headerAlign: "center", sortable: false, filterable: false, disableColumnMenu: true, - renderCell: function render({row}) { + renderCell: function render({ row }) { return ( <> - + - ) + ); }, }, ], - [], - ) + [] + ); return ( - row.code} /> + row.code} + /> - ) -} + ); +}); diff --git a/src/pages/sight/create.tsx b/src/pages/sight/create.tsx index 2c7e52a..7c0b76a 100644 --- a/src/pages/sight/create.tsx +++ b/src/pages/sight/create.tsx @@ -8,24 +8,36 @@ import { TOKEN_KEY } from "../../authProvider"; import { observer } from "mobx-react-lite"; import Cookies from "js-cookie"; import { useLocation } from "react-router"; - +import { languageStore } from "../../store/LanguageStore"; export const SightCreate = observer(() => { - const [language, setLanguage] = useState(Cookies.get("lang") || "ru"); + const { language, setLanguageAction } = languageStore; + const [sightData, setSightData] = useState({ + ru: { + name: "", + address: "", + }, + en: { + name: "", + address: "", + }, + zh: { + name: "", + address: "", + }, + }); + // Состояния для предпросмотра const handleLanguageChange = (lang: string) => { - setLanguage(lang); - Cookies.set("lang", lang); + setSightData((prevData) => ({ + ...prevData, + [language]: { + name: watch("name") ?? "", + address: watch("address") ?? "", + }, + })); + setLanguageAction(lang); }; - useEffect(() => { - const lang = Cookies.get("lang")!; - Cookies.set("lang", language); - - return () => { - Cookies.set("lang", lang); - }; - }, [language]); - const { saveButtonProps, refineCore: { formLoading }, @@ -41,6 +53,17 @@ export const SightCreate = observer(() => { }); const { city_id } = cityStore; + useEffect(() => { + if (sightData[language as keyof typeof sightData]?.name) { + setValue("name", sightData[language as keyof typeof sightData]?.name); + } + if (sightData[language as keyof typeof sightData]?.address) { + setValue( + "address", + sightData[language as keyof typeof sightData]?.address + ); + } + }, [sightData, language, setValue]); const [namePreview, setNamePreview] = useState(""); const [coordinatesPreview, setCoordinatesPreview] = useState({ latitude: "", diff --git a/src/pages/sight/edit.tsx b/src/pages/sight/edit.tsx index c19d99e..a71c7f2 100644 --- a/src/pages/sight/edit.tsx +++ b/src/pages/sight/edit.tsx @@ -19,6 +19,8 @@ import { TOKEN_KEY } from "../../authProvider"; import { observer } from "mobx-react-lite"; import { languageStore } from "../../store/LanguageStore"; +import axios from "axios"; +import { LanguageSwitch } from "../../components/LanguageSwitch/index"; function a11yProps(index: number) { return { @@ -53,6 +55,21 @@ export const SightEdit = observer(() => { const { id: sightId } = useParams<{ id: string }>(); const { language, setLanguageAction } = languageStore; + const [sightData, setSightData] = useState({ + ru: { + name: "", + address: "", + }, + en: { + name: "", + address: "", + }, + zh: { + name: "", + address: "", + }, + }); + const { saveButtonProps, register, @@ -71,6 +88,10 @@ export const SightEdit = observer(() => { }, }); + useEffect(() => { + setLanguageAction("ru"); + }, []); + const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({ resource: "city", onSearch: (value) => [ @@ -80,7 +101,20 @@ export const SightEdit = observer(() => { value, }, ], + meta: { + headers: { + "Accept-Language": "ru", + }, + }, }); + const [mediaFile, setMediaFile] = useState<{ + src: string; + filename: string; + }>({ + src: "", + filename: "", + }); + const [tabValue, setTabValue] = useState(0); const { autocompleteProps: mediaAutocompleteProps } = useAutocomplete({ resource: "media", @@ -112,6 +146,18 @@ export const SightEdit = observer(() => { ], }); + useEffect(() => { + if (sightData[language as keyof typeof sightData]?.name) { + setValue("name", sightData[language as keyof typeof sightData]?.name); + } + if (sightData[language as keyof typeof sightData]?.address) { + setValue( + "address", + sightData[language as keyof typeof sightData]?.address || "" + ); + } + }, [language, sightData, setValue]); + useEffect(() => { const latitude = getValues("latitude"); const longitude = getValues("longitude"); @@ -183,6 +229,46 @@ export const SightEdit = observer(() => { }); }, [latitudeContent, longitudeContent]); + useEffect(() => { + const getMedia = async () => { + if (!linkedArticles[selectedArticleIndex]?.id) return; + try { + const response = await axios.get( + `${import.meta.env.VITE_KRBL_API}/article/${ + linkedArticles[selectedArticleIndex].id + }/media`, + { + headers: { + Authorization: `Bearer ${localStorage.getItem(TOKEN_KEY)}`, + }, + } + ); + const media = response.data[0]; + if (media) { + setMediaFile({ + src: `${import.meta.env.VITE_KRBL_MEDIA}${ + media.id + }/download?token=${localStorage.getItem(TOKEN_KEY)}`, + filename: media.filename, + }); + console.log(media); + } else { + setMediaFile({ + src: "", + filename: "", + }); // или другой дефолт + } + } catch (error) { + setMediaFile({ + src: "", + filename: "", + }); // или обработка ошибки + } + }; + + getMedia(); + }, [selectedArticleIndex, linkedArticles]); + useEffect(() => { const selectedCity = cityAutocompleteProps.options.find( (option) => option.id === cityContent @@ -243,6 +329,22 @@ export const SightEdit = observer(() => { setPreviewArticlePreview(selectedPreviewArticle?.heading || ""); }, [previewArticleContent, articleAutocompleteProps.options]); + const handleLanguageChange = (lang: string) => { + setSightData((prevData) => ({ + ...prevData, + [language]: { + name: watch("name") ?? "", + address: watch("address") ?? "", + }, + })); + setLanguageAction(lang); + }; + useEffect(() => { + return () => { + setLanguageAction("ru"); + }; + }, [setLanguageAction]); + return ( @@ -251,13 +353,167 @@ export const SightEdit = observer(() => { onChange={(_, newValue) => setTabValue(newValue)} aria-label="basic tabs example" > - - - + + + + + + + + + + + + type="edit" + parentId={sightId!} + dragAllowed={true} + setItemsParent={setLinkedArticles} + parentResource="sight" + fields={articleFields} + childResource="article" + title="статьи" + /> + + + + + + + theme.palette.mode === "dark" ? "background.paper" : "#fff", + }} + > + + Предпросмотр + + + + {mediaFile.src && ( + <> + {mediaFile.filename.endsWith(".mp4") ? ( + + {/* Водяные знаки */} + + + {selectedArticle && ( + + {selectedArticle.heading} + + )} + + {selectedArticle && ( + + {selectedArticle.body} + + )} + + {/* Координаты */} + + {linkedArticles.map((article, index) => ( + setSelectedArticleIndex(index)} + sx={{ + cursor: "pointer", + bgcolor: + selectedArticleIndex === index + ? "primary.main" + : "transparent", + color: selectedArticleIndex === index ? "white" : "inherit", + p: 1, + borderRadius: 1, + }} + > + + {article.heading} + + + ))} + + + + + + { p: 1, borderRadius: 1, }} - onClick={() => setLanguageAction("ru")} + onClick={() => handleLanguageChange("ru")} > RU @@ -312,7 +568,7 @@ export const SightEdit = observer(() => { p: 1, borderRadius: 1, }} - onClick={() => setLanguageAction("en")} + onClick={() => handleLanguageChange("en")} > EN @@ -327,7 +583,7 @@ export const SightEdit = observer(() => { p: 1, borderRadius: 1, }} - onClick={() => setLanguageAction("zh")} + onClick={() => handleLanguageChange("zh")} > ZH @@ -776,135 +1032,6 @@ export const SightEdit = observer(() => { - - - - - - - - - type="edit" - parentId={sightId!} - setItemsParent={setLinkedArticles} - parentResource="sight" - fields={articleFields} - childResource="article" - title="статьи" - /> - - - - - - - theme.palette.mode === "dark" ? "background.paper" : "#fff", - }} - > - - Предпросмотр - - - {/* Водяные знаки */} - - - {selectedArticle && ( - - {selectedArticle.heading} - - )} - - {selectedArticle && ( - - {selectedArticle.body} - - )} - - {/* Координаты */} - - {linkedArticles.map((article, index) => ( - setSelectedArticleIndex(index)} - sx={{ - cursor: "pointer", - bgcolor: - selectedArticleIndex === index - ? "primary.main" - : "transparent", - color: selectedArticleIndex === index ? "white" : "inherit", - p: 1, - borderRadius: 1, - }} - > - - {article.heading} - - - ))} - - - - { sx={{ flex: 1, display: "flex", flexDirection: "column" }} autoComplete="off" > + { const { city_id } = cityStore; + const { language } = languageStore; + const { dataGridProps } = useDataGrid({ - resource: "sight/", + resource: "sight", + + meta: { + headers: { + "Accept-Language": language, + }, + }, + filters: { permanent: [ { @@ -147,6 +157,7 @@ export const SightList = observer(() => { row.id} diff --git a/src/pages/station/edit.tsx b/src/pages/station/edit.tsx index 96a4fa6..cd9933d 100644 --- a/src/pages/station/edit.tsx +++ b/src/pages/station/edit.tsx @@ -15,6 +15,10 @@ import { Controller } from "react-hook-form"; import { useParams } from "react-router"; import { LinkedItems } from "../../components/LinkedItems"; import { type SightItem, sightFields } from "./types"; +import { observer } from "mobx-react-lite"; +import { languageStore } from "../../store/LanguageStore"; +import { useEffect, useState } from "react"; +import { LanguageSwitch } from "../../components/LanguageSwitch/index"; const TRANSFER_FIELDS = [ { name: "bus", label: "Автобус" }, @@ -28,16 +32,126 @@ const TRANSFER_FIELDS = [ { name: "trolleybus", label: "Троллейбус" }, ]; -export const StationEdit = () => { +export const StationEdit = observer(() => { + const { language, setLanguageAction } = languageStore; + const [stationData, setStationData] = useState({ + ru: { + name: "", + system_name: "", + description: "", + address: "", + latitude: "", + longitude: "", + }, + en: { + name: "", + system_name: "", + description: "", + address: "", + latitude: "", + longitude: "", + }, + zh: { + name: "", + system_name: "", + description: "", + address: "", + latitude: "", + longitude: "", + }, + }); + + const handleLanguageChange = () => { + setStationData((prevData) => ({ + ...prevData, + [language]: { + name: watch("name") ?? "", + system_name: watch("system_name") ?? "", + description: watch("description") ?? "", + address: watch("address") ?? "", + latitude: watch("latitude") ?? "", + longitude: watch("longitude") ?? "", + }, + })); + }; + + const [coordinatesPreview, setCoordinatesPreview] = useState({ + latitude: "", + longitude: "", + }); + const { saveButtonProps, register, control, + getValues, + setValue, + watch, formState: { errors }, - } = useForm({}); + } = useForm({ + refineCoreProps: { + meta: { + headers: { "Accept-Language": language }, + }, + }, + }); + + useEffect(() => { + if (stationData[language as keyof typeof stationData]?.name) { + setValue("name", stationData[language as keyof typeof stationData]?.name); + } + if (stationData[language as keyof typeof stationData]?.address) { + setValue( + "system_name", + stationData[language as keyof typeof stationData]?.system_name || "" + ); + } + if (stationData[language as keyof typeof stationData]?.description) { + setValue( + "description", + stationData[language as keyof typeof stationData]?.description || "" + ); + } + if (stationData[language as keyof typeof stationData]?.latitude) { + setValue( + "latitude", + stationData[language as keyof typeof stationData]?.latitude || "" + ); + } + if (stationData[language as keyof typeof stationData]?.longitude) { + setValue( + "longitude", + stationData[language as keyof typeof stationData]?.longitude || "" + ); + } + }, [language, stationData, setValue]); + + useEffect(() => { + setLanguageAction("ru"); + }, []); const { id: stationId } = useParams<{ id: string }>(); + const handleCoordinatesChange = (e: React.ChangeEvent) => { + const [lat, lon] = e.target.value.split(",").map((s) => s.trim()); + setCoordinatesPreview({ + latitude: lat, + longitude: lon, + }); + setValue("latitude", lat); + setValue("longitude", lon); + }; + + const latitudeContent = watch("latitude"); + const longitudeContent = watch("longitude"); + + useEffect(() => { + setCoordinatesPreview({ + latitude: latitudeContent || "", + longitude: longitudeContent || "", + }); + }, [latitudeContent, longitudeContent]); + const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({ resource: "city", onSearch: (value) => [ @@ -47,8 +161,28 @@ export const StationEdit = () => { value, }, ], + + meta: { + headers: { + "Accept-Language": "ru", + }, + }, + queryOptions: { + queryKey: ["city"], + }, }); + useEffect(() => { + const latitude = getValues("latitude"); + const longitude = getValues("longitude"); + if (latitude && longitude) { + setCoordinatesPreview({ + latitude: latitude, + longitude: longitude, + }); + } + }, [getValues]); + return ( { sx={{ display: "flex", flexDirection: "column" }} autoComplete="off" > + { label={"Адрес"} name="address" /> + - + { )} ); -}; +}); diff --git a/src/pages/station/list.tsx b/src/pages/station/list.tsx index bb831aa..2713010 100644 --- a/src/pages/station/list.tsx +++ b/src/pages/station/list.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useEffect, useMemo } from "react"; import { type GridColDef } from "@mui/x-data-grid"; import { DeleteButton, @@ -12,12 +12,19 @@ import { CustomDataGrid } from "../../components/CustomDataGrid"; import { localeText } from "../../locales/ru/localeText"; import { cityStore } from "../../store/CityStore"; import { observer } from "mobx-react-lite"; +import { languageStore } from "../../store/LanguageStore"; export const StationList = observer(() => { const { city_id } = cityStore; + const { language } = languageStore; const { dataGridProps } = useDataGrid({ resource: "station", + meta: { + headers: { + "Accept-Language": language, + }, + }, filters: { permanent: [ { @@ -160,6 +167,7 @@ export const StationList = observer(() => { row.id} hasCoordinates diff --git a/src/pages/vehicle/edit.tsx b/src/pages/vehicle/edit.tsx index 5c612b9..af57990 100644 --- a/src/pages/vehicle/edit.tsx +++ b/src/pages/vehicle/edit.tsx @@ -1,48 +1,52 @@ -import {Autocomplete, Box, TextField} from '@mui/material' -import {Edit, useAutocomplete} from '@refinedev/mui' -import {useForm} from '@refinedev/react-hook-form' -import {Controller} from 'react-hook-form' +import { Autocomplete, Box, TextField } from "@mui/material"; +import { Edit, useAutocomplete } from "@refinedev/mui"; +import { useForm } from "@refinedev/react-hook-form"; +import { Controller } from "react-hook-form"; -import {VEHICLE_TYPES} from '../../lib/constants' +import { VEHICLE_TYPES } from "../../lib/constants"; type VehicleFormValues = { - tail_number: number - type: number - city_id: number -} + tail_number: number; + type: number; + city_id: number; +}; export const VehicleEdit = () => { const { saveButtonProps, register, control, - formState: {errors}, - } = useForm({}) + formState: { errors }, + } = useForm({}); - const {autocompleteProps: carrierAutocompleteProps} = useAutocomplete({ - resource: 'carrier', + const { autocompleteProps: carrierAutocompleteProps } = useAutocomplete({ + resource: "carrier", onSearch: (value) => [ { - field: 'short_name', - operator: 'contains', + field: "short_name", + operator: "contains", value, }, ], - }) + }); return ( - + { control={control} name="type" rules={{ - required: 'Это поле является обязательным', + required: "Это поле является обязательным", }} defaultValue={null} - render={({field}) => ( + render={({ field }) => ( option.value === field.value) || null} + value={ + VEHICLE_TYPES.find((option) => option.value === field.value) || + null + } onChange={(_, value) => { - field.onChange(value?.value || null) + field.onChange(value?.value || null); }} getOptionLabel={(item) => { - return item ? item.label : '' + return item ? item.label : ""; }} isOptionEqualToValue={(option, value) => { - return option.value === value?.value + return option.value === value?.value; }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> )} /> @@ -76,29 +93,47 @@ export const VehicleEdit = () => { ( + render={({ field }) => ( option.id === field.value) || null} + value={ + carrierAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } onChange={(_, value) => { - field.onChange(value?.id || '') + field.onChange(value?.id || ""); }} getOptionLabel={(item) => { - return item ? item.short_name : '' + return item ? item.short_name : ""; }} isOptionEqualToValue={(option, value) => { - return option.id === value?.id + return option.id === value?.id; }} - filterOptions={(options, {inputValue}) => { - return options.filter((option) => option.short_name.toLowerCase().includes(inputValue.toLowerCase())) + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.short_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> )} /> - ) -} + ); +}; diff --git a/src/pages/vehicle/list.tsx b/src/pages/vehicle/list.tsx index 74d03c5..b3e3722 100644 --- a/src/pages/vehicle/list.tsx +++ b/src/pages/vehicle/list.tsx @@ -1,91 +1,120 @@ -import {type GridColDef} from '@mui/x-data-grid' -import {CustomDataGrid} from '../../components/CustomDataGrid' -import {DeleteButton, EditButton, List, ShowButton, useDataGrid} from '@refinedev/mui' -import React from 'react' -import {VEHICLE_TYPES} from '../../lib/constants' +import { type GridColDef } from "@mui/x-data-grid"; +import { CustomDataGrid } from "../../components/CustomDataGrid"; +import { + DeleteButton, + EditButton, + List, + ShowButton, + useDataGrid, +} from "@refinedev/mui"; +import React, { useEffect } from "react"; +import { VEHICLE_TYPES } from "../../lib/constants"; -import {localeText} from '../../locales/ru/localeText' +import { localeText } from "../../locales/ru/localeText"; +import { observer } from "mobx-react-lite"; +import { languageStore } from "../../store/LanguageStore"; -export const VehicleList = () => { - const {dataGridProps} = useDataGrid({}) +export const VehicleList = observer(() => { + const { language } = languageStore; + + const { dataGridProps } = useDataGrid({ + resource: "vehicle", + meta: { + headers: { + "Accept-Language": language, + }, + }, + }); const columns = React.useMemo( () => [ { - field: 'id', - headerName: 'ID', - type: 'number', + field: "id", + headerName: "ID", + type: "number", minWidth: 70, - display: 'flex', - align: 'left', - headerAlign: 'left', + display: "flex", + align: "left", + headerAlign: "left", }, { - field: 'carrier_id', - headerName: 'ID перевозчика', - type: 'string', + field: "carrier_id", + headerName: "ID перевозчика", + type: "string", minWidth: 150, - display: 'flex', - align: 'left', - headerAlign: 'left', + display: "flex", + align: "left", + headerAlign: "left", }, { - field: 'tail_number', - headerName: 'Бортовой номер', - type: 'number', + field: "tail_number", + headerName: "Бортовой номер", + type: "number", minWidth: 150, - display: 'flex', - align: 'left', - headerAlign: 'left', + display: "flex", + align: "left", + headerAlign: "left", }, { - field: 'type', - headerName: 'Тип', - type: 'string', + field: "type", + headerName: "Тип", + type: "string", minWidth: 200, - display: 'flex', - align: 'left', - headerAlign: 'left', + display: "flex", + align: "left", + headerAlign: "left", renderCell: (params) => { - const value = params.row.type - return VEHICLE_TYPES.find((type) => type.value === value)?.label || value + const value = params.row.type; + return ( + VEHICLE_TYPES.find((type) => type.value === value)?.label || value + ); }, }, { - field: 'city', - headerName: 'Город', - type: 'string', - align: 'left', - headerAlign: 'left', + field: "city", + headerName: "Город", + type: "string", + align: "left", + headerAlign: "left", flex: 1, }, { - field: 'actions', - headerName: 'Действия', + field: "actions", + headerName: "Действия", minWidth: 120, - display: 'flex', - align: 'right', - headerAlign: 'center', + display: "flex", + align: "right", + headerAlign: "center", sortable: false, filterable: false, disableColumnMenu: true, - renderCell: function render({row}) { + renderCell: function render({ row }) { return ( <> - + - ) + ); }, }, ], - [], - ) + [] + ); return ( - row.id} /> + row.id} + /> - ) -} + ); +}); diff --git a/src/store/ArticleStore.ts b/src/store/ArticleStore.ts new file mode 100644 index 0000000..f844e28 --- /dev/null +++ b/src/store/ArticleStore.ts @@ -0,0 +1,20 @@ +import { makeAutoObservable } from "mobx"; + +class ArticleStore { + articleModalOpen: boolean = false; + selectedArticleId: number | null = null; + + constructor() { + makeAutoObservable(this); + } + + setArticleIdAction = (id: number) => { + this.selectedArticleId = id; + }; + + setArticleModalOpenAction = (open: boolean) => { + this.articleModalOpen = open; + }; +} + +export const articleStore = new ArticleStore(); diff --git a/src/store/StationStore.ts b/src/store/StationStore.ts new file mode 100644 index 0000000..5304821 --- /dev/null +++ b/src/store/StationStore.ts @@ -0,0 +1,20 @@ +import { makeAutoObservable } from "mobx"; + +class StationStore { + stationModalOpen: boolean = false; + selectedStationId: number | null = null; + + constructor() { + makeAutoObservable(this); + } + + setStationIdAction = (id: number) => { + this.selectedStationId = id; + }; + + setStationModalOpenAction = (open: boolean) => { + this.stationModalOpen = open; + }; +} + +export const stationStore = new StationStore();