diff --git a/src/App.tsx b/src/App.tsx index 829f113..bb67e71 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -80,7 +80,7 @@ import { KBarProvider, RefineKbar } from "@refinedev/kbar"; function App() { return ( <LoadingProvider> - <HashRouter> + <BrowserRouter> <ColorModeContextProvider> <CssBaseline /> <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} /> @@ -425,7 +425,7 @@ function App() { </DevtoolsProvider> </RefineSnackbarProvider> </ColorModeContextProvider> - </HashRouter> + </BrowserRouter> </LoadingProvider> ); } diff --git a/src/components/LinkedItems.tsx b/src/components/LinkedItems.tsx index bdf7cf8..614eb3f 100644 --- a/src/components/LinkedItems.tsx +++ b/src/components/LinkedItems.tsx @@ -56,6 +56,7 @@ type LinkedItemsProps<T> = { parentResource: string; childResource: string; fields: Field<T>[]; + setItemsParent?: (items: T[]) => void; title: string; type: "show" | "edit"; extraField?: ExtraFieldConfig; @@ -74,6 +75,7 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({ parentId, parentResource, childResource, + setItemsParent, fields, title, dragAllowed = false, @@ -135,6 +137,12 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({ } }, [childResource, availableItems]); + useEffect(() => { + if (setItemsParent) { + setItemsParent(linkedItems); + } + }, [linkedItems, setItemsParent]); + useEffect(() => { // При загрузке linkedItems можно запросить текущие языки для статей if (childResource === "article" && linkedItems.length > 0) { @@ -315,9 +323,7 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({ {field.label} </TableCell> ))} - {childResource === "article" && ( - <TableCell key="language">Язык</TableCell> - )} + {type === "edit" && ( <TableCell width="120px">Действие</TableCell> )} @@ -366,96 +372,7 @@ export const LinkedItems = <T extends { id: number; [key: string]: any }>({ : item[field.data]} </TableCell> ))} - {childResource === "article" && ( - <TableCell> - <Box - display="flex" - justifyContent="center" - alignItems="center" - flexDirection="column" - gap={1} - > - <Box - sx={{ - padding: "4px", - cursor: "pointer", - background: - articleLanguages[item.id] === "RU" - ? theme.palette.primary.main - : "transparent", - color: - articleLanguages[item.id] === "RU" - ? theme.palette.primary.contrastText - : theme.palette.text.primary, - border: - articleLanguages[item.id] !== "RU" - ? `1px solid ${theme.palette.primary.main}` - : "none", - }} - onClick={() => - handleArticleLanguageChange( - item.id, - "RU" - ) - } - > - RU - </Box> - <Box - sx={{ - padding: "4px", - cursor: "pointer", - background: - articleLanguages[item.id] === "EN" - ? theme.palette.primary.main - : "transparent", - color: - articleLanguages[item.id] === "EN" - ? theme.palette.primary.contrastText - : theme.palette.text.primary, - border: - articleLanguages[item.id] !== "EN" - ? `1px solid ${theme.palette.primary.main}` - : "none", - }} - onClick={() => - handleArticleLanguageChange( - item.id, - "EN" - ) - } - > - EN - </Box> - <Box - sx={{ - padding: "4px", - cursor: "pointer", - background: - articleLanguages[item.id] === "ZH" - ? theme.palette.primary.main - : "transparent", - color: - articleLanguages[item.id] === "ZH" - ? theme.palette.primary.contrastText - : theme.palette.text.primary, - border: - articleLanguages[item.id] !== "ZH" - ? `1px solid ${theme.palette.primary.main}` - : "none", - }} - onClick={() => - handleArticleLanguageChange( - item.id, - "ZH" - ) - } - > - ZN - </Box> - </Box> - </TableCell> - )} + {type === "edit" && ( <TableCell> <Button diff --git a/src/pages/article/edit.tsx b/src/pages/article/edit.tsx index b7763a3..6dde00f 100644 --- a/src/pages/article/edit.tsx +++ b/src/pages/article/edit.tsx @@ -2,8 +2,8 @@ import { Box, TextField, Typography, Paper } from "@mui/material"; import { Edit } from "@refinedev/mui"; import { useForm } from "@refinedev/react-hook-form"; import { Controller } from "react-hook-form"; -import { useLocation, useParams } from "react-router"; -import React, { useState, useEffect } from "react"; +import { useParams } from "react-router"; +import React, { useState, useEffect, useMemo } from "react"; import ReactMarkdown from "react-markdown"; import { useList } from "@refinedev/core"; @@ -12,17 +12,13 @@ import { LinkedItems } from "../../components/LinkedItems"; import { MediaItem, mediaFields } from "./types"; import { TOKEN_KEY } from "../../authProvider"; import "easymde/dist/easymde.min.css"; -import Cookies from "js-cookie"; +import { languageStore } from "../../store/LanguageStore"; +import { observer } from "mobx-react-lite"; const MemoizedSimpleMDE = React.memo(MarkdownEditor); -export const ArticleEdit = () => { - // const [initialLanguage] = useState(Cookies.get("lang")!); - // const { pathname } = useLocation(); +export const ArticleEdit = observer(() => { + const { language, setLanguageAction } = languageStore; - // useEffect(() => { - // Cookies.set("lang", initialLanguage); - // }, [pathname]); - const [language, setLanguage] = useState(Cookies.get("lang") || "ru"); const [articleData, setArticleData] = useState<{ ru: { heading: string; body: string }; en: { heading: string; body: string }; @@ -35,7 +31,7 @@ export const ArticleEdit = () => { const { id: articleId } = useParams<{ id: string }>(); const [preview, setPreview] = useState(""); const [headingPreview, setHeadingPreview] = useState(""); - const simpleMDEOptions = React.useMemo( + const simpleMDEOptions = useMemo( () => ({ placeholder: "Введите контент в формате Markdown...", spellChecker: false, @@ -51,7 +47,7 @@ export const ArticleEdit = () => { watch, formState: { errors }, setValue, - } = useForm({ + } = useForm<{ heading: string; body: string }>({ refineCoreProps: { meta: { headers: { @@ -61,14 +57,6 @@ export const ArticleEdit = () => { }, }); - useEffect(() => { - const lang = Cookies.get("lang")!; - Cookies.set("lang", language); - return () => { - Cookies.set("lang", lang); - }; - }, [language]); - useEffect(() => { setValue( "heading", @@ -88,12 +76,11 @@ export const ArticleEdit = () => { setArticleData((prevData) => ({ ...prevData, [language]: { - heading: watch("heading") || "", - body: watch("body") || "", + heading: watch("heading") ?? "", + body: watch("body") ?? "", }, })); - setLanguage(lang); - Cookies.set("lang", lang); + setLanguageAction(lang); }; const bodyContent = watch("body"); @@ -107,17 +94,16 @@ export const ArticleEdit = () => { setHeadingPreview(headingContent || ""); }, [headingContent]); - const onSubmit = (data: { heading: string; body: string }) => { - // Здесь вы будете отправлять данные на сервер, - // учитывая текущий язык (language) - console.log("Данные для сохранения:", data, language); - // ... ваша логика сохранения ... - }; - const { data: mediaData } = useList<MediaItem>({ resource: `article/${articleId}/media`, }); + useEffect(() => { + return () => { + setLanguageAction("ru"); + }; + }, [setLanguageAction]); + return ( <Edit saveButtonProps={saveButtonProps}> <Box sx={{ display: "flex", gap: 2 }}> @@ -186,8 +172,8 @@ export const ArticleEdit = () => { {...register("heading", { required: "Это поле является обязательным", })} - error={!!(errors as any)?.heading} - helperText={(errors as any)?.heading?.message} + error={!!errors?.heading} + helperText={errors?.heading?.message as string} margin="normal" fullWidth InputLabelProps={{ shrink: true }} @@ -347,4 +333,4 @@ export const ArticleEdit = () => { </Box> </Edit> ); -}; +}); diff --git a/src/pages/sight/create.tsx b/src/pages/sight/create.tsx index 24d819d..2c7e52a 100644 --- a/src/pages/sight/create.tsx +++ b/src/pages/sight/create.tsx @@ -390,7 +390,7 @@ export const SightCreate = observer(() => { renderInput={(params) => ( <TextField {...params} - label="Выберите обложку" + label="Выберите логотип достопримечательности" margin="normal" variant="outlined" error={!!errors.thumbnail} @@ -559,7 +559,7 @@ export const SightCreate = observer(() => { renderInput={(params) => ( <TextField {...params} - label="Cтатья-предпросмотр" + label="Медиа-предпросмотр" margin="normal" variant="outlined" error={!!errors.preview_article} @@ -661,12 +661,12 @@ export const SightCreate = observer(() => { gutterBottom sx={{ color: "text.secondary" }} > - Обложка: + Логотип достопримечательности: </Typography> <Box component="img" src={thumbnailPreview} - alt="Обложка" + alt="Логотип" sx={{ maxWidth: "100%", height: "40vh", diff --git a/src/pages/sight/edit.tsx b/src/pages/sight/edit.tsx index 514e2b6..c19d99e 100644 --- a/src/pages/sight/edit.tsx +++ b/src/pages/sight/edit.tsx @@ -104,6 +104,11 @@ export const SightEdit = observer(() => { operator: "contains", value, }, + { + field: "media_type", + operator: "contains", + value, + }, ], }); @@ -134,6 +139,7 @@ export const SightEdit = observer(() => { latitude: "", longitude: "", }); + const [selectedArticleIndex, setSelectedArticleIndex] = useState(0); const [cityPreview, setCityPreview] = useState(""); const [thumbnailPreview, setThumbnailPreview] = useState<string | null>(null); const [watermarkLUPreview, setWatermarkLUPreview] = useState<string | null>( @@ -144,9 +150,10 @@ export const SightEdit = observer(() => { ); const [leftArticlePreview, setLeftArticlePreview] = useState(""); const [previewArticlePreview, setPreviewArticlePreview] = useState(""); - + const [linkedArticles, setLinkedArticles] = useState<ArticleItem[]>([]); // Следим за изменениями во всех полях - const coordinatesContent = watch("coordinates"); + const selectedArticle = linkedArticles[selectedArticleIndex]; + const addressContent = watch("address"); const nameContent = watch("name"); const latitudeContent = watch("latitude"); @@ -163,6 +170,12 @@ export const SightEdit = observer(() => { setNamePreview(nameContent || ""); }, [nameContent]); + useEffect(() => { + return () => { + setLanguageAction("ru"); + }; + }, []); + useEffect(() => { setCoordinatesPreview({ latitude: latitudeContent || "", @@ -245,13 +258,22 @@ export const SightEdit = observer(() => { </Box> <CustomTabPanel value={tabValue} index={0}> - <Edit saveButtonProps={saveButtonProps}> - <Box sx={{ display: "flex", gap: 2 }}> + <Edit + saveButtonProps={saveButtonProps} + footerButtonProps={{ + sx: { + bottom: 0, + left: 0, + }, + }} + > + <Box sx={{ display: "flex", gap: 2, position: "relative" }}> <Box sx={{ display: "flex", flexDirection: "column", flex: 1, + maxWidth: "50%", gap: 10, justifyContent: "space-between", }} @@ -330,17 +352,7 @@ export const SightEdit = observer(() => { label={"Название *"} name="name" /> - <TextField - value={`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`} - onChange={handleCoordinatesChange} - error={!!(errors as any)?.latitude} - helperText={(errors as any)?.latitude?.message} - margin="normal" - fullWidth - InputLabelProps={{ shrink: true }} - type="text" - label={"Координаты *"} - /> + <input type="hidden" {...register("longitude", { @@ -469,16 +481,18 @@ export const SightEdit = observer(() => { return option.id === value?.id; }} filterOptions={(options, { inputValue }) => { - return options.filter((option) => - option.media_name - .toLowerCase() - .includes(inputValue.toLowerCase()) + return options.filter( + (option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) && + option.media_type === 3 ); }} renderInput={(params) => ( <TextField {...params} - label="Выберите обложку" + label="Выберите логотип достопримечательности" margin="normal" variant="outlined" error={!!errors.arms} @@ -490,91 +504,94 @@ export const SightEdit = observer(() => { )} /> - <Controller - control={control} - name="watermark_lu" - defaultValue={null} - render={({ field }) => ( - <Autocomplete - {...mediaAutocompleteProps} - value={ - mediaAutocompleteProps.options.find( - (option) => option.id === field.value - ) || null - } - onChange={(_, value) => { - field.onChange(value?.id || ""); - }} - getOptionLabel={(item) => { - return item ? item.media_name : ""; - }} - isOptionEqualToValue={(option, value) => { - return option.id === value?.id; - }} - filterOptions={(options, { inputValue }) => { - return options.filter((option) => - option.media_name - .toLowerCase() - .includes(inputValue.toLowerCase()) - ); - }} - renderInput={(params) => ( - <TextField - {...params} - label="Выберите водный знак (Левый верх)" - margin="normal" - variant="outlined" - error={!!errors.arms} - helperText={(errors as any)?.arms?.message} - required - /> - )} - /> - )} - /> - - <Controller - control={control} - name="watermark_rd" - defaultValue={null} - render={({ field }) => ( - <Autocomplete - {...mediaAutocompleteProps} - value={ - mediaAutocompleteProps.options.find( - (option) => option.id === field.value - ) || null - } - onChange={(_, value) => { - field.onChange(value?.id || ""); - }} - getOptionLabel={(item) => { - return item ? item.media_name : ""; - }} - isOptionEqualToValue={(option, value) => { - return option.id === value?.id; - }} - filterOptions={(options, { inputValue }) => { - return options.filter((option) => - option.media_name - .toLowerCase() - .includes(inputValue.toLowerCase()) - ); - }} - renderInput={(params) => ( - <TextField - {...params} - label="Выберите водный знак (Правый вверх)" - margin="normal" - variant="outlined" - error={!!errors.arms} - helperText={(errors as any)?.arms?.message} - required - /> - )} - /> - )} - /> + <Box sx={{ display: "none" }}> + <Controller + control={control} + name="watermark_lu" + defaultValue={null} + render={({ field }) => ( + <Autocomplete + {...mediaAutocompleteProps} + value={ + mediaAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } + onChange={(_, value) => { + field.onChange(value?.id || ""); + }} + getOptionLabel={(item) => { + return item ? item.media_name : ""; + }} + isOptionEqualToValue={(option, value) => { + return option.id === value?.id; + }} + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); + }} + renderInput={(params) => ( + <TextField + {...params} + label="Выберите водный знак (Левый верх)" + margin="normal" + variant="outlined" + error={!!errors.arms} + helperText={(errors as any)?.arms?.message} + required + /> + )} + /> + )} + /> + </Box> + <Box sx={{ display: "none" }}> + <Controller + control={control} + name="watermark_rd" + defaultValue={null} + render={({ field }) => ( + <Autocomplete + {...mediaAutocompleteProps} + value={ + mediaAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } + onChange={(_, value) => { + field.onChange(value?.id || ""); + }} + getOptionLabel={(item) => { + return item ? item.media_name : ""; + }} + isOptionEqualToValue={(option, value) => { + return option.id === value?.id; + }} + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); + }} + renderInput={(params) => ( + <TextField + {...params} + label="Выберите водный знак (Правый вверх)" + margin="normal" + variant="outlined" + error={!!errors.arms} + helperText={(errors as any)?.arms?.message} + required + /> + )} + /> + )} + /> + </Box> <Controller control={control} @@ -650,7 +667,7 @@ export const SightEdit = observer(() => { renderInput={(params) => ( <TextField {...params} - label="Cтатья-предпросмотр" + label="Медиа-предпросмотр" margin="normal" variant="outlined" error={!!errors.arms} @@ -667,11 +684,14 @@ export const SightEdit = observer(() => { {/* Блок предпросмотра */} <Paper sx={{ - flex: 1, + position: "fixed", p: 2, + width: "30%", - position: "sticky", - top: 16, + top: "179px", + + right: 50, + zIndex: 1000, borderRadius: 2, border: "1px solid", borderColor: "primary.main", @@ -686,8 +706,8 @@ export const SightEdit = observer(() => { {/* Название достопримечательности */} <Typography variant="h4" - gutterBottom sx={{ + wordWrap: "break-word", color: (theme) => theme.palette.mode === "dark" ? "grey.300" : "grey.800", mb: 3, @@ -728,22 +748,6 @@ export const SightEdit = observer(() => { </Box> </Typography> - {/* Координаты */} - <Typography variant="body1" sx={{ mb: 2 }}> - <Box component="span" sx={{ color: "text.secondary" }}> - Координаты:{" "} - </Box> - <Box - component="span" - sx={{ - color: (theme) => - theme.palette.mode === "dark" ? "grey.300" : "grey.800", - }} - > - {`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`} - </Box> - </Typography> - {/* Обложка */} {thumbnailPreview && ( <Box sx={{ mb: 2 }}> @@ -752,12 +756,12 @@ export const SightEdit = observer(() => { gutterBottom sx={{ color: "text.secondary" }} > - Обложка: + Логотип достопримечательности: </Typography> <Box component="img" src={thumbnailPreview} - alt="Обложка" + alt="Логотип" sx={{ maxWidth: "100%", height: "40vh", @@ -768,6 +772,282 @@ export const SightEdit = observer(() => { /> </Box> )} + </Paper> + </Box> + </Edit> + </CustomTabPanel> + <CustomTabPanel value={tabValue} index={1}> + <Edit + saveButtonProps={saveButtonProps} + footerButtonProps={{ + sx: { + bottom: 0, + left: 0, + }, + }} + > + <Box + sx={{ + maxWidth: "50%", + display: "flex", + flexDirection: "column", + gap: 2, + position: "relative", + }} + > + <Box + component="form" + sx={{ flex: 1, display: "flex", flexDirection: "column" }} + autoComplete="off" + > + <TextField + {...register("name", { + required: "Это поле является обязательным", + })} + error={!!(errors as any)?.name} + helperText={(errors as any)?.name?.message} + margin="normal" + fullWidth + InputLabelProps={{ shrink: true }} + type="text" + label={"Название *"} + name="name" + /> + </Box> + <Box sx={{ mt: 3 }}> + <LinkedItems<ArticleItem> + type="edit" + parentId={sightId!} + setItemsParent={setLinkedArticles} + parentResource="sight" + fields={articleFields} + childResource="article" + title="статьи" + /> + + <CreateSightArticle + parentId={sightId!} + parentResource="sight" + childResource="article" + title="статью" + /> + </Box> + </Box> + </Edit> + <Paper + sx={{ + position: "fixed", + p: 2, + + width: "30%", + + top: "178px", + + right: 50, + zIndex: 1000, + borderRadius: 2, + border: "1px solid", + borderColor: "primary.main", + bgcolor: (theme) => + theme.palette.mode === "dark" ? "background.paper" : "#fff", + }} + > + <Typography variant="h6" gutterBottom color="primary"> + Предпросмотр + </Typography> + + {/* Водяные знаки */} + <Box sx={{ mb: 2 }}> + <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}> + {selectedArticle && ( + <Typography + variant="h4" + gutterBottom + sx={{ color: "text.primary" }} + > + {selectedArticle.heading} + </Typography> + )} + + {selectedArticle && ( + <Typography + variant="body1" + gutterBottom + sx={{ color: "text.primary" }} + > + {selectedArticle.body} + </Typography> + )} + </Box> + {/* Координаты */} + <Box sx={{ display: "flex", gap: 1, mt: 2 }}> + {linkedArticles.map((article, index) => ( + <Box + key={article.id} + onClick={() => setSelectedArticleIndex(index)} + sx={{ + cursor: "pointer", + bgcolor: + selectedArticleIndex === index + ? "primary.main" + : "transparent", + color: selectedArticleIndex === index ? "white" : "inherit", + p: 1, + borderRadius: 1, + }} + > + <Typography variant="body1" gutterBottom> + {article.heading} + </Typography> + </Box> + ))} + </Box> + </Box> + </Paper> + </CustomTabPanel> + <CustomTabPanel value={tabValue} index={2}> + <Edit + saveButtonProps={saveButtonProps} + footerButtonProps={{ + sx: { + bottom: 0, + left: 0, + }, + }} + > + <Box + sx={{ + maxWidth: "50%", + display: "flex", + gap: 2, + position: "relative", + }} + > + <Box + component="form" + sx={{ flex: 1, display: "flex", flexDirection: "column" }} + autoComplete="off" + > + <Controller + control={control} + name="watermark_lu" + defaultValue={null} + render={({ field }) => ( + <Autocomplete + {...mediaAutocompleteProps} + value={ + mediaAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } + onChange={(_, value) => { + field.onChange(value?.id || ""); + }} + getOptionLabel={(item) => { + return item ? item.media_name : ""; + }} + isOptionEqualToValue={(option, value) => { + return option.id === value?.id; + }} + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); + }} + renderInput={(params) => ( + <TextField + {...params} + label="Выберите водный знак (Левый верх)" + margin="normal" + variant="outlined" + error={!!errors.arms} + helperText={(errors as any)?.arms?.message} + required + /> + )} + /> + )} + /> + + <Controller + control={control} + name="watermark_rd" + defaultValue={null} + render={({ field }) => ( + <Autocomplete + {...mediaAutocompleteProps} + value={ + mediaAutocompleteProps.options.find( + (option) => option.id === field.value + ) || null + } + onChange={(_, value) => { + field.onChange(value?.id || ""); + }} + getOptionLabel={(item) => { + return item ? item.media_name : ""; + }} + isOptionEqualToValue={(option, value) => { + return option.id === value?.id; + }} + filterOptions={(options, { inputValue }) => { + return options.filter((option) => + option.media_name + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); + }} + renderInput={(params) => ( + <TextField + {...params} + label="Выберите водный знак (Правый вверх)" + margin="normal" + variant="outlined" + error={!!errors.arms} + helperText={(errors as any)?.arms?.message} + required + /> + )} + /> + )} + /> + + <TextField + value={`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`} + onChange={handleCoordinatesChange} + error={!!(errors as any)?.latitude} + helperText={(errors as any)?.latitude?.message} + margin="normal" + fullWidth + InputLabelProps={{ shrink: true }} + type="text" + label={"Координаты *"} + /> + </Box> + + <Paper + sx={{ + position: "fixed", + p: 2, + + width: "30%", + + top: "178px", + + right: 50, + zIndex: 1000, + borderRadius: 2, + border: "1px solid", + borderColor: "primary.main", + bgcolor: (theme) => + theme.palette.mode === "dark" ? "background.paper" : "#fff", + }} + > + <Typography variant="h6" gutterBottom color="primary"> + Предпросмотр + </Typography> {/* Водяные знаки */} <Box sx={{ mb: 2 }}> @@ -828,87 +1108,29 @@ export const SightEdit = observer(() => { </Box> )} </Box> - </Box> - - {/* Связанные статьи */} - <Box> - {/* <Typography variant="body1" gutterBottom sx={{color: 'text.secondary'}}> - Связанные статьи: - </Typography> */} - {leftArticlePreview && ( - <Typography variant="body1" gutterBottom> - <Box component="span" sx={{ color: "text.secondary" }}> - Левая статья:{" "} - </Box> - <Box - component={Link} - to={`/article/show/${watch("left_article")}`} - sx={{ - color: (theme) => - theme.palette.mode === "dark" - ? "grey.300" - : "grey.800", - textDecoration: "none", - "&:hover": { - textDecoration: "underline", - }, - }} - > - {leftArticlePreview} - </Box> - </Typography> - )} - {previewArticlePreview && ( - <Typography variant="body1" gutterBottom> - <Box component="span" sx={{ color: "text.secondary" }}> - Статья-предпросмотр:{" "} - </Box> - <Box - component={Link} - to={`/article/show/${watch("preview_article")}`} - sx={{ - color: (theme) => - theme.palette.mode === "dark" - ? "grey.300" - : "grey.800", - textDecoration: "none", - "&:hover": { - textDecoration: "underline", - }, - }} - > - {previewArticlePreview} - </Box> - </Typography> - )} + {/* Координаты */} + <Typography + variant="body1" + sx={{ display: "flex", flexDirection: "column", mb: 2 }} + > + <Box component="span" sx={{ color: "text.secondary" }}> + Координаты:{" "} + </Box> + <Box + component="span" + sx={{ + color: (theme) => + theme.palette.mode === "dark" ? "grey.300" : "grey.800", + }} + > + {`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`} + </Box> + </Typography> </Box> </Paper> </Box> - <Box sx={{ mt: 3 }}> - <LinkedItems<ArticleItem> - type="edit" - parentId={sightId!} - parentResource="sight" - childResource="article" - fields={articleFields} - title="статьи" - /> - - <CreateSightArticle - parentId={sightId!} - parentResource="sight" - childResource="article" - title="статью" - /> - </Box> </Edit> </CustomTabPanel> - <CustomTabPanel value={tabValue} index={1}> - 1 - </CustomTabPanel> - <CustomTabPanel value={tabValue} index={2}> - 2 - </CustomTabPanel> </Box> ); }); diff --git a/src/pages/station/edit.tsx b/src/pages/station/edit.tsx index 6523772..96a4fa6 100644 --- a/src/pages/station/edit.tsx +++ b/src/pages/station/edit.tsx @@ -195,58 +195,6 @@ export const StationEdit = () => { /> )} /> - - <TextField - {...register("offset_x", { - // required: 'Это поле является обязательным', - })} - 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" - /> - - <TextField - {...register("offset_y", { - // required: 'Это поле является обязательным', - })} - 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" - /> - - {/* Группа полей пересадок */} - <Paper sx={{ p: 2, mt: 2 }}> - <Typography variant="h6" gutterBottom> - Пересадки - </Typography> - <Grid container spacing={2}> - {TRANSFER_FIELDS.map((field) => ( - <Grid item xs={12} sm={6} md={4} key={field.name}> - <TextField - {...register(`transfers.${field.name}`)} - error={!!(errors as any)?.transfers?.[field.name]} - helperText={(errors as any)?.transfers?.[field.name]?.message} - margin="normal" - fullWidth - InputLabelProps={{ shrink: true }} - type="text" - label={field.label} - name={`transfers.${field.name}`} - /> - </Grid> - ))} - </Grid> - </Paper> </Box> {stationId && ( diff --git a/src/pages/station/list.tsx b/src/pages/station/list.tsx index 70f1f9b..bb831aa 100644 --- a/src/pages/station/list.tsx +++ b/src/pages/station/list.tsx @@ -7,7 +7,7 @@ import { ShowButton, useDataGrid, } from "@refinedev/mui"; -import { Stack } from "@mui/material"; +import { Stack, Typography } from "@mui/material"; import { CustomDataGrid } from "../../components/CustomDataGrid"; import { localeText } from "../../locales/ru/localeText"; import { cityStore } from "../../store/CityStore"; @@ -58,6 +58,19 @@ export const StationList = observer(() => { align: "left", headerAlign: "left", }, + { + field: "direction", + headerName: "Направление", + type: "boolean", + minWidth: 200, + display: "flex", + + renderCell: ({ value }) => ( + <Typography style={{ color: value ? "#48989f" : "#7f6b58" }}> + {value ? "прямой" : "обратный"} + </Typography> + ), + }, { field: "latitude", headerName: "Широта", diff --git a/src/pages/station/types.ts b/src/pages/station/types.ts index 5cd0d02..5f5896d 100644 --- a/src/pages/station/types.ts +++ b/src/pages/station/types.ts @@ -29,6 +29,7 @@ export const stationFields: Array<FieldType<StationItem>> = [ // {label: 'ID', data: 'id'}, { label: "Название", data: "name" }, { label: "Системное название", data: "system_name" }, + // { label: "Направление", data: "direction" }, { label: "Адрес", data: "address" }, // {label: 'Широта', data: 'latitude'}, // {label: 'Долгота', data: 'longitude'}, diff --git a/src/providers/data.ts b/src/providers/data.ts index 6f5284b..6706abd 100644 --- a/src/providers/data.ts +++ b/src/providers/data.ts @@ -17,7 +17,7 @@ axiosInstance.interceptors.request.use((config) => { // Добавляем язык в кастомный заголовок - config.headers["X-Language"] = "ru"; + config.headers["X-Language"] = languageStore.language; console.log("Request headers:", config.headers); diff --git a/src/store/CityStore.ts b/src/store/CityStore.ts index c587d73..5b02763 100644 --- a/src/store/CityStore.ts +++ b/src/store/CityStore.ts @@ -1,7 +1,7 @@ import { makeAutoObservable } from "mobx"; class CityStore { - city_id: string = ""; + city_id: string = "0"; constructor() { makeAutoObservable(this); @@ -9,7 +9,12 @@ class CityStore { } initialize() { - this.city_id = localStorage.getItem("city_id") ?? ""; + const id = localStorage.getItem("city_id"); + if (id) { + this.city_id = id; + } else { + this.city_id = "0"; + } } setCityIdAction = (city_id: string) => {