feat: Add translation on 3 languages for sight page

This commit is contained in:
2025-06-01 00:34:59 +03:00
parent 0d9bbb140f
commit 87386c6a73
22 changed files with 768 additions and 732 deletions

View File

@@ -14,79 +14,44 @@ import {
ReactMarkdownEditor,
} from "@widgets";
import { Unlink, Trash2, ImagePlus } from "lucide-react";
import { useState, useEffect, useCallback } from "react";
import { useState, useCallback } from "react";
import { observer } from "mobx-react-lite";
export const LeftWidgetTab = observer(
({ value, index }: { value: number; index: number }) => {
const { sightInfo, updateSightInfo, loadSightInfo } = editSightStore;
const { articleLoading, getArticleByArticleId } = articlesStore;
const { language } = languageStore;
const { sight, updateSightInfo } = editSightStore;
const { getArticleByArticleId } = articlesStore;
const linkedArticle = getArticleByArticleId.get(); // Получаем связанную статью
const data = sightInfo[languageStore.language]; // Получаем данные для текущего языка
useEffect(() => {
// Этот useEffect должен загружать данные ИЗ СВЯЗАННОЙ СТАТЬИ
// ТОЛЬКО ЕСЛИ данные для ТЕКУЩЕГО ЯЗЫКА еще не были загружены
// или если sightInfo.left_article изменился (т.е. привязали новую статью).
// Мы также должны учитывать, что linkedArticle может измениться (т.е. новую статью привязали)
// или language изменился.
// Если для текущего языка данные еще не "загружены" (`loaded: false`),
// тогда мы берем их из `linkedArticle` и инициализируем.
console.log("data.left.loaded", data.left.loaded);
if (!data.left.loaded) {
// <--- КЛЮЧЕВОЕ УСЛОВИЕ
if (linkedArticle && !articleLoading) {
console.log("loadSightInfo", linkedArticle, language);
loadSightInfo(
languageStore.language,
linkedArticle.heading,
linkedArticle.body || "",
null
);
}
}
// Зависимости: linkedArticle (для реакции на изменение привязанной статьи),
// languageStore.language (для реакции на изменение языка),
// loadSightInfo (чтобы useEffect знал об изменениях в функции),
// data.left.loaded (чтобы useEffect перепроверил условие, когда этот флаг изменится).
// Важно: если data.left.loaded становится true, то этот эффект не будет
// перезапускаться для того же языка.
}, [
linkedArticle?.heading,
language,
loadSightInfo,
data.left.loaded,
articleLoading,
]);
const data = sight[languageStore.language]; // Получаем данные для текущего языка
const [isSelectMediaDialogOpen, setIsSelectMediaDialogOpen] =
useState(false);
const handleOpenMediaDialog = useCallback(() => {
setIsSelectMediaDialogOpen(true);
}, []);
const handleMediaSelected = useCallback(
(selectedMedia: any) => {
// При выборе медиа, обновляем данные для ТЕКУЩЕГО ЯЗЫКА
// сохраняя текущие heading и body.
updateSightInfo(
languageStore.language,
data.left.heading,
data.left.body,
selectedMedia
);
setIsSelectMediaDialogOpen(false);
},
[
const handleMediaSelected = useCallback(() => {
// При выборе медиа, обновляем данные для ТЕКУЩЕГО ЯЗЫКА
// сохраняя текущие heading и body.
updateSightInfo(
languageStore.language,
data.left.heading,
data.left.body,
updateSightInfo,
]
);
{
left: {
heading: data.left.heading,
body: data.left.body,
},
},
false
);
setIsSelectMediaDialogOpen(false);
}, [
languageStore.language,
{
left: {
heading: data.left.heading,
body: data.left.body,
},
},
false,
]);
const handleCloseMediaDialog = useCallback(() => {
setIsSelectMediaDialogOpen(false);
@@ -150,13 +115,17 @@ export const LeftWidgetTab = observer(
>
<TextField
label="Название информации"
value={data.left.heading}
value={data?.left?.heading}
onChange={(e) =>
updateSightInfo(
languageStore.language,
e.target.value,
data.left.body,
data.left.media
{
left: {
heading: e.target.value,
body: data.left.body,
},
},
false
)
}
variant="outlined"
@@ -164,19 +133,23 @@ export const LeftWidgetTab = observer(
/>
<ReactMarkdownEditor
value={data.left.body}
value={data?.left?.body}
onChange={(value) =>
updateSightInfo(
languageStore.language,
data.left.heading,
value,
data.left.media
{
left: {
heading: data.left.heading,
body: value,
},
},
false
)
}
/>
{/* Блок МЕДИА для статьи */}
<Paper elevation={1} sx={{ padding: 2, mt: 1 }}>
{/* <Paper elevation={1} sx={{ padding: 2, mt: 1 }}>
<Typography variant="h6" gutterBottom>
МЕДИА
</Typography>
@@ -226,16 +199,21 @@ export const LeftWidgetTab = observer(
onClick={() =>
updateSightInfo(
languageStore.language,
data.left.heading,
data.left.body,
null
{
left: {
heading: data.left.heading,
body: data.left.body,
media: null,
},
},
false
)
}
>
Удалить медиа
</Button>
)}
</Paper>
</Paper> */}
</Box>
{/* Правая колонка: Предпросмотр */}
@@ -247,7 +225,6 @@ export const LeftWidgetTab = observer(
gap: 1.5,
}}
>
<Typography variant="h6">Предпросмотр</Typography>
<Paper
elevation={3}
sx={{
@@ -263,8 +240,7 @@ export const LeftWidgetTab = observer(
flexDirection: "column",
}}
>
{/* Медиа в превью (если есть) */}
{data.left.media ? (
{/* {data.left.media?.filename ? (
<Box
sx={{
width: "100%",
@@ -276,7 +252,7 @@ export const LeftWidgetTab = observer(
}}
>
<img
src={data.left.media.filename}
src={data.left.media?.filename ?? ""}
alt="Превью медиа"
style={{
objectFit: "cover",
@@ -286,19 +262,21 @@ export const LeftWidgetTab = observer(
/>
</Box>
) : (
<Box
sx={{
width: "100%",
height: 200,
backgroundColor: "grey.300",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<ImagePlus size={48} color="grey" />
</Box>
)}
)} */}
<Box
sx={{
width: "100%",
height: 200,
backgroundColor: "grey.300",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<ImagePlus size={48} color="grey" />
</Box>
{/* Заголовок в превью */}
<Box
@@ -313,7 +291,7 @@ export const LeftWidgetTab = observer(
component="h2"
sx={{ wordBreak: "break-word" }}
>
{data.left.heading || "Название информации"}
{data?.left?.heading || "Название информации"}
</Typography>
</Box>
@@ -324,7 +302,7 @@ export const LeftWidgetTab = observer(
flexGrow: 1,
}}
>
<ReactMarkdownComponent value={data.left.body} />
<ReactMarkdownComponent value={data?.left?.body} />
</Box>
</Paper>
</Box>