changes for 15.05

This commit is contained in:
Spynder 2025-05-17 05:55:57 +03:00
parent ab1fd6b22a
commit 34423b73a3
12 changed files with 212 additions and 128 deletions

View File

@ -162,9 +162,7 @@ export const CreateSightArticle = ({
// Получаем существующие статьи для определения порядкового номера
const existingItemsResponse = await axiosInstance.get(
`${
import.meta.env.VITE_KRBL_API
}/${parentResource}/${parentId}/${childResource}`
`${import.meta.env.VITE_KRBL_API}/${parentResource}/${parentId}/${childResource}`
);
const existingItems = existingItemsResponse.data ?? [];
const nextPageNum = existingItems.length + 1;
@ -172,14 +170,26 @@ export const CreateSightArticle = ({
if (!left) {
// Привязываем статью к достопримечательности если она не левая
await axiosInstance.post(
`${
import.meta.env.VITE_KRBL_API
}/${parentResource}/${parentId}/${childResource}/`,
`${import.meta.env.VITE_KRBL_API}/${parentResource}/${parentId}/${childResource}/`,
{
[`${childResource}_id`]: itemId,
page_num: nextPageNum,
}
);
} else {
const response = await axiosInstance.get(
`${import.meta.env.VITE_KRBL_API}/${parentResource}/${parentId}/`
);
const data = response.data;
if(data) {
await axiosInstance.patch(
`${import.meta.env.VITE_KRBL_API}/${parentResource}/${parentId}/`,
{
...data,
left_article: itemId
}
);
}
}
// Загружаем все медиа файлы и получаем их ID
@ -220,7 +230,7 @@ export const CreateSightArticle = ({
};
return (
<Box component="form" onSubmit={handleSubmitItem(handleCreate)}>
<Box>
<TextField
{...registerItem("heading", {
required: "Это поле является обязательным",
@ -335,7 +345,7 @@ export const CreateSightArticle = ({
</Box>
<Box sx={{ mt: 2, display: "flex", gap: 2 }}>
<Button variant="contained" color="primary" type="submit">
<Button variant="contained" color="primary" onClick={handleSubmitItem(handleCreate)}>
Создать
</Button>
<Button

View File

@ -69,6 +69,7 @@ type LinkedItemsProps<T> = {
dontRecurse?: boolean;
disableCreation?: boolean;
updatedLinkedItems?: T[];
refresh?: number;
};
const reorder = (list: any[], startIndex: number, endIndex: number) => {
@ -126,7 +127,8 @@ export const LinkedItemsContents = <T extends { id: number; [key: string]: any }
type,
onUpdate,
disableCreation = false,
updatedLinkedItems
updatedLinkedItems,
refresh
}: LinkedItemsProps<T>) => {
const { language } = languageStore;
const { setArticleModalOpenAction, setArticleIdAction } = articleStore;
@ -207,7 +209,7 @@ export const LinkedItemsContents = <T extends { id: number; [key: string]: any }
setLinkedItems([]);
});
}
}, [parentId, parentResource, childResource, language]);
}, [parentId, parentResource, childResource, language, refresh]);
useEffect(() => {
if (type === "edit") {

View File

@ -7,7 +7,7 @@ import "easymde/dist/easymde.min.css";
import { memo, useMemo, useEffect, useCallback, useState } from "react";
import { MarkdownEditor } from "../../MarkdownEditor";
import { Edit } from "@refinedev/mui";
import { languageStore } from "../../../store/LanguageStore";
import { EVERY_LANGUAGE, languageStore } from "../../../store/LanguageStore";
import { LanguageSwitch } from "../../LanguageSwitch/index";
import { useDropzone } from "react-dropzone";
import {
@ -24,7 +24,7 @@ type MediaFile = {
file: File;
preview: string;
uploading: boolean;
mediaId?: number;
media_id?: number;
};
const style = {
@ -42,26 +42,18 @@ const style = {
};
export const ArticleEditModal = observer(() => {
const { language } = languageStore;
const [articleData, setArticleData] = useState({
ru: {
heading: "",
body: "",
},
en: {
heading: "",
body: "",
},
zh: {
heading: "",
body: "",
},
heading: EVERY_LANGUAGE(language),
body: EVERY_LANGUAGE(language),
});
const { articleModalOpen, setArticleModalOpenAction, selectedArticleId } =
articleStore;
const { language } = languageStore;
const [mediaFiles, setMediaFiles] = useState<MediaFile[]>([]);
const [refresh, setRefresh] = useState(0);
useEffect(() => {
return () => {
setArticleModalOpenAction(false);
@ -103,6 +95,7 @@ export const ArticleEditModal = observer(() => {
);
setMediaFiles(mediaFiles);
setRefresh(refresh+1);
} catch (error) {
console.error("Error loading existing media:", error);
}
@ -131,7 +124,8 @@ export const ArticleEditModal = observer(() => {
onMutationSuccess: async () => {
try {
// Upload new media files
const newMediaFiles = mediaFiles.filter((file) => !file.mediaId);
const newMediaFiles = mediaFiles.filter((file) => !file.media_id);
const existingMediaAmount = mediaFiles.filter((file) => file.media_id).length;
const mediaIds = await Promise.all(
newMediaFiles.map(async (mediaFile) => {
return await uploadMedia(mediaFile);
@ -147,7 +141,7 @@ export const ArticleEditModal = observer(() => {
}/article/${selectedArticleId}/media/`,
{
media_id: mediaId,
media_order: index + 1,
media_order: index + existingMediaAmount + 1,
}
)
)
@ -169,27 +163,25 @@ export const ArticleEditModal = observer(() => {
});
useEffect(() => {
if (articleData[language as keyof typeof articleData]?.heading) {
setValue(
"heading",
articleData[language as keyof typeof articleData]?.heading || ""
);
if (articleData.heading[language]) {
setValue("heading", articleData.heading[language])
}
if (articleData[language as keyof typeof articleData]?.body) {
setValue(
"body",
articleData[language as keyof typeof articleData]?.body || ""
);
if (articleData.body[language]) {
setValue("body", articleData.body[language])
}
}, [language, articleData, setValue]);
const handleLanguageChange = () => {
setArticleData((prevData) => ({
...prevData,
[language]: {
heading: watch("heading") || "",
body: watch("body") || "",
heading: {
...prevData.heading,
[language]: watch("heading") ?? ""
},
body: {
...prevData.body,
[language]: watch("body") ?? ""
}
}));
};
@ -240,10 +232,10 @@ export const ArticleEditModal = observer(() => {
const mediaFile = mediaFiles[index];
// If it's an existing media file (has mediaId), delete it from the server
if (mediaFile.mediaId) {
if (mediaFile.media_id) {
try {
await axiosInstance.delete(
`${import.meta.env.VITE_KRBL_API}/media/${mediaFile.mediaId}`
`${import.meta.env.VITE_KRBL_API}/media/${mediaFile.media_id}`
);
} catch (error) {
console.error("Error deleting media:", error);

View File

@ -13,7 +13,7 @@ export function MediaView({media} : Readonly<{media?: MediaData}>) {
const token = localStorage.getItem(TOKEN_KEY);
return (
<Box
sx={{maxHeight: "300px", width: "100%", display: "flex", justifyContent: "center"}}
sx={{maxHeight: "300px", width: "100%", display: "flex", flexGrow: 1, justifyContent: "center"}}
>
{media?.media_type === 1 && (
<img

View File

@ -23,8 +23,6 @@ export const ArticleEdit = observer(() => {
body: EVERY_LANGUAGE("")
});
const { id: articleId } = useParams<{ id: string }>();
const [preview, setPreview] = useState("");
const [headingPreview, setHeadingPreview] = useState("");
const simpleMDEOptions = useMemo(
() => ({
placeholder: "Введите контент в формате Markdown...",
@ -42,6 +40,7 @@ export const ArticleEdit = observer(() => {
watch,
formState: { errors },
setValue,
getValues,
} = useForm<{ heading: string; body: string }>({
refineCoreProps: META_LANGUAGE(language)
});
@ -50,15 +49,21 @@ export const ArticleEdit = observer(() => {
const headingContent = watch("heading");
useEffect(() => {
setValue("heading", articleData.heading[language] ?? "");
setHeadingPreview(articleData.heading[language] ?? "");
setValue("body", articleData.body[language] ?? "");
setPreview(articleData.body[language] ?? "");
}, [language, articleData, setValue]);
console.log(articleData)
}, [articleData])
useEffect(() => {
console.log("Trying to udpate")
//setHeadingPreview(articleData.heading[language] ?? "");
//setPreview(articleData.body[language] ?? "");
if(articleData.heading[language])
setValue("heading", articleData.heading[language]);
if(articleData.body[language])
setValue("body", articleData.body[language]);
}, [language]);
function updateTranslations(update: boolean = true) {
const newArticleData = {
...articleData,
heading: {
...articleData.heading,
[language]: watch("heading") ?? "",
@ -75,25 +80,16 @@ export const ArticleEdit = observer(() => {
const handleLanguageChange = (lang: Languages) => {
updateTranslations();
setLanguageAction(lang);
console.log("Setting preview to", articleData.body[lang] ?? "")
};
const handleFormSubmit = handleSubmit((values: FieldValues) => {
const newTranslations = updateTranslations(false);
console.log(newTranslations);
return onFinish({
translations: newTranslations
translations: updateTranslations(false)
});
});
useEffect(() => {
setPreview(bodyContent ?? "");
}, [bodyContent]);
useEffect(() => {
setHeadingPreview(headingContent ?? "");
}, [headingContent]);
const { data: mediaData } = useList<MediaItem>({
const { data: mediaData, refetch } = useList<MediaItem>({
resource: `article/${articleId}/media`,
});
@ -156,6 +152,7 @@ export const ArticleEdit = observer(() => {
childResource="media"
fields={mediaFields}
title="медиа"
onUpdate={refetch}
/>
)}
</Box>
@ -191,7 +188,7 @@ export const ArticleEdit = observer(() => {
mb: 3,
}}
>
{headingPreview}
{headingContent}
</Typography>
{/* Markdown контент */}
@ -235,7 +232,7 @@ export const ArticleEdit = observer(() => {
},
}}
>
<ReactMarkdown>{preview}</ReactMarkdown>
<ReactMarkdown>{bodyContent}</ReactMarkdown>
</Box>
{/* Привязанные медиа */}
@ -256,8 +253,10 @@ export const ArticleEdit = observer(() => {
<Box
key={media.id}
sx={{
width: 120,
height: 120,
display: "flex",
width: "45%",
height: "45%",
aspectRatio: "1/1",
borderRadius: 1,
overflow: "hidden",
border: "1px solid",

View File

@ -3,7 +3,7 @@ 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";
import { languageStore, META_LANGUAGE } from "../../store/LanguageStore";
export const CarrierCreate = observer(() => {
const { language } = languageStore;
const {
@ -13,13 +13,7 @@ export const CarrierCreate = observer(() => {
control,
formState: { errors },
} = useForm({
refineCoreProps: {
meta: {
headers: {
"Accept-Language": language,
},
},
},
refineCoreProps: META_LANGUAGE(language)
});
const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({

View File

@ -39,7 +39,7 @@ export const CarrierEdit = observer(() => {
value,
},
],
...META_LANGUAGE(language)
...META_LANGUAGE("ru")
});
@ -233,7 +233,8 @@ export const CarrierEdit = observer(() => {
return options.filter((option) =>
option.media_name
.toLowerCase()
.includes(inputValue.toLowerCase())
.includes(inputValue.toLowerCase()) &&
option.media_type == 3
);
}}
renderInput={(params) => (

View File

@ -34,6 +34,23 @@ export const RouteCreate = () => {
],
});
const { autocompleteProps: governorAppealAutocompleteProps } = useAutocomplete({
resource: "article",
onSearch: (value) => [
{
field: "heading",
operator: "contains",
value,
},
{
field: "media_type",
operator: "contains",
value,
},
]
});
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
@ -94,7 +111,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.route_number?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="text"
label={"Номер маршрута *"}
name="route_number"
@ -174,7 +191,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.path?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="text"
label={"Координаты маршрута *"}
name="path"
@ -192,25 +209,53 @@ export const RouteCreate = () => {
helperText={(errors as any)?.route_sys_number?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="number"
label={"Системный номер маршрута *"}
name="route_sys_number"
/>
<TextField
{...register("governor_appeal", {
// required: 'Это поле является обязательным',
setValueAs: (value) => Number(value),
})}
error={!!(errors as any)?.governor_appeal}
helperText={(errors as any)?.governor_appeal?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
type="number"
label={"Обращение губернатора"}
<Controller
control={control}
name="governor_appeal"
defaultValue={null}
render={({ field }) => (
<Autocomplete
{...governorAppealAutocompleteProps}
value={
governorAppealAutocompleteProps.options.find(
(option) => option.id === field.value
) ?? null
}
onChange={(_, value) => {
field.onChange(value?.id ?? "");
}}
getOptionLabel={(item) => {
return item ? item.heading : "";
}}
isOptionEqualToValue={(option, value) => {
return option.id === value?.id;
}}
filterOptions={(options, { inputValue }) => {
return options.filter((option) =>
option.heading
.toLowerCase()
.includes(inputValue.toLowerCase())
);
}}
renderInput={(params) => (
<TextField
{...params}
label="Обращение губернатора"
margin="normal"
variant="outlined"
error={!!errors.arms}
helperText={(errors as any)?.arms?.message}
required
/>
)}
/>
)}
/>
<TextField
@ -222,7 +267,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.scale_min?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="number"
label={"Масштаб (мин)"}
name="scale_min"
@ -237,7 +282,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.scale_max?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="number"
label={"Масштаб (макс)"}
name="scale_max"
@ -252,7 +297,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.rotate?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="number"
label={"Поворот"}
name="rotate"
@ -267,7 +312,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.center_latitude?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="number"
label={"Центр. широта"}
name="center_latitude"
@ -282,7 +327,7 @@ export const RouteCreate = () => {
helperText={(errors as any)?.center_longitude?.message}
margin="normal"
fullWidth
InputLabelProps={{ shrink: true }}
slotProps={{inputLabel: {shrink: true}}}
type="number"
label={"Центр. долгота"}
name="center_longitude"

View File

@ -19,8 +19,12 @@ import {
vehicleFields,
} from "./types";
import { useEffect } from "react";
import { META_LANGUAGE, languageStore } from "@stores";
import { observer } from "mobx-react-lite";
import { LanguageSelector } from "@ui";
export const RouteEdit = () => {
export const RouteEdit = observer(() => {
const { language } = languageStore;
const {
saveButtonProps,
register,
@ -29,7 +33,9 @@ export const RouteEdit = () => {
refineCore: { queryResult },
setValue,
watch,
} = useForm({});
} = useForm({
refineCoreProps: META_LANGUAGE(language)
});
const navigate = useNavigate();
const { id: routeId } = useParams<{ id: string }>();
@ -53,6 +59,7 @@ export const RouteEdit = () => {
value,
},
],
...META_LANGUAGE(language)
});
const { autocompleteProps: governorAppealAutocompleteProps } = useAutocomplete({
@ -69,7 +76,8 @@ export const RouteEdit = () => {
operator: "contains",
value,
},
]
],
...META_LANGUAGE(language)
});
return (
@ -82,6 +90,7 @@ export const RouteEdit = () => {
sx={{ display: "flex", flexDirection: "column"}}
autoComplete="off"
>
<LanguageSelector />
<Controller
control={control}
name="carrier_id"
@ -393,4 +402,4 @@ export const RouteEdit = () => {
</Box>
</Edit>
);
};
});

View File

@ -13,11 +13,15 @@ import MapIcon from '@mui/icons-material/Map';
import { localeText } from "../../locales/ru/localeText";
import { useLink } from "@refinedev/core";
import { observer } from "mobx-react-lite";
import { languageStore, META_LANGUAGE } from "@stores";
export const RouteList = () => {
export const RouteList = observer(() => {
const Link = useLink();
const { language } = languageStore;
const { dataGridProps } = useDataGrid({
resource: "route/",
meta: META_LANGUAGE(language)
});
const columns = React.useMemo<GridColDef[]>(
@ -177,7 +181,8 @@ export const RouteList = () => {
columns={columns}
localeText={localeText}
getRowId={(row: any) => row.id}
languageEnabled
/>
</List>
);
};
});

View File

@ -11,8 +11,9 @@ import {
vehicleFields,
} from "./types";
import { useNavigate, useParams } from "react-router";
import { observer } from "mobx-react-lite";
export const RouteShow = () => {
export const RouteShow = observer(() => {
const { query } = useShow({});
const { data, isLoading } = query;
const record = data?.data;
@ -115,4 +116,4 @@ export const RouteShow = () => {
</Stack>
</Show>
);
};
});

View File

@ -76,6 +76,7 @@ export const SightEdit = observer(() => {
formState: { errors },
} = useForm({
refineCoreProps: META_LANGUAGE(language),
warnWhenUnsavedChanges: false
});
const getMedia = async (id?: string | number) => {
@ -119,6 +120,7 @@ export const SightEdit = observer(() => {
}>();
const [tabValue, setTabValue] = useState(0);
const { autocompleteProps: mediaAutocompleteProps } = useAutocomplete({
resource: "media",
onSearch: (value) => [
@ -144,7 +146,8 @@ export const SightEdit = observer(() => {
operator: "contains",
value,
},
]
],
});
useEffect(() => {
@ -216,13 +219,6 @@ export const SightEdit = observer(() => {
const watermarkLUContent = watch("watermark_lu");
const watermarkRDContent = watch("watermark_rd");
// useEffect(() => {
// return () => {
// setLanguageAction("ru");
// };
// }, []);
useEffect(() => {
setCoordinatesPreview({
latitude: latitudeContent ?? "",
@ -290,7 +286,7 @@ export const SightEdit = observer(() => {
media,
});
});
}, [leftArticleId]);
}, [leftArticleId, articleAutocompleteProps.loading]);
function updateTranslations(update: boolean = true) {
const newSightData = {
@ -621,18 +617,48 @@ export const SightEdit = observer(() => {
Редактировать выбранную левую статью
</Button>
) : (
<Link to="/article/create">
<Button
variant="outlined"
size="large"
<>
<Typography
variant="h6"
gutterBottom
px={2}
py={.5}
sx={{
width: "100%",
color: "text.primary"
}}
color="secondary"
>
Создать новую статью
</Button>
</Link>
Создать и прикрепить новую статью:
</Typography>
<CreateSightArticle
language={language}
parentId={sightId!}
parentResource="sight"
childResource="article"
title="статью"
left
setHeadingParent={(heading) => {
//console.log("Updating", heading)
setCreatingArticleHeading(heading);
}}
setBodyParent={(body) => {
setCreatingArticleBody(body);
}}
/>
</>
// <Link to="/article/create">
// <Button
// variant="outlined"
// size="large"
// sx={{
// width: "100%",
// }}
// color="secondary"
// >
// Создать новую статью
// </Button>
// </Link>
)}
</Box>
</Box>
@ -678,7 +704,7 @@ export const SightEdit = observer(() => {
mb: 3,
}}
>
{leftArticleData?.heading}
{leftArticleId ? leftArticleData?.heading : creatingArticleHeading}
</Typography>
{/* Адрес */}
@ -706,7 +732,7 @@ export const SightEdit = observer(() => {
theme.palette.mode === "dark" ? "grey.300" : "grey.800",
}}
>
{leftArticleData?.body}
{leftArticleId ? leftArticleData?.body : creatingArticleBody}
</Box>
</Typography>
</Paper>
@ -807,7 +833,7 @@ export const SightEdit = observer(() => {
}}
onClick={() => setArticleAdditionMode("creating")}
>
Создать новую статью
Создать и привязать новую статью
</Box>
</Box>
</Box>
@ -870,7 +896,7 @@ export const SightEdit = observer(() => {
parentResource="sight"
childResource="article"
title="статью"
left
//left
setHeadingParent={(heading) => {
console.log("Updating", heading)
setCreatingArticleHeading(heading);