This commit is contained in:
parent
28826123ec
commit
16640cb116
@ -51,6 +51,7 @@
|
||||
"react-simple-maps": "^3.0.0",
|
||||
"react-simplemde-editor": "^5.2.0",
|
||||
"react-swipeable": "^7.0.2",
|
||||
"react-toastify": "^11.0.5",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"three": "^0.175.0",
|
||||
"vite-plugin-svgr": "^4.3.0"
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
ALLOWED_VIDEO_TYPES,
|
||||
} from "../components/media/MediaFormUtils";
|
||||
import { EVERY_LANGUAGE, Languages } from "@stores";
|
||||
import { useNotification } from "@refinedev/core";
|
||||
|
||||
const MemoizedSimpleMDE = React.memo(MarkdownEditor);
|
||||
|
||||
@ -30,14 +31,16 @@ type MediaFile = {
|
||||
};
|
||||
|
||||
type Props = {
|
||||
parentId: string | number;
|
||||
parentId?: string | number;
|
||||
parentResource: string;
|
||||
childResource: string;
|
||||
title: string;
|
||||
left?: boolean;
|
||||
language: Languages,
|
||||
setHeadingParent?: (heading: string) => void,
|
||||
setBodyParent?: (body: string) => void,
|
||||
language: Languages;
|
||||
setHeadingParent?: (heading: string) => void;
|
||||
setBodyParent?: (body: string) => void;
|
||||
onSave?: (something: any) => void;
|
||||
noReset?: boolean;
|
||||
};
|
||||
|
||||
export const CreateSightArticle = ({
|
||||
@ -48,8 +51,11 @@ export const CreateSightArticle = ({
|
||||
left,
|
||||
language,
|
||||
setHeadingParent,
|
||||
setBodyParent
|
||||
setBodyParent,
|
||||
onSave,
|
||||
noReset,
|
||||
}: Props) => {
|
||||
const notification = useNotification();
|
||||
const theme = useTheme();
|
||||
const [mediaFiles, setMediaFiles] = useState<MediaFile[]>([]);
|
||||
const [workingLanguage, setWorkingLanguage] = useState<Languages>(language);
|
||||
@ -69,10 +75,9 @@ export const CreateSightArticle = ({
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const [articleData, setArticleData] = useState({
|
||||
heading: EVERY_LANGUAGE(""),
|
||||
body: EVERY_LANGUAGE("")
|
||||
body: EVERY_LANGUAGE(""),
|
||||
});
|
||||
|
||||
function updateTranslations() {
|
||||
@ -85,8 +90,8 @@ export const CreateSightArticle = ({
|
||||
body: {
|
||||
...articleData.body,
|
||||
[workingLanguage]: watch("body") ?? "",
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
setArticleData(newArticleData);
|
||||
return newArticleData;
|
||||
}
|
||||
@ -126,8 +131,12 @@ export const CreateSightArticle = ({
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
accept: {
|
||||
"image/*": ALLOWED_IMAGE_TYPES,
|
||||
"video/*": ALLOWED_VIDEO_TYPES,
|
||||
"image/jpeg": [".jpeg", ".jpg"],
|
||||
"image/png": [".png"],
|
||||
"image/webp": [".webp"],
|
||||
"video/mp4": [".mp4"],
|
||||
"video/webm": [".webm"],
|
||||
"video/ogg": [".ogg"],
|
||||
},
|
||||
multiple: true,
|
||||
});
|
||||
@ -153,42 +162,48 @@ export const CreateSightArticle = ({
|
||||
try {
|
||||
// Создаем статью
|
||||
const response = await axiosInstance.post(
|
||||
`${import.meta.env.VITE_KRBL_API}/${childResource}`, {
|
||||
`${import.meta.env.VITE_KRBL_API}/${childResource}`,
|
||||
{
|
||||
...data,
|
||||
translations: updateTranslations()
|
||||
translations: updateTranslations(),
|
||||
}
|
||||
);
|
||||
const itemId = response.data.id;
|
||||
|
||||
// Получаем существующие статьи для определения порядкового номера
|
||||
const existingItemsResponse = await axiosInstance.get(
|
||||
`${import.meta.env.VITE_KRBL_API}/${parentResource}/${parentId}/${childResource}`
|
||||
);
|
||||
const existingItems = existingItemsResponse.data ?? [];
|
||||
const nextPageNum = existingItems.length + 1;
|
||||
if (parentId) {
|
||||
// Получаем существующие статьи для определения порядкового номера
|
||||
const existingItemsResponse = await axiosInstance.get(
|
||||
`${
|
||||
import.meta.env.VITE_KRBL_API
|
||||
}/${parentResource}/${parentId}/${childResource}`
|
||||
);
|
||||
const existingItems = existingItemsResponse.data ?? [];
|
||||
const nextPageNum = existingItems.length + 1;
|
||||
|
||||
if (!left) {
|
||||
// Привязываем статью к достопримечательности если она не левая
|
||||
await axiosInstance.post(
|
||||
`${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}/`,
|
||||
if (!left) {
|
||||
await axiosInstance.post(
|
||||
`${
|
||||
import.meta.env.VITE_KRBL_API
|
||||
}/${parentResource}/${parentId}/${childResource}/`,
|
||||
{
|
||||
...data,
|
||||
left_article: itemId
|
||||
[`${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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,10 +226,21 @@ export const CreateSightArticle = ({
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
resetItem();
|
||||
setMediaFiles([]);
|
||||
window.location.reload();
|
||||
if (noReset) {
|
||||
setValue("heading", "");
|
||||
setValue("body", "");
|
||||
} else {
|
||||
resetItem();
|
||||
}
|
||||
if (onSave) {
|
||||
onSave(response.data);
|
||||
notification.open({
|
||||
message: "Статья успешно создана",
|
||||
type: "success",
|
||||
});
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("Error creating item:", err);
|
||||
}
|
||||
@ -239,7 +265,7 @@ export const CreateSightArticle = ({
|
||||
helperText={(itemErrors as any)?.heading?.message}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
slotProps={{inputLabel: {shrink: true}}}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
type="text"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
@ -345,7 +371,12 @@ export const CreateSightArticle = ({
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mt: 2, display: "flex", gap: 2 }}>
|
||||
<Button variant="contained" color="primary" onClick={handleSubmitItem(handleCreate)}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
type="submit"
|
||||
onClick={handleSubmitItem(handleCreate)}
|
||||
>
|
||||
Создать
|
||||
</Button>
|
||||
<Button
|
||||
|
@ -38,7 +38,7 @@ const style = {
|
||||
bgcolor: "background.paper",
|
||||
border: "2px solid #000",
|
||||
boxShadow: 24,
|
||||
p: 4
|
||||
p: 4,
|
||||
};
|
||||
|
||||
export const ArticleEditModal = observer(() => {
|
||||
@ -62,13 +62,11 @@ export const ArticleEditModal = observer(() => {
|
||||
|
||||
// Load existing media files when editing an article
|
||||
const loadExistingMedia = async () => {
|
||||
console.log("Called loadExistingMedia")
|
||||
console.log("Called loadExistingMedia");
|
||||
if (selectedArticleId) {
|
||||
try {
|
||||
const response = await axiosInstance.get(
|
||||
`${
|
||||
import.meta.env.VITE_KRBL_API
|
||||
}/article/${selectedArticleId}/media`
|
||||
`${import.meta.env.VITE_KRBL_API}/article/${selectedArticleId}/media`
|
||||
);
|
||||
const existingMedia = response.data;
|
||||
|
||||
@ -95,7 +93,7 @@ export const ArticleEditModal = observer(() => {
|
||||
);
|
||||
|
||||
setMediaFiles(mediaFiles);
|
||||
setRefresh(refresh+1);
|
||||
setRefresh(refresh + 1);
|
||||
} catch (error) {
|
||||
console.error("Error loading existing media:", error);
|
||||
}
|
||||
@ -125,7 +123,9 @@ export const ArticleEditModal = observer(() => {
|
||||
try {
|
||||
// Upload new media files
|
||||
const newMediaFiles = mediaFiles.filter((file) => !file.media_id);
|
||||
const existingMediaAmount = mediaFiles.filter((file) => file.media_id).length;
|
||||
const existingMediaAmount = mediaFiles.filter(
|
||||
(file) => file.media_id
|
||||
).length;
|
||||
const mediaIds = await Promise.all(
|
||||
newMediaFiles.map(async (mediaFile) => {
|
||||
return await uploadMedia(mediaFile);
|
||||
@ -164,10 +164,10 @@ export const ArticleEditModal = observer(() => {
|
||||
|
||||
useEffect(() => {
|
||||
if (articleData.heading[language]) {
|
||||
setValue("heading", articleData.heading[language])
|
||||
setValue("heading", articleData.heading[language]);
|
||||
}
|
||||
if (articleData.body[language]) {
|
||||
setValue("body", articleData.body[language])
|
||||
setValue("body", articleData.body[language]);
|
||||
}
|
||||
}, [language, articleData, setValue]);
|
||||
|
||||
@ -176,12 +176,12 @@ export const ArticleEditModal = observer(() => {
|
||||
...prevData,
|
||||
heading: {
|
||||
...prevData.heading,
|
||||
[language]: watch("heading") ?? ""
|
||||
[language]: watch("heading") ?? "",
|
||||
},
|
||||
body: {
|
||||
...prevData.body,
|
||||
[language]: watch("body") ?? ""
|
||||
}
|
||||
[language]: watch("body") ?? "",
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
@ -205,8 +205,12 @@ export const ArticleEditModal = observer(() => {
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
accept: {
|
||||
"image/*": ALLOWED_IMAGE_TYPES,
|
||||
"video/*": ALLOWED_VIDEO_TYPES,
|
||||
"image/jpeg": [".jpeg", ".jpg"],
|
||||
"image/png": [".png"],
|
||||
"image/webp": [".webp"],
|
||||
"video/mp4": [".mp4"],
|
||||
"video/webm": [".webm"],
|
||||
"video/ogg": [".ogg"],
|
||||
},
|
||||
multiple: true,
|
||||
});
|
||||
@ -258,7 +262,7 @@ export const ArticleEditModal = observer(() => {
|
||||
onClose={() => setArticleModalOpenAction(false)}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
sx={{overflow: "auto"}}
|
||||
sx={{ overflow: "auto" }}
|
||||
>
|
||||
<Box sx={style}>
|
||||
<Edit
|
||||
|
@ -1,5 +1,5 @@
|
||||
@import './stylesheets/hidden-functionality.css';
|
||||
@import './stylesheets/roles-functionality.css';
|
||||
@import "./stylesheets/hidden-functionality.css";
|
||||
@import "./stylesheets/roles-functionality.css";
|
||||
|
||||
.limited-text {
|
||||
overflow: hidden;
|
||||
@ -7,3 +7,19 @@
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.backup-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
color: #544044;
|
||||
border-radius: 10%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.backup-button:hover {
|
||||
background-color: rgba(84, 64, 68, 0.5);
|
||||
}
|
||||
|
@ -1,4 +1,13 @@
|
||||
import { Autocomplete, Box, TextField, Typography, Paper } from "@mui/material";
|
||||
import {
|
||||
Autocomplete,
|
||||
Box,
|
||||
TextField,
|
||||
Typography,
|
||||
Paper,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
} from "@mui/material";
|
||||
import { Create, useAutocomplete } from "@refinedev/mui";
|
||||
import { useForm } from "@refinedev/react-hook-form";
|
||||
import { Controller, FieldValues } from "react-hook-form";
|
||||
@ -8,11 +17,14 @@ import { TOKEN_KEY } from "@providers";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { EVERY_LANGUAGE, Languages, languageStore, cityStore } from "@stores";
|
||||
import { LanguageSelector } from "@ui";
|
||||
import { CreateSightArticle } from "@components";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
|
||||
export const SightCreate = observer(() => {
|
||||
const { language, setLanguageAction } = languageStore;
|
||||
const [sightData, setSightData] = useState({
|
||||
name: EVERY_LANGUAGE(""),
|
||||
address: EVERY_LANGUAGE("")
|
||||
address: EVERY_LANGUAGE(""),
|
||||
});
|
||||
|
||||
const {
|
||||
@ -46,9 +58,9 @@ export const SightCreate = observer(() => {
|
||||
address: {
|
||||
...sightData.address,
|
||||
[language]: watch("address") ?? "",
|
||||
}
|
||||
}
|
||||
if(update) setSightData(newSightData);
|
||||
},
|
||||
};
|
||||
if (update) setSightData(newSightData);
|
||||
return newSightData;
|
||||
}
|
||||
|
||||
@ -62,7 +74,7 @@ export const SightCreate = observer(() => {
|
||||
console.log(newTranslations);
|
||||
return onFinish({
|
||||
...values,
|
||||
translations: newTranslations
|
||||
translations: newTranslations,
|
||||
});
|
||||
});
|
||||
|
||||
@ -71,6 +83,11 @@ export const SightCreate = observer(() => {
|
||||
latitude: "",
|
||||
longitude: "",
|
||||
});
|
||||
|
||||
const [creatingArticleHeading, setCreatingArticleHeading] =
|
||||
useState<string>("");
|
||||
const [creatingArticleBody, setCreatingArticleBody] = useState<string>("");
|
||||
|
||||
const [cityPreview, setCityPreview] = useState("");
|
||||
const [thumbnailPreview, setThumbnailPreview] = useState<string | null>(null);
|
||||
const [watermarkLUPreview, setWatermarkLUPreview] = useState<string | null>(
|
||||
@ -80,6 +97,8 @@ export const SightCreate = observer(() => {
|
||||
null
|
||||
);
|
||||
const [leftArticlePreview, setLeftArticlePreview] = useState("");
|
||||
const [customOptions, setCustomOptions] = useState<any[]>([]);
|
||||
|
||||
const [previewArticlePreview, setPreviewArticlePreview] = useState("");
|
||||
|
||||
const handleCoordinatesChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@ -122,6 +141,7 @@ export const SightCreate = observer(() => {
|
||||
|
||||
const { autocompleteProps: articleAutocompleteProps } = useAutocomplete({
|
||||
resource: "article",
|
||||
|
||||
onSearch: (value) => [
|
||||
{
|
||||
field: "heading",
|
||||
@ -131,6 +151,8 @@ export const SightCreate = observer(() => {
|
||||
],
|
||||
});
|
||||
|
||||
const mergedOptions = [...articleAutocompleteProps.options, ...customOptions];
|
||||
|
||||
// Следим за изменениями во всех полях
|
||||
const nameContent = watch("name");
|
||||
const addressContent = watch("address");
|
||||
@ -222,10 +244,13 @@ export const SightCreate = observer(() => {
|
||||
}, [previewArticleContent, articleAutocompleteProps.options]);
|
||||
|
||||
return (
|
||||
<Create isLoading={formLoading} saveButtonProps={{
|
||||
...saveButtonProps,
|
||||
onClick: handleFormSubmit
|
||||
}}>
|
||||
<Create
|
||||
isLoading={formLoading}
|
||||
saveButtonProps={{
|
||||
...saveButtonProps,
|
||||
onClick: handleFormSubmit,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", flex: 1, gap: 2 }}>
|
||||
{/* Форма создания */}
|
||||
@ -244,7 +269,7 @@ export const SightCreate = observer(() => {
|
||||
helperText={(errors as any)?.name?.message}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
slotProps={{inputLabel: {shrink: true}}}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
type="text"
|
||||
label={"Название *"}
|
||||
name="name"
|
||||
@ -257,7 +282,7 @@ export const SightCreate = observer(() => {
|
||||
helperText={(errors as any)?.latitude?.message}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
slotProps={{inputLabel: {shrink: true}}}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
type="text"
|
||||
label={"Координаты *"}
|
||||
/>
|
||||
@ -286,7 +311,7 @@ export const SightCreate = observer(() => {
|
||||
helperText={(errors as any)?.address?.message}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
slotProps={{inputLabel: {shrink: true}}}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
type="text"
|
||||
label={"Адрес"}
|
||||
name="address"
|
||||
@ -370,7 +395,7 @@ export const SightCreate = observer(() => {
|
||||
variant="outlined"
|
||||
error={!!errors.thumbnail}
|
||||
helperText={(errors as any)?.thumbnail?.message}
|
||||
// required
|
||||
// required
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -468,27 +493,25 @@ export const SightCreate = observer(() => {
|
||||
render={({ field }) => (
|
||||
<Autocomplete
|
||||
{...articleAutocompleteProps}
|
||||
options={mergedOptions} // ← use merged options
|
||||
value={
|
||||
articleAutocompleteProps.options.find(
|
||||
(option) => option.id === field.value
|
||||
) ?? null
|
||||
mergedOptions.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) =>
|
||||
getOptionLabel={(item) => (item ? item.heading : "")}
|
||||
isOptionEqualToValue={(option, value) =>
|
||||
option.id === value?.id
|
||||
}
|
||||
filterOptions={(options, { inputValue }) =>
|
||||
options.filter((option) =>
|
||||
option.heading
|
||||
.toLowerCase()
|
||||
.includes(inputValue.toLowerCase())
|
||||
);
|
||||
}}
|
||||
)
|
||||
}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
@ -503,6 +526,31 @@ export const SightCreate = observer(() => {
|
||||
)}
|
||||
/>
|
||||
|
||||
{!leftArticleContent && (
|
||||
<Accordion>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls="create-article-content"
|
||||
id="create-article-header"
|
||||
>
|
||||
<Typography>Создать новую статью</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<CreateSightArticle
|
||||
language={language}
|
||||
parentResource="sight"
|
||||
childResource="article"
|
||||
title="статью"
|
||||
noReset
|
||||
left
|
||||
onSave={(something: any) => {
|
||||
setCustomOptions((prev) => [...prev, something]);
|
||||
}}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="preview_media"
|
||||
@ -516,7 +564,7 @@ export const SightCreate = observer(() => {
|
||||
) || null
|
||||
}
|
||||
onChange={(_, value) => {
|
||||
console.log(value, _)
|
||||
console.log(value, _);
|
||||
field.onChange(value?.id || "");
|
||||
}}
|
||||
getOptionLabel={(item) => {
|
||||
@ -531,7 +579,7 @@ export const SightCreate = observer(() => {
|
||||
option.media_name
|
||||
.toLowerCase()
|
||||
.includes(inputValue.toLowerCase()) &&
|
||||
[1,2,5,6].includes(option.media_type)
|
||||
[1, 2, 3, 4, 5, 6].includes(option.media_type)
|
||||
);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,8 +12,14 @@ import { CustomDataGrid } from "@components";
|
||||
import { localeText } from "../../locales/ru/localeText";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useMany } from "@refinedev/core";
|
||||
import { DatabaseBackup } from "lucide-react";
|
||||
import axios from "axios";
|
||||
import { TOKEN_KEY } from "../../providers/authProvider";
|
||||
import { toast } from "react-toastify";
|
||||
import { useNotification } from "@refinedev/core";
|
||||
|
||||
export const SnapshotList = observer(() => {
|
||||
const notification = useNotification();
|
||||
const { dataGridProps } = useDataGrid({
|
||||
resource: "snapshots",
|
||||
hasPagination: false,
|
||||
@ -47,6 +53,30 @@ export const SnapshotList = observer(() => {
|
||||
return map;
|
||||
}, [parentsData]);
|
||||
|
||||
const handleBackup = async (id: number) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${import.meta.env.VITE_KRBL_API}/snapshots/${id}/restore`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem(TOKEN_KEY)}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
notification?.open({
|
||||
message: "Cнапшот восстановлен",
|
||||
type: "success",
|
||||
});
|
||||
} catch (error) {
|
||||
notification?.open({
|
||||
message: "Ошибка при восстановлении снимка",
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const columns = React.useMemo<GridColDef[]>(
|
||||
() => [
|
||||
{
|
||||
@ -80,6 +110,12 @@ export const SnapshotList = observer(() => {
|
||||
renderCell: function render({ row }) {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="backup-button"
|
||||
onClick={() => handleBackup(row.ID)}
|
||||
>
|
||||
<DatabaseBackup />
|
||||
</button>
|
||||
<ShowButton hideText recordItemId={row.ID} />
|
||||
<DeleteButton
|
||||
hideText
|
||||
|
@ -19,8 +19,6 @@ axiosInstance.interceptors.request.use((config) => {
|
||||
|
||||
config.headers["X-Language"] = config.headers["Accept-Language"];
|
||||
|
||||
console.log("Request headers:", config.headers);
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
|
@ -5703,6 +5703,13 @@ react-swipeable@^7.0.2:
|
||||
resolved "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.2.tgz"
|
||||
integrity sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==
|
||||
|
||||
react-toastify@^11.0.5:
|
||||
version "11.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-11.0.5.tgz#ce4c42d10eeb433988ab2264d3e445c4e9d13313"
|
||||
integrity sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==
|
||||
dependencies:
|
||||
clsx "^2.1.1"
|
||||
|
||||
react-transition-group@^4.4.5:
|
||||
version "4.4.5"
|
||||
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user