feat: Add translation on 3 languages for sight page
This commit is contained in:
@ -1,404 +1,371 @@
|
||||
// RightWidgetTab.tsx
|
||||
import { Box, Button, Paper, TextField, Typography } from "@mui/material";
|
||||
import {
|
||||
TabPanel,
|
||||
Box,
|
||||
Button,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Paper,
|
||||
Typography,
|
||||
Menu,
|
||||
MenuItem,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
articlesStore,
|
||||
BackButton,
|
||||
languageStore, // Предполагаем, что он есть в @shared
|
||||
Language, // Предполагаем, что он есть в @shared
|
||||
// SelectArticleModal, // Добавим позже
|
||||
// articlesStore, // Добавим позже
|
||||
SelectArticleModal,
|
||||
TabPanel,
|
||||
} from "@shared";
|
||||
import { LanguageSwitcher } from "@widgets"; // Предполагаем, что LanguageSwitcher у вас есть
|
||||
import { SightEdit } from "@widgets";
|
||||
import { Plus } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
import { editSightStore, BlockItem } from "@shared"; // Путь к вашему стору
|
||||
import { useState } from "react";
|
||||
|
||||
// Импортируем сюда же определения BlockItem, если не выносим в types.ts
|
||||
// export interface BlockItem { id: string; type: 'media' | 'article'; nameForSidebar: string; linkedArticleStoreId?: string; }
|
||||
|
||||
// --- Начальные данные для структуры блоков (позже это может загружаться) ---
|
||||
// ID здесь должны быть уникальными для списка.
|
||||
const initialBlockStructures: Omit<BlockItem, "nameForSidebar">[] = [
|
||||
{ id: "preview_media_main", type: "media" },
|
||||
{ id: "article_1_local", type: "article" }, // Эти статьи будут редактироваться локально
|
||||
{ id: "article_2_local", type: "article" },
|
||||
// --- Mock Data (can be moved to a separate file or fetched from an API) ---
|
||||
const mockRightWidgetBlocks = [
|
||||
{ id: "preview_media", name: "Превью-медиа", type: "special" },
|
||||
{ id: "article_1", name: "1. История", type: "article" },
|
||||
{ id: "article_2", name: "2. Факты", type: "article" },
|
||||
{
|
||||
id: "article_3",
|
||||
name: "3. Блокада (Пример длинного названия)",
|
||||
type: "article",
|
||||
},
|
||||
];
|
||||
|
||||
interface RightWidgetTabProps {
|
||||
value: number;
|
||||
index: number;
|
||||
const mockSelectedBlockData = {
|
||||
id: "article_1",
|
||||
heading: "История основания Санкт-Петербурга",
|
||||
body: "## Начало\nГород был основан 27 мая 1703 года Петром I...",
|
||||
media: [],
|
||||
};
|
||||
|
||||
// --- ArticleListSidebar Component ---
|
||||
interface ArticleBlock {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
linkedArticleId?: string; // Added for linked articles
|
||||
}
|
||||
|
||||
export const RightWidgetTab = observer(
|
||||
({ value, index }: RightWidgetTabProps) => {
|
||||
const { language } = languageStore; // Текущий язык
|
||||
const { sightInfo } = editSightStore; // Данные достопримечательности
|
||||
interface ArticleListSidebarProps {
|
||||
blocks: ArticleBlock[];
|
||||
selectedBlockId: string | null;
|
||||
onSelectBlock: (blockId: string) => void;
|
||||
onCreateNew: () => void;
|
||||
onSelectExisting: () => void;
|
||||
}
|
||||
|
||||
// 1. Структура блоков: порядок, тип, связи (не сам контент)
|
||||
// Имена nameForSidebar будут динамически браться из sightInfo или articlesStore
|
||||
const [blockItemsStructure, setBlockItemsStructure] = useState<
|
||||
Omit<BlockItem, "nameForSidebar">[]
|
||||
>(initialBlockStructures);
|
||||
const ArticleListSidebar = ({
|
||||
blocks,
|
||||
selectedBlockId,
|
||||
onSelectBlock,
|
||||
onCreateNew,
|
||||
onSelectExisting,
|
||||
}: ArticleListSidebarProps) => {
|
||||
const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
// 2. ID выбранного блока для редактирования
|
||||
const [selectedBlockId, setSelectedBlockId] = useState<string | null>(
|
||||
() => {
|
||||
// По умолчанию выбираем первый блок, если он есть
|
||||
return initialBlockStructures.length > 0
|
||||
? initialBlockStructures[0].id
|
||||
: null;
|
||||
}
|
||||
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setMenuAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setMenuAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper
|
||||
elevation={2}
|
||||
sx={{
|
||||
width: 260,
|
||||
minWidth: 240,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
padding: 1.5,
|
||||
borderRadius: 2,
|
||||
border: "1px solid",
|
||||
borderColor: "divider",
|
||||
}}
|
||||
>
|
||||
<List
|
||||
dense
|
||||
sx={{
|
||||
overflowY: "auto",
|
||||
flexGrow: 1,
|
||||
maxHeight: "calc(100% - 60px)",
|
||||
}}
|
||||
>
|
||||
{blocks.map((block) => (
|
||||
<ListItemButton
|
||||
key={block.id}
|
||||
selected={selectedBlockId === block.id}
|
||||
onClick={() => onSelectBlock(block.id)}
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
mb: 0.5,
|
||||
backgroundColor:
|
||||
selectedBlockId === block.id ? "primary.light" : "transparent",
|
||||
"&.Mui-selected": {
|
||||
backgroundColor: "primary.main",
|
||||
color: "primary.contrastText",
|
||||
"&:hover": {
|
||||
backgroundColor: "primary.dark",
|
||||
},
|
||||
},
|
||||
"&:hover": {
|
||||
backgroundColor:
|
||||
selectedBlockId !== block.id ? "action.hover" : undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
primary={block.name}
|
||||
primaryTypographyProps={{
|
||||
fontWeight: selectedBlockId === block.id ? "bold" : "normal",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
|
||||
<button
|
||||
className="w-8 h-8 rounded-full bg-blue-500 text-white flex items-center justify-center hover:bg-blue-600 transition-colors"
|
||||
onClick={handleMenuOpen}
|
||||
>
|
||||
<Plus color="white" />
|
||||
</button>
|
||||
<Menu
|
||||
anchorEl={menuAnchorEl}
|
||||
open={Boolean(menuAnchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "right",
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={onCreateNew}>Создать новую</MenuItem>
|
||||
<MenuItem onClick={onSelectExisting}>Выбрать существующую</MenuItem>
|
||||
</Menu>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
// --- ArticleEditorPane Component ---
|
||||
interface ArticleData {
|
||||
id: string;
|
||||
heading: string;
|
||||
body: string;
|
||||
media: any[]; // Define a proper type for media if available
|
||||
}
|
||||
|
||||
interface ArticleEditorPaneProps {
|
||||
articleData: ArticleData | null;
|
||||
}
|
||||
|
||||
const ArticleEditorPane = ({ articleData }: ArticleEditorPaneProps) => {
|
||||
if (!articleData) {
|
||||
return (
|
||||
<Paper
|
||||
elevation={2}
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
padding: 2.5,
|
||||
borderRadius: 2,
|
||||
border: "1px solid",
|
||||
borderColor: "divider",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Выберите блок для редактирования
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Состояние для модального окна выбора существующей статьи (добавим позже)
|
||||
// const [isSelectModalOpen, setIsSelectModalOpen] = useState(false);
|
||||
return (
|
||||
<Paper
|
||||
elevation={2}
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
padding: 2.5,
|
||||
borderRadius: 2,
|
||||
border: "1px solid",
|
||||
borderColor: "divider",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<SightEdit />
|
||||
<Paper elevation={1} sx={{ padding: 2, mt: 1, width: "75%" }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
МЕДИА
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: 100,
|
||||
backgroundColor: "grey.100",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: 1,
|
||||
mb: 1,
|
||||
border: "2px dashed",
|
||||
borderColor: "grey.300",
|
||||
}}
|
||||
>
|
||||
<Typography color="text.secondary">Нет медиа</Typography>
|
||||
</Box>
|
||||
<Button variant="contained">Выбрать/Загрузить медиа</Button>
|
||||
</Paper>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
// --- Производные данные (Derived State) ---
|
||||
// --- RightWidgetTab (Parent) Component ---
|
||||
export const RightWidgetTab = observer(
|
||||
({ value, index }: { value: number; index: number }) => {
|
||||
const [rightWidgetBlocks, setRightWidgetBlocks] = useState<ArticleBlock[]>(
|
||||
mockRightWidgetBlocks
|
||||
);
|
||||
const [selectedBlockId, setSelectedBlockId] = useState<string | null>(
|
||||
mockRightWidgetBlocks[1]?.id || null
|
||||
);
|
||||
const [isSelectModalOpen, setIsSelectModalOpen] = useState(false);
|
||||
|
||||
// Блоки для отображения в сайдбаре (с локализованными именами)
|
||||
const blocksForSidebar: BlockItem[] = useMemo(() => {
|
||||
return blockItemsStructure.map((struct) => {
|
||||
let name = `Блок ${struct.id}`; // Имя по умолчанию
|
||||
|
||||
if (struct.type === "media" && struct.id === "preview_media_main") {
|
||||
name = "Превью-медиа"; // Фиксированное имя для этого блока
|
||||
} else if (struct.type === "article") {
|
||||
if (struct.linkedArticleStoreId) {
|
||||
// TODO: Найти имя в articlesStore по struct.linkedArticleStoreId
|
||||
name = `Связанная: ${struct.linkedArticleStoreId}`;
|
||||
} else {
|
||||
// Это локальная статья, берем заголовок из editSightStore
|
||||
const articleContent = sightInfo[language]?.right?.find(
|
||||
(a) => a.id === struct.id
|
||||
);
|
||||
name =
|
||||
articleContent?.heading ||
|
||||
`Статья ${struct.id.slice(-4)} (${language.toUpperCase()})`;
|
||||
}
|
||||
}
|
||||
return { ...struct, nameForSidebar: name };
|
||||
});
|
||||
}, [blockItemsStructure, language, sightInfo]);
|
||||
|
||||
// Данные выбранного блока (структура + контент)
|
||||
const selectedBlockData = useMemo(() => {
|
||||
if (!selectedBlockId) return null;
|
||||
const structure = blockItemsStructure.find(
|
||||
(b) => b.id === selectedBlockId
|
||||
);
|
||||
if (!structure) return null;
|
||||
|
||||
if (structure.type === "article" && !structure.linkedArticleStoreId) {
|
||||
const content = sightInfo[language]?.right?.find(
|
||||
(a) => a.id === selectedBlockId
|
||||
);
|
||||
return {
|
||||
structure,
|
||||
content: content || { id: selectedBlockId, heading: "", body: "" }, // Заглушка, если нет контента
|
||||
};
|
||||
}
|
||||
// Для media или связанных статей пока просто структура
|
||||
return { structure, content: null };
|
||||
}, [selectedBlockId, blockItemsStructure, language, sightInfo]);
|
||||
|
||||
// --- Обработчики событий ---
|
||||
const handleSelectBlock = (blockId: string) => {
|
||||
setSelectedBlockId(blockId);
|
||||
console.log("Selected block:", blockId);
|
||||
};
|
||||
|
||||
const handleCreateNewArticle = () => {
|
||||
const newBlockId = `article_local_${Date.now()}`;
|
||||
const newBlockStructure: Omit<BlockItem, "nameForSidebar"> = {
|
||||
id: newBlockId,
|
||||
type: "article",
|
||||
};
|
||||
setBlockItemsStructure((prev) => [...prev, newBlockStructure]);
|
||||
|
||||
// Добавляем пустой контент для этой статьи во все языки в editSightStore
|
||||
const baseName = `Новая статья ${
|
||||
blockItemsStructure.filter((b) => b.type === "article").length + 1
|
||||
}`;
|
||||
["ru", "en", "zh"].forEach((lang) => {
|
||||
const currentLang = lang as Language;
|
||||
if (
|
||||
editSightStore.sightInfo[currentLang] &&
|
||||
!editSightStore.sightInfo[currentLang].right?.find(
|
||||
(r) => r.id === newBlockId
|
||||
)
|
||||
) {
|
||||
editSightStore.sightInfo[currentLang].right.push({
|
||||
id: newBlockId,
|
||||
heading: `${baseName} (${currentLang.toUpperCase()})`,
|
||||
body: `Содержимое для ${baseName} (${currentLang.toUpperCase()})...`,
|
||||
});
|
||||
}
|
||||
});
|
||||
const handleCreateNew = () => {
|
||||
const newBlockId = `article_${Date.now()}`;
|
||||
setRightWidgetBlocks((prevBlocks) => [
|
||||
...prevBlocks,
|
||||
{
|
||||
id: newBlockId,
|
||||
name: `${
|
||||
prevBlocks.filter((b) => b.type === "article").length + 1
|
||||
}. Новый блок`,
|
||||
type: "article",
|
||||
},
|
||||
]);
|
||||
setSelectedBlockId(newBlockId);
|
||||
};
|
||||
|
||||
const handleHeadingChange = (newHeading: string) => {
|
||||
if (
|
||||
selectedBlockData &&
|
||||
selectedBlockData.structure.type === "article" &&
|
||||
!selectedBlockData.structure.linkedArticleStoreId
|
||||
) {
|
||||
const blockId = selectedBlockData.structure.id;
|
||||
const langData = editSightStore.sightInfo[language];
|
||||
const article = langData?.right?.find((a) => a.id === blockId);
|
||||
if (article) {
|
||||
article.heading = newHeading;
|
||||
} else if (langData) {
|
||||
// Если статьи еще нет, добавляем
|
||||
langData.right.push({ id: blockId, heading: newHeading, body: "" });
|
||||
}
|
||||
// Обновить имя в сайдбаре (т.к. blocksForSidebar пересчитается)
|
||||
// Для этого достаточно, чтобы sightInfo был observable и blocksForSidebar от него зависел
|
||||
}
|
||||
const handleSelectExisting = () => {
|
||||
setIsSelectModalOpen(true);
|
||||
};
|
||||
|
||||
const handleBodyChange = (newBody: string) => {
|
||||
if (
|
||||
selectedBlockData &&
|
||||
selectedBlockData.structure.type === "article" &&
|
||||
!selectedBlockData.structure.linkedArticleStoreId
|
||||
) {
|
||||
const blockId = selectedBlockData.structure.id;
|
||||
const langData = editSightStore.sightInfo[language];
|
||||
const article = langData?.right?.find((a) => a.id === blockId);
|
||||
if (article) {
|
||||
article.body = newBody;
|
||||
} else if (langData) {
|
||||
// Если статьи еще нет, добавляем
|
||||
langData.right.push({ id: blockId, heading: "", body: newBody });
|
||||
}
|
||||
}
|
||||
const handleCloseSelectModal = () => {
|
||||
setIsSelectModalOpen(false);
|
||||
};
|
||||
|
||||
const handleDeleteBlock = (blockIdToDelete: string) => {
|
||||
setBlockItemsStructure((prev) =>
|
||||
prev.filter((b) => b.id !== blockIdToDelete)
|
||||
);
|
||||
// Удаляем контент из editSightStore для всех языков
|
||||
["ru", "en", "zh"].forEach((lang) => {
|
||||
const currentLang = lang as Language;
|
||||
if (editSightStore.sightInfo[currentLang]) {
|
||||
editSightStore.sightInfo[currentLang].right =
|
||||
editSightStore.sightInfo[currentLang].right?.filter(
|
||||
(r) => r.id !== blockIdToDelete
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedBlockId === blockIdToDelete) {
|
||||
setSelectedBlockId(
|
||||
blockItemsStructure.length > 1
|
||||
? blockItemsStructure.filter((b) => b.id !== blockIdToDelete)[0]?.id
|
||||
: null
|
||||
);
|
||||
const handleSelectArticle = (articleId: string) => {
|
||||
// @ts-ignore
|
||||
const article = articlesStore.articles.find((a) => a.id === articleId);
|
||||
if (article) {
|
||||
const newBlockId = `article_linked_${article.id}_${Date.now()}`;
|
||||
setRightWidgetBlocks((prevBlocks) => [
|
||||
...prevBlocks,
|
||||
{
|
||||
id: newBlockId,
|
||||
name: `${
|
||||
prevBlocks.filter((b) => b.type === "article").length + 1
|
||||
}. ${article.service_name}`,
|
||||
type: "article",
|
||||
linkedArticleId: article.id,
|
||||
},
|
||||
]);
|
||||
setSelectedBlockId(newBlockId);
|
||||
}
|
||||
handleCloseSelectModal();
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
console.log(
|
||||
"Сохранение Right Widget:",
|
||||
JSON.stringify(editSightStore.sightInfo, null, 2)
|
||||
);
|
||||
// Здесь будет логика отправки editSightStore.sightInfo на сервер
|
||||
alert("Данные для сохранения (см. консоль)");
|
||||
console.log("Saving right widget...");
|
||||
// Implement save logic here, e.g., send data to an API
|
||||
};
|
||||
|
||||
// --- Инициализация контента в сторе для initialBlockStructures (если его там нет) ---
|
||||
useEffect(() => {
|
||||
initialBlockStructures.forEach((struct) => {
|
||||
if (struct.type === "article" && !struct.linkedArticleStoreId) {
|
||||
const baseName = `Статья ${struct.id.split("_")[1]}`; // Пример "История" или "Факты"
|
||||
["ru", "en", "zh"].forEach((lang) => {
|
||||
const currentLang = lang as Language;
|
||||
if (
|
||||
editSightStore.sightInfo[currentLang] &&
|
||||
!editSightStore.sightInfo[currentLang].right?.find(
|
||||
(r) => r.id === struct.id
|
||||
)
|
||||
) {
|
||||
editSightStore.sightInfo[currentLang].right?.push({
|
||||
id: struct.id,
|
||||
heading: `${baseName} (${currentLang.toUpperCase()})`, // Например: "История (RU)"
|
||||
body: `Начальное содержимое для ${baseName} на ${currentLang.toUpperCase()}.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []); // Запускается один раз при монтировании
|
||||
// Determine the current block data to pass to the editor pane
|
||||
const currentBlockToEdit = selectedBlockId
|
||||
? selectedBlockId === mockSelectedBlockData.id
|
||||
? mockSelectedBlockData
|
||||
: {
|
||||
id: selectedBlockId,
|
||||
heading:
|
||||
rightWidgetBlocks.find((b) => b.id === selectedBlockId)?.name ||
|
||||
"Заголовок...",
|
||||
body: "Содержимое...",
|
||||
media: [],
|
||||
}
|
||||
: null;
|
||||
|
||||
// Get list of already linked article IDs
|
||||
const linkedArticleIds = rightWidgetBlocks
|
||||
.filter((block) => block.linkedArticleId)
|
||||
.map((block) => block.linkedArticleId as string);
|
||||
|
||||
return (
|
||||
<TabPanel value={value} index={index}>
|
||||
<LanguageSwitcher />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
minHeight: "calc(100vh - 200px)",
|
||||
minHeight: "calc(100vh - 200px)", // Adjust as needed
|
||||
gap: 2,
|
||||
paddingBottom: "70px",
|
||||
paddingBottom: "70px", // Space for the save button
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<BackButton />
|
||||
|
||||
<Box sx={{ display: "flex", flexGrow: 1, gap: 2.5, minHeight: 0 }}>
|
||||
{/* Компонент сайдбара списка блоков */}
|
||||
<Paper
|
||||
elevation={1}
|
||||
sx={{
|
||||
width: 280,
|
||||
padding: 1.5,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Блоки
|
||||
</Typography>
|
||||
<Box sx={{ flexGrow: 1, overflowY: "auto" }}>
|
||||
{blocksForSidebar.map((block) => (
|
||||
<Button
|
||||
key={block.id}
|
||||
fullWidth
|
||||
variant={
|
||||
selectedBlockId === block.id ? "contained" : "outlined"
|
||||
}
|
||||
onClick={() => handleSelectBlock(block.id)}
|
||||
sx={{
|
||||
justifyContent: "flex-start",
|
||||
mb: 0.5,
|
||||
textTransform: "none",
|
||||
}}
|
||||
>
|
||||
{block.nameForSidebar}
|
||||
</Button>
|
||||
))}
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleCreateNewArticle}
|
||||
sx={{ mt: 1 }}
|
||||
>
|
||||
+ Новая статья
|
||||
</Button>
|
||||
{/* TODO: Кнопка "Выбрать существующую" */}
|
||||
</Paper>
|
||||
<Box sx={{ display: "flex", flexGrow: 1, gap: 2.5 }}>
|
||||
<ArticleListSidebar
|
||||
blocks={rightWidgetBlocks}
|
||||
selectedBlockId={selectedBlockId}
|
||||
onSelectBlock={handleSelectBlock}
|
||||
onCreateNew={handleCreateNew}
|
||||
onSelectExisting={handleSelectExisting}
|
||||
/>
|
||||
|
||||
{/* Компонент редактора выбранного блока */}
|
||||
<Paper
|
||||
elevation={1}
|
||||
sx={{ flexGrow: 1, padding: 2.5, overflowY: "auto" }}
|
||||
>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Редактор блока ({language.toUpperCase()})
|
||||
</Typography>
|
||||
{selectedBlockData ? (
|
||||
<Box>
|
||||
<Typography variant="subtitle1">
|
||||
ID: {selectedBlockData.structure.id}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
Тип: {selectedBlockData.structure.type}
|
||||
</Typography>
|
||||
{selectedBlockData.structure.type === "media" && (
|
||||
<Box
|
||||
my={2}
|
||||
p={2}
|
||||
border="1px dashed grey"
|
||||
height={150}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Typography color="textSecondary">
|
||||
Загрузчик медиа для "{selectedBlockData.structure.id}"
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{selectedBlockData.structure.type === "article" &&
|
||||
!selectedBlockData.structure.linkedArticleStoreId &&
|
||||
selectedBlockData.content && (
|
||||
<Box mt={2}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Заголовок статьи"
|
||||
value={selectedBlockData.content.heading}
|
||||
onChange={(e) => handleHeadingChange(e.target.value)}
|
||||
sx={{ mb: 2 }}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={8}
|
||||
label="Текст статьи"
|
||||
value={selectedBlockData.content.body}
|
||||
onChange={(e) => handleBodyChange(e.target.value)}
|
||||
sx={{ mb: 2 }}
|
||||
// Здесь позже можно будет вставить SightEdit
|
||||
/>
|
||||
{/* TODO: Секция медиа для статьи */}
|
||||
<Button
|
||||
color="error"
|
||||
variant="outlined"
|
||||
onClick={() =>
|
||||
handleDeleteBlock(selectedBlockData.structure.id)
|
||||
}
|
||||
>
|
||||
Удалить эту статью
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
{selectedBlockData.structure.type === "article" &&
|
||||
selectedBlockData.structure.linkedArticleStoreId && (
|
||||
<Box mt={2}>
|
||||
<Typography>
|
||||
Это связанная статья:{" "}
|
||||
{selectedBlockData.structure.linkedArticleStoreId}
|
||||
</Typography>
|
||||
{/* TODO: Кнопки "Открепить", "Удалить из списка" */}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<Typography color="textSecondary">
|
||||
Выберите блок для редактирования
|
||||
</Typography>
|
||||
)}
|
||||
</Paper>
|
||||
<ArticleEditorPane articleData={currentBlockToEdit} />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
padding: 2,
|
||||
backgroundColor: "background.paper",
|
||||
borderTop: "1px solid",
|
||||
borderColor: "divider",
|
||||
zIndex: 10,
|
||||
backgroundColor: "background.paper", // Ensure button is visible
|
||||
width: "100%", // Cover the full width to make it a sticky footer
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
onClick={handleSave}
|
||||
size="large"
|
||||
>
|
||||
<Button variant="contained" color="success" onClick={handleSave}>
|
||||
Сохранить изменения
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{/* <SelectArticleModal open={isSelectModalOpen} ... /> */}
|
||||
|
||||
<SelectArticleModal
|
||||
open={isSelectModalOpen}
|
||||
onClose={handleCloseSelectModal}
|
||||
onSelectArticle={handleSelectArticle}
|
||||
linkedArticleIds={linkedArticleIds}
|
||||
/>
|
||||
</TabPanel>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user