WhiteNightsAdminPanel/src/pages/article/create.tsx
2025-05-15 04:32:23 +03:00

218 lines
6.5 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 { Create } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller, FieldValues } from "react-hook-form";
import React, { useState, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import { MarkdownEditor } from "../../components/MarkdownEditor";
import "easymde/dist/easymde.min.css";
import { LanguageSelector } from "@ui";
import { observer } from "mobx-react-lite";
import { EVERY_LANGUAGE, Languages, languageStore, META_LANGUAGE } from "@stores";
const MemoizedSimpleMDE = React.memo(MarkdownEditor);
export const ArticleCreate = observer(() => {
const { language, setLanguageAction } = languageStore;
const [articleData, setArticleData] = useState({
heading: EVERY_LANGUAGE(""),
body: EVERY_LANGUAGE("")
});
const {
saveButtonProps,
refineCore: { formLoading, onFinish },
register,
control,
watch,
formState: { errors },
setValue,
handleSubmit,
} = useForm({
refineCoreProps: {
resource: "article",
...META_LANGUAGE(language)
}
});
// Следим за изменениями в полях body и heading
const bodyContent = watch("body");
const headingContent = watch("heading");
function updateTranslations(update: boolean = true) {
const newArticleData = {
...articleData,
heading: {
...articleData.heading,
[language]: watch("heading") ?? "",
},
body: {
...articleData.body,
[language]: watch("body") ?? "",
}
}
if(update) setArticleData(newArticleData);
return newArticleData;
}
const handleFormSubmit = handleSubmit((values) => {
const newTranslations = updateTranslations(false);
return onFinish({
translations: newTranslations
});
});
useEffect(() => {
setValue("heading", articleData.heading[language] ?? "");
setValue("body", articleData.body[language] ?? "");
setPreview(articleData.body[language] ?? "");
setHeadingPreview(articleData.heading[language] ?? "");
}, [language, articleData, setValue]);
const handleLanguageChange = (lang: Languages) => {
updateTranslations();
setLanguageAction(lang);
};
const [preview, setPreview] = useState("");
const [headingPreview, setHeadingPreview] = useState("");
useEffect(() => {
setPreview(bodyContent ?? "");
}, [bodyContent]);
useEffect(() => {
setHeadingPreview(headingContent ?? "");
}, [headingContent]);
const simpleMDEOptions = React.useMemo(() => ({
placeholder: "Введите контент в формате Markdown...",
spellChecker: false,
}), []);
return (
<Create isLoading={formLoading} saveButtonProps={{
onClick: handleFormSubmit
}}>
<Box sx={{ display: "flex", flex: 1, 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 as any)?.heading}
helperText={(errors as any)?.heading?.message}
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}
onChange={onChange}
options={simpleMDEOptions}
className="my-markdown-editor"
/>
)}
/>
</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,
}}
>
{headingPreview}
</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>{preview}</ReactMarkdown>
</Box>
</Paper>
</Box>
</Create>
);
});