WhiteNightsAdminPanel/src/pages/article/edit.tsx
2025-05-20 00:09:57 +03:00

294 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Box, TextField, Typography, Paper } from "@mui/material";
import { Edit } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller, FieldValues } from "react-hook-form";
import { useParams } from "react-router";
import React, { useState, useEffect, useMemo } from "react";
import ReactMarkdown from "react-markdown";
import { useList } from "@refinedev/core";
import { MarkdownEditor, LinkedItems } from "@components";
import { MediaItem, mediaFields } from "./types";
import "easymde/dist/easymde.min.css";
import { EVERY_LANGUAGE, Languages, languageStore, META_LANGUAGE } from "@stores";
import { observer } from "mobx-react-lite";
import { LanguageSelector, MediaView } from "@ui";
import rehypeRaw from "rehype-raw";
const MemoizedSimpleMDE = React.memo(MarkdownEditor);
export const ArticleEdit = observer(() => {
const { language, setLanguageAction } = languageStore;
const [articleData, setArticleData] = useState({
heading: EVERY_LANGUAGE(""),
body: EVERY_LANGUAGE("")
});
const { id: articleId } = useParams<{ id: string }>();
const simpleMDEOptions = useMemo(
() => ({
placeholder: "Введите контент в формате Markdown...",
spellChecker: false,
}),
[]
);
const {
saveButtonProps,
refineCore: { onFinish },
register,
control,
handleSubmit,
watch,
formState: { errors },
setValue,
getValues,
} = useForm<{ heading: string; body: string }>({
refineCoreProps: META_LANGUAGE(language)
});
const bodyContent = watch("body");
const headingContent = watch("heading");
useEffect(() => {
console.log(bodyContent)
}, [bodyContent])
useEffect(() => {
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 = {
heading: {
...articleData.heading,
[language]: watch("heading") ?? "",
},
body: {
...articleData.body,
[language]: watch("body") ?? "",
}
}
if(update) setArticleData(newArticleData);
return newArticleData;
}
const handleLanguageChange = (lang: Languages) => {
updateTranslations();
setLanguageAction(lang);
console.log("Setting preview to", articleData.body[lang] ?? "")
};
const handleFormSubmit = handleSubmit((values: FieldValues) => {
return onFinish({
translations: updateTranslations(false)
});
});
const { data: mediaData, refetch } = useList<MediaItem>({
resource: `article/${articleId}/media`,
});
useEffect(() => {
return () => {
setLanguageAction("ru");
};
}, [setLanguageAction]);
return (
<Edit saveButtonProps={{
...saveButtonProps,
onClick: handleFormSubmit
}}
>
<Box sx={{ display: "flex", gap: 2 }}>
{/* Форма редактирования */}
<Box sx={{ display: "flex", flex: 1, flexDirection: "column", gap: 2 }}>
<LanguageSelector action={handleLanguageChange} />
<Box
component="form"
sx={{ flex: 1, display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("heading", {
required: "Это поле является обязательным",
})}
error={!!errors?.heading}
helperText={errors?.heading?.message as string}
margin="normal"
fullWidth
slotProps={{inputLabel: {shrink: true}}}
type="text"
label="Заголовок *"
name="heading"
/>
<Controller
control={control}
name="body"
//rules={{ required: "Это поле является обязательным" }}
defaultValue=""
render={({ field: { onChange, value } }) => (
<MemoizedSimpleMDE
value={value} // markdown
onChange={onChange}
options={simpleMDEOptions}
className="my-markdown-editor"
/>
)}
/>
{articleId && (
<LinkedItems<MediaItem>
type="edit"
parentId={articleId}
parentResource="article"
childResource="media"
fields={mediaFields}
title="медиа"
onUpdate={refetch}
/>
)}
</Box>
</Box>
{/* Блок предпросмотра */}
<Paper
sx={{
flex: 1,
p: 2,
maxHeight: "calc(100vh - 200px)",
overflowY: "auto",
position: "sticky",
top: 16,
borderRadius: 2,
border: "1px solid",
borderColor: "primary.main",
bgcolor: (theme) =>
theme.palette.mode === "dark" ? "background.paper" : "#fff",
}}
>
<Typography variant="h6" gutterBottom color="primary">
Предпросмотр
</Typography>
{/* Заголовок статьи */}
<Typography
variant="h4"
gutterBottom
sx={{
color: (theme) =>
theme.palette.mode === "dark" ? "grey.300" : "grey.800",
mb: 3,
}}
>
{headingContent}
</Typography>
{/* Markdown контент */}
<Box
sx={{
"& img": {
maxWidth: "100%",
height: "auto",
borderRadius: 1,
},
"& h1, & h2, & h3, & h4, & h5, & h6": {
color: "primary.main",
mt: 2,
mb: 1,
},
"& p": {
mb: 2,
color: (theme) =>
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",
},
}}
>
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{bodyContent}</ReactMarkdown>
</Box>
{/* Привязанные медиа */}
{mediaData?.data && mediaData.data.length > 0 && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" gutterBottom color="primary">
Привязанные медиа:
</Typography>
<Box
sx={{
display: "flex",
gap: 1,
flexWrap: "wrap",
mb: 2,
}}
>
{mediaData.data.map((media) => (
<Box
key={media.id}
sx={{
display: "flex",
width: "45%",
height: "45%",
aspectRatio: "1/1",
borderRadius: 1,
overflow: "hidden",
border: "1px solid",
borderColor: "primary.main",
}}
>
<MediaView media={media} />
{/* <img
src={`${import.meta.env.VITE_KRBL_MEDIA}${
media.id
}/download?token=${localStorage.getItem(TOKEN_KEY)}`}
alt={media.media_name}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
/> */}
</Box>
))}
</Box>
</Box>
)}
</Paper>
</Box>
</Edit>
);
});