import {
Autocomplete,
Box,
TextField,
Paper,
Typography,
Tab,
Tabs,
Button,
Stack,
} from "@mui/material";
import { Edit, useAutocomplete } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller, FieldValues } from "react-hook-form";
import { Link, useParams } from "react-router";
import React, { useState, useEffect } from "react";
import { CreateSightArticle, LinkedItemsContents } from "@components";
import { ArticleItem, articleFields } from "./types";
import { axiosInstance, TOKEN_KEY } from "@providers";
import { observer } from "mobx-react-lite";
import {
Languages,
languageStore,
articleStore,
META_LANGUAGE,
EVERY_LANGUAGE,
} from "@stores";
import axios from "axios";
import { LanguageSelector, MediaData, MediaView } from "@ui";
import { ArticleEditModal } from "../../components/modals/ArticleEditModal/index";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
"aria-controls": `simple-tabpanel-${index}`,
};
}
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function CustomTabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
{value === index && {children}}
);
}
export const SightEdit = observer(() => {
const { id: sightId } = useParams<{ id: string }>();
const { language, setLanguageAction } = languageStore;
const [previewSelected, setPreviewSelected] = useState(true);
const { setArticleModalOpenAction, setArticleIdAction } = articleStore;
const [sightData, setSightData] = useState({
name: EVERY_LANGUAGE(""),
address: EVERY_LANGUAGE(""),
});
const [coordinates, setCoordinates] = useState("");
const {
saveButtonProps,
register,
refineCore: { onFinish },
control,
watch,
getValues,
setValue,
handleSubmit,
formState: { errors },
} = useForm({
refineCoreProps: META_LANGUAGE(language),
warnWhenUnsavedChanges: false,
});
const name = watch("name");
const getMedia = async (id?: string | number) => {
if (!id) return;
try {
const response = await axios.get(
`${import.meta.env.VITE_KRBL_API}/article/${id}/media`,
{
headers: {
Authorization: `Bearer ${localStorage.getItem(TOKEN_KEY)}`,
"Accept-Language": language,
},
}
);
return response.data[0];
} catch {
return;
}
};
useEffect(() => {
setLanguageAction("ru");
}, []);
const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({
resource: "city",
onSearch: (value) => [
{
field: "name",
operator: "contains",
value,
},
],
...META_LANGUAGE("ru"),
});
const [mediaFile, setMediaFile] = useState();
const [leftArticleData, setLeftArticleData] = useState<{
heading: string;
body: string;
media: MediaData;
}>();
const [tabValue, setTabValue] = useState(0);
const { autocompleteProps: mediaAutocompleteProps } = useAutocomplete({
resource: "media",
onSearch: (value) => [
{
field: "media_name",
operator: "contains",
value,
},
],
});
const { autocompleteProps: articleAutocompleteProps } = useAutocomplete({
resource: "article",
onSearch: (value) => [
{
field: "heading",
operator: "contains",
value,
},
{
field: "media_type",
operator: "contains",
value,
},
],
});
useEffect(() => {
if (sightData.name[language]) setValue("name", sightData.name[language]);
if (sightData.address[language])
setValue("address", sightData.address[language]);
}, [language, sightData, setValue]);
// Состояния для предпросмотра
const [creatingArticleHeading, setCreatingArticleHeading] =
useState("");
const [creatingArticleBody, setCreatingArticleBody] = useState("");
const [coordinatesPreview, setCoordinatesPreview] = useState("");
const [selectedArticleIndex, setSelectedArticleIndex] = useState(-1);
const [previewMediaFile, setPreviewMediaFile] = useState();
const [thumbnailPreview, setThumbnailPreview] = useState(null);
const [watermarkLUPreview, setWatermarkLUPreview] = useState(
null
);
const [watermarkRDPreview, setWatermarkRDPreview] = useState(
null
);
const [linkedArticles, setLinkedArticles] = useState([]);
// Следим за изменениями во всех полях
const selectedArticle = linkedArticles[selectedArticleIndex];
const previewMediaId = watch("preview_media");
const leftArticleId = watch("left_article");
useEffect(() => {
if (previewMediaId) {
const selectedMedia = mediaAutocompleteProps.options.find(
(option) => option.id === previewMediaId
);
console.log("Triggering", previewMediaId);
if (!selectedMedia) return;
setPreviewMediaFile(selectedMedia);
}
}, [previewMediaId, mediaAutocompleteProps.options]);
// useEffect(() => {
// const selectedWatermarkLU = mediaAutocompleteProps.options.find(
// (option) => option.id === watermarkLUContent
// );
// setWatermarkLUPreview(
// selectedWatermarkLU
// ? `${import.meta.env.VITE_KRBL_MEDIA}${
// selectedWatermarkLU.id
// }/download?token=${localStorage.getItem(TOKEN_KEY)}`
// : null
// );
// }, [watermarkLUContent, ]);
const addressContent = watch("address");
const latitudeContent = watch("latitude");
const longitudeContent = watch("longitude");
const thumbnailContent = watch("thumbnail");
const watermarkLUContent = watch("watermark_lu");
const watermarkRDContent = watch("watermark_rd");
useEffect(() => {
if (latitudeContent && longitudeContent) {
setCoordinates(`${latitudeContent} ${longitudeContent}`);
}
}, [latitudeContent, longitudeContent]);
const handleCoordinatesChange = (e: React.ChangeEvent) => {
setCoordinates(e.target.value);
if (e.target.value) {
const [lat, lon] = e.target.value.split(" ").map((s) => s.trim());
setCoordinatesPreview(`${lat ?? "0"}, ${lon ?? "0"}`);
setValue("latitude", lat ?? "");
setValue("longitude", lon ?? "");
} else {
setCoordinatesPreview("");
setValue("latitude", "");
setValue("longitude", "");
}
};
useEffect(() => {
if (linkedArticles[selectedArticleIndex]?.id) {
getMedia(linkedArticles[selectedArticleIndex].id).then((media) => {
setMediaFile(media);
});
}
}, [selectedArticleIndex, linkedArticles]);
useEffect(() => {
const selectedThumbnail = mediaAutocompleteProps.options.find(
(option) => option.id === thumbnailContent
);
setThumbnailPreview(
selectedThumbnail
? `${import.meta.env.VITE_KRBL_MEDIA}${
selectedThumbnail.id
}/download?token=${localStorage.getItem(TOKEN_KEY)}`
: null
);
}, [thumbnailContent, mediaAutocompleteProps.options]);
useEffect(() => {
const selectedWatermarkLU = mediaAutocompleteProps.options.find(
(option) => option.id === watermarkLUContent
);
setWatermarkLUPreview(
selectedWatermarkLU
? `${import.meta.env.VITE_KRBL_MEDIA}${
selectedWatermarkLU.id
}/download?token=${localStorage.getItem(TOKEN_KEY)}`
: null
);
}, [watermarkLUContent, mediaAutocompleteProps.options]);
useEffect(() => {
const selectedWatermarkRD = mediaAutocompleteProps.options.find(
(option) => option.id === watermarkRDContent
);
setWatermarkRDPreview(
selectedWatermarkRD
? `${import.meta.env.VITE_KRBL_MEDIA}${
selectedWatermarkRD.id
}/download?token=${localStorage.getItem(TOKEN_KEY)}`
: null
);
}, [watermarkRDContent, mediaAutocompleteProps.options]);
useEffect(() => {
const selectedLeftArticle = articleAutocompleteProps.options.find(
(option) => option.id === leftArticleId
);
if (!selectedLeftArticle?.id) return;
getMedia(selectedLeftArticle.id).then((media) => {
setLeftArticleData({
heading: selectedLeftArticle.heading,
body: selectedLeftArticle.body,
media,
});
});
}, [leftArticleId, articleAutocompleteProps.loading]);
function updateTranslations(update: boolean = true) {
const newSightData = {
...sightData,
name: {
...sightData.name,
[language]: watch("name") ?? "",
},
address: {
...sightData.address,
[language]: watch("address") ?? "",
},
};
if (update) setSightData(newSightData);
return newSightData;
}
const handleLanguageChange = (lang: Languages) => {
updateTranslations();
setLanguageAction(lang);
};
const handleFormSubmit = handleSubmit(async (values: FieldValues) => {
const newTranslations = updateTranslations(false);
console.log(newTranslations);
await onFinish({
...values,
translations: newTranslations,
});
});
useEffect(() => {
return () => {
setLanguageAction("ru");
};
}, [setLanguageAction]);
const [articleAdditionMode, setArticleAdditionMode] = useState<
"attaching" | "creating"
>("attaching");
const [selectedItemId, setSelectedItemId] = useState();
const [updatedLinkedArticles, setUpdatedLinkedArticles] = useState<
ArticleItem[]
>([]);
const linkItem = () => {
if (!selectedItemId) return;
const requestData = {
article_id: selectedItemId,
page_num: linkedArticles.length + 1,
};
axiosInstance
.post(
`${import.meta.env.VITE_KRBL_API}/sight/${sightId}/article`,
requestData
)
.then(() => {
axiosInstance
.get(`${import.meta.env.VITE_KRBL_API}/sight/${sightId}/article`)
.then((response) => {
setUpdatedLinkedArticles(response?.data || []);
setSelectedItemId(undefined);
});
})
.catch((error) => {
console.error("Error linking item:", error);
});
};
return (
setTabValue(newValue)}
aria-label="basic tabs example"
>
{/* Форма редактирования */}
(
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) => (
{
//setPreviewSelected(true);
//setSelectedMediaIndex(-1);
}}
label="Медиа-предпросмотр"
margin="normal"
variant="outlined"
error={!!errors.arms}
helperText={(errors as any)?.arms?.message}
/>
)}
/>
)}
/>
}
/>
(
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()) &&
option.media_type === 3
);
}}
renderInput={(params) => (
)}
/>
)}
/>
(
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) => (
)}
/>
)}
/>
(
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) => (
)}
/>
)}
/>
(
option.id === field.value
) || null
}
onChange={(_, value) => {
field.onChange(value?.id || "");
setLeftArticleData(undefined);
}}
getOptionLabel={(item) => {
return item ? item.service_name : "";
}}
isOptionEqualToValue={(option, value) => {
return option.id === value?.id;
}}
filterOptions={(options, { inputValue }) => {
return options.filter((option) =>
option.service_name
.toLowerCase()
.includes(inputValue.toLowerCase())
);
}}
renderInput={(params) => (
)}
/>
)}
/>
{leftArticleId ? (
) : (
<>
Создать и прикрепить новую статью:
{
//console.log("Updating", heading)
setCreatingArticleHeading(heading);
}}
setBodyParent={(body) => {
setCreatingArticleBody(body);
}}
/>
>
//
//
//
)}
{leftArticleData?.media && (
)}
{/* Заголовок статьи */}
theme.palette.mode === "dark" ? "grey.300" : "grey.800",
mb: 3,
mt: 3,
}}
>
{name}
{/* Адрес */}
Адрес:{" "}
theme.palette.mode === "dark" ? "grey.300" : "grey.800",
}}
>
{addressContent}
theme.palette.mode === "dark" ? "grey.300" : "grey.800",
},
"& a": {
color: "primary.main",
textDecoration: "none",
"&:hover": {
textDecoration: "underline",
},
},
"& blockquote": {
borderLeft: "4px solid",
borderColor: "primary.main",
pl: 2,
my: 2,
color: "text.secondary",
},
"& code": {
bgcolor: (theme) =>
theme.palette.mode === "dark" ? "grey.900" : "grey.100",
p: 0.5,
borderRadius: 0.5,
color: "primary.main",
},
}}
>
{leftArticleId ? leftArticleData?.body : creatingArticleBody}
(
option.id === field.value
) || null
}
onChange={(_, value) => {
console.log(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) => (
{
setPreviewSelected(true);
//setSelectedMediaIndex(-1);
}}
label="Медиа-предпросмотр"
margin="normal"
variant="outlined"
error={!!errors.arms}
helperText={(errors as any)?.arms?.message}
/>
)}
/>
)}
/>
setArticleAdditionMode("attaching")}
>
Добавить существующую статью
setArticleAdditionMode("creating")}
>
Создать и привязать новую статью
{articleAdditionMode === "attaching" && (
option.id === selectedItemId
) || null
}
onChange={(_, value) => {
setSelectedItemId(value?.id || "");
setLeftArticleData(undefined);
}}
getOptionLabel={(item) => {
return item ? item.service_name : "";
}}
isOptionEqualToValue={(option, value) => {
return option.id === value?.id;
}}
filterOptions={(options, { inputValue }) => {
return options.filter((option) =>
option.service_name
.toLowerCase()
.includes(inputValue.toLowerCase())
);
}}
renderInput={(params) => (
)}
/>
)}
{articleAdditionMode === "creating" && (
{
console.log("Updating", heading);
setCreatingArticleHeading(heading);
}}
setBodyParent={(body) => {
setCreatingArticleBody(body);
}}
/>
)}
Привязанные статьи
type="edit"
disableCreation
parentId={sightId!}
dragAllowed={true}
setItemsParent={setLinkedArticles}
parentResource="sight"
fields={articleFields}
childResource="article"
title="статьи"
updatedLinkedItems={updatedLinkedArticles}
/>
{/* Предпросмотр */}
{previewSelected && previewMediaFile && (
)}
{mediaFile && !previewSelected && (
)}
{
{!previewSelected && articleAdditionMode !== "creating" && (
{selectedArticle && (
{selectedArticle.heading}
)}
{selectedArticle && (
theme.palette.mode === "dark"
? "grey.300"
: "grey.800",
},
"& a": {
color: "primary.main",
textDecoration: "none",
"&:hover": {
textDecoration: "underline",
},
},
"& blockquote": {
borderLeft: "4px solid",
borderColor: "primary.main",
pl: 2,
my: 2,
color: "text.secondary",
},
"& code": {
bgcolor: (theme) =>
theme.palette.mode === "dark"
? "grey.900"
: "grey.100",
p: 0.5,
borderRadius: 0.5,
color: "primary.main",
},
}}
>
{selectedArticle.body}
)}
)}
{articleAdditionMode === "creating" && (
{creatingArticleHeading}
theme.palette.mode === "dark"
? "grey.300"
: "grey.800",
},
"& a": {
color: "primary.main",
textDecoration: "none",
"&:hover": {
textDecoration: "underline",
},
},
"& blockquote": {
borderLeft: "4px solid",
borderColor: "primary.main",
pl: 2,
my: 2,
color: "text.secondary",
},
"& code": {
bgcolor: (theme) =>
theme.palette.mode === "dark"
? "grey.900"
: "grey.100",
p: 0.5,
borderRadius: 0.5,
color: "primary.main",
},
}}
>
{creatingArticleBody}
)}
{linkedArticles.map((article, index) => (
{
setSelectedArticleIndex(index);
setPreviewSelected(false);
}}
sx={{
cursor: "pointer",
bgcolor: "transparent",
color: "inherit",
textDecoration:
selectedArticleIndex === index
? "underline"
: "none",
p: 1,
borderRadius: 1,
}}
>
{article.heading}
))}
}
(
option.id === field.value
) || null
}
onChange={(_, value) => {
field.onChange(value?.id || "");
}}
getOptionLabel={(item) => {
return item ? item.name : "";
}}
isOptionEqualToValue={(option, value) => {
console.log(cityAutocompleteProps.options);
return option.id === value?.id;
}}
filterOptions={(options, { inputValue }) => {
return options.filter((option) =>
option.name
.toLowerCase()
.includes(inputValue.toLowerCase())
);
}}
renderInput={(params) => (
)}
/>
)}
/>
(
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()) &&
option.media_type === 3
);
}}
renderInput={(params) => (
)}
/>
)}
/>
(
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()) &&
option.media_type === 4
);
}}
renderInput={(params) => (
)}
/>
)}
/>
(
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()) &&
option.media_type === 4
);
}}
renderInput={(params) => (
)}
/>
)}
/>
{/* Предпросмотр */}
theme.palette.mode === "dark" ? "background.paper" : "#fff",
}}
>
Предпросмотр
{thumbnailPreview && (
Адрес:{" "}
theme.palette.mode === "dark"
? "grey.300"
: "grey.800",
}}
>
{`${addressContent}`}
Логотип достопримечательности:
)}
{/* Водяные знаки */}
Водяные знаки:
{watermarkLUPreview && (
Левый верхний:
)}
{watermarkRDPreview && (
Правый верхний:
)}
{/* Координаты */}
Координаты:{" "}
theme.palette.mode === "dark"
? "grey.300"
: "grey.800",
}}
>
{coordinatesPreview}
);
});