Update media
select in EditSightPage
and CreateSightPage
This commit is contained in:
@ -13,42 +13,179 @@ import {
|
||||
languageStore,
|
||||
SelectArticleModal,
|
||||
TabPanel,
|
||||
SelectMediaDialog, // Import
|
||||
UploadMediaDialog, // Import
|
||||
} from "@shared";
|
||||
import {
|
||||
LanguageSwitcher,
|
||||
MediaArea, // Import
|
||||
MediaAreaForSight, // Import
|
||||
ReactMarkdownComponent,
|
||||
ReactMarkdownEditor,
|
||||
} from "@widgets";
|
||||
import { ImagePlus, Plus } from "lucide-react";
|
||||
import { ImagePlus, Plus, X } from "lucide-react"; // Import X
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react"; // Added useEffect
|
||||
import { MediaViewer } from "../../MediaViewer/index";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
type MediaItemShared = {
|
||||
// Define if not already available from @shared
|
||||
id: string;
|
||||
filename: string;
|
||||
media_name?: string;
|
||||
media_type: number;
|
||||
};
|
||||
|
||||
// --- RightWidgetTab (Parent) Component ---
|
||||
export const CreateRightTab = observer(
|
||||
({ value, index }: { value: number; index: number }) => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const { sight, createNewRightArticle, updateRightArticleInfo } =
|
||||
createSightStore;
|
||||
const {
|
||||
sight,
|
||||
createNewRightArticle,
|
||||
updateRightArticleInfo,
|
||||
linkPreviewMedia,
|
||||
unlinkPreviewMedia,
|
||||
createLinkWithRightArticle,
|
||||
deleteRightArticleMedia,
|
||||
setFileToUpload, // From store
|
||||
setUploadMediaOpen, // From store
|
||||
uploadMediaOpen, // From store
|
||||
unlinkRightAritcle, // Corrected spelling
|
||||
deleteRightArticle,
|
||||
linkExistingRightArticle,
|
||||
createSight,
|
||||
clearCreateSight, // For resetting form
|
||||
} = createSightStore;
|
||||
const { language } = languageStore;
|
||||
const [articleDialogOpen, setArticleDialogOpen] = useState(false);
|
||||
|
||||
const [selectArticleDialogOpen, setSelectArticleDialogOpen] =
|
||||
useState(false);
|
||||
const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>(
|
||||
null
|
||||
);
|
||||
const [type, setType] = useState<"article" | "media">("media");
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
|
||||
const [isSelectMediaDialogOpen, setIsSelectMediaDialogOpen] =
|
||||
useState(false);
|
||||
const [mediaTarget, setMediaTarget] = useState<
|
||||
"sightPreview" | "rightArticle" | null
|
||||
>(null);
|
||||
|
||||
// Reset activeArticleIndex if language changes and index is out of bounds
|
||||
useEffect(() => {
|
||||
if (
|
||||
activeArticleIndex !== null &&
|
||||
activeArticleIndex >= sight[language].right.length
|
||||
) {
|
||||
setActiveArticleIndex(null);
|
||||
setType("media"); // Default back to media preview if selected article disappears
|
||||
}
|
||||
}, [language, sight[language].right, activeArticleIndex]);
|
||||
|
||||
const openMenu = Boolean(anchorEl);
|
||||
const handleClickMenu = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
const handleCloseMenu = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleSave = () => {
|
||||
console.log("Saving right widget...");
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
await createSight(language);
|
||||
toast.success("Достопримечательность успешно создана!");
|
||||
clearCreateSight(); // Reset form
|
||||
setActiveArticleIndex(null);
|
||||
setType("media");
|
||||
// Potentially navigate away: history.push('/sights-list');
|
||||
} catch (error) {
|
||||
console.error("Failed to save sight:", error);
|
||||
toast.error("Ошибка при создании достопримечательности.");
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectArticle = (index: number) => {
|
||||
setActiveArticleIndex(index);
|
||||
const handleDisplayArticleFromList = (idx: number) => {
|
||||
setActiveArticleIndex(idx);
|
||||
setType("article");
|
||||
};
|
||||
|
||||
const handleCreateNewLocalArticle = async () => {
|
||||
handleCloseMenu();
|
||||
try {
|
||||
const newArticleId = await createNewRightArticle();
|
||||
// Automatically select the new article if ID is returned
|
||||
const newIndex = sight[language].right.findIndex(
|
||||
(a) => a.id === newArticleId
|
||||
);
|
||||
if (newIndex > -1) {
|
||||
setActiveArticleIndex(newIndex);
|
||||
setType("article");
|
||||
} else {
|
||||
// Fallback if findIndex fails (should not happen if store updates correctly)
|
||||
setActiveArticleIndex(sight[language].right.length - 1);
|
||||
setType("article");
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("Не удалось создать новую статью.");
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectExistingArticleAndLink = async (
|
||||
selectedArticleId: number
|
||||
) => {
|
||||
try {
|
||||
await linkExistingRightArticle(selectedArticleId);
|
||||
setSelectArticleDialogOpen(false); // Close dialog
|
||||
const newIndex = sight[language].right.findIndex(
|
||||
(a) => a.id === selectedArticleId
|
||||
);
|
||||
if (newIndex > -1) {
|
||||
setActiveArticleIndex(newIndex);
|
||||
setType("article");
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("Не удалось привязать существующую статью.");
|
||||
}
|
||||
};
|
||||
|
||||
const currentRightArticle =
|
||||
activeArticleIndex !== null && sight[language].right[activeArticleIndex]
|
||||
? sight[language].right[activeArticleIndex]
|
||||
: null;
|
||||
|
||||
// Media Handling for Dialogs
|
||||
const handleOpenUploadMedia = () => {
|
||||
setUploadMediaOpen(true);
|
||||
};
|
||||
|
||||
const handleOpenSelectMediaDialog = (
|
||||
target: "sightPreview" | "rightArticle"
|
||||
) => {
|
||||
setMediaTarget(target);
|
||||
setIsSelectMediaDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleMediaSelectedFromDialog = async (media: MediaItemShared) => {
|
||||
setIsSelectMediaDialogOpen(false);
|
||||
if (mediaTarget === "sightPreview") {
|
||||
await linkPreviewMedia(media.id);
|
||||
} else if (mediaTarget === "rightArticle" && currentRightArticle) {
|
||||
await createLinkWithRightArticle(media, currentRightArticle.id);
|
||||
}
|
||||
setMediaTarget(null);
|
||||
};
|
||||
|
||||
const handleMediaUploaded = async (media: MediaItemShared) => {
|
||||
// After UploadMediaDialog finishes
|
||||
setUploadMediaOpen(false);
|
||||
setFileToUpload(null);
|
||||
if (mediaTarget === "sightPreview") {
|
||||
linkPreviewMedia(media.id);
|
||||
} else if (mediaTarget === "rightArticle" && currentRightArticle) {
|
||||
await createLinkWithRightArticle(media, currentRightArticle.id);
|
||||
}
|
||||
setMediaTarget(null); // Reset target
|
||||
};
|
||||
|
||||
return (
|
||||
@ -59,7 +196,7 @@ export const CreateRightTab = observer(
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
minHeight: "calc(100vh - 200px)", // Adjust as needed
|
||||
minHeight: "calc(100vh - 200px)",
|
||||
gap: 2,
|
||||
paddingBottom: "70px", // Space for the save button
|
||||
position: "relative",
|
||||
@ -68,336 +205,389 @@ export const CreateRightTab = observer(
|
||||
<BackButton />
|
||||
|
||||
<Box sx={{ display: "flex", flexGrow: 1, gap: 2.5 }}>
|
||||
{/* Left Column: Navigation & Article List */}
|
||||
<Box className="flex flex-col w-[75%] gap-2">
|
||||
<Box className="w-full flex gap-2 ">
|
||||
<Box className="relative w-[20%] h-[70vh] flex flex-col rounded-2xl overflow-y-auto gap-3 border border-gray-300 p-3">
|
||||
<Box className="relative w-[20%] h-[70vh] flex flex-col rounded-2xl overflow-y-auto gap-3 border border-gray-300 p-3">
|
||||
<Box className="flex flex-col gap-3 max-h-[60vh] overflow-y-auto">
|
||||
<Box
|
||||
onClick={() => {
|
||||
setType("media");
|
||||
// setActiveArticleIndex(null); // Optional: deselect article when switching to general media view
|
||||
}}
|
||||
className="w-full bg-green-200 p-4 rounded-2xl cursor-pointer text-sm hover:bg-gray-300 transition-all duration-300"
|
||||
className={`w-full p-4 rounded-2xl cursor-pointer text-sm hover:bg-gray-300 transition-all duration-300 ${
|
||||
type === "media"
|
||||
? "bg-green-300 font-semibold"
|
||||
: "bg-green-200"
|
||||
}`}
|
||||
>
|
||||
<Typography>Предпросмотр медиа</Typography>
|
||||
</Box>
|
||||
|
||||
{sight[language].right.map((article, index) => (
|
||||
{sight[language].right.map((article, artIdx) => (
|
||||
<Box
|
||||
key={index}
|
||||
className="w-full bg-gray-200 p-4 rounded-2xl text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300"
|
||||
onClick={() => {
|
||||
handleSelectArticle(index);
|
||||
setType("article");
|
||||
}}
|
||||
key={article.id || artIdx} // article.id should be preferred
|
||||
className={`w-full p-4 rounded-2xl text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300 ${
|
||||
type === "article" && activeArticleIndex === artIdx
|
||||
? "bg-blue-300 font-semibold"
|
||||
: "bg-gray-200"
|
||||
}`}
|
||||
onClick={() => handleDisplayArticleFromList(artIdx)}
|
||||
>
|
||||
<Typography>{article.heading}</Typography>
|
||||
<Typography noWrap title={article.heading}>
|
||||
{article.heading || "Без названия"}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<button
|
||||
className="w-10 h-10 bg-blue-500 rounded-full absolute bottom-5 left-5 flex items-center justify-center"
|
||||
onClick={handleClick}
|
||||
className="w-10 h-10 bg-blue-500 rounded-full absolute bottom-5 left-5 flex items-center justify-center hover:bg-blue-600"
|
||||
onClick={handleClickMenu}
|
||||
aria-controls={openMenu ? "add-article-menu" : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={openMenu ? "true" : undefined}
|
||||
>
|
||||
<Plus size={20} color="white" />
|
||||
</button>
|
||||
<Menu
|
||||
id="basic-menu"
|
||||
id="add-article-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
"aria-labelledby": "basic-button",
|
||||
}}
|
||||
sx={{
|
||||
mt: 1,
|
||||
}}
|
||||
open={openMenu}
|
||||
onClose={handleCloseMenu}
|
||||
MenuListProps={{ "aria-labelledby": "basic-button" }}
|
||||
sx={{ mt: 1 }}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
createNewRightArticle();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleCreateNewLocalArticle}>
|
||||
<Typography>Создать новую</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setArticleDialogOpen(true);
|
||||
handleClose();
|
||||
setSelectArticleDialogOpen(true);
|
||||
handleCloseMenu();
|
||||
}}
|
||||
>
|
||||
<Typography>Выбрать существующую статью</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
{type === "article" && (
|
||||
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3">
|
||||
{activeArticleIndex !== null && (
|
||||
<>
|
||||
<Box className="flex justify-end gap-2 mb-3">
|
||||
<Button variant="contained" color="primary">
|
||||
Открепить
|
||||
</Button>
|
||||
|
||||
<Button variant="contained" color="success">
|
||||
Удалить
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", gap: 3, flexGrow: 1 }}>
|
||||
{/* Левая колонка: Редактирование */}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
flex: 2,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="Название информации"
|
||||
value={
|
||||
sight[language].right[activeArticleIndex]
|
||||
.heading
|
||||
}
|
||||
onChange={(e) =>
|
||||
updateRightArticleInfo(
|
||||
activeArticleIndex,
|
||||
language,
|
||||
e.target.value,
|
||||
sight[language].right[activeArticleIndex].body
|
||||
)
|
||||
}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<ReactMarkdownEditor
|
||||
value={
|
||||
sight[language].right[activeArticleIndex].body
|
||||
}
|
||||
onChange={(value) =>
|
||||
updateRightArticleInfo(
|
||||
activeArticleIndex,
|
||||
language,
|
||||
sight[language].right[activeArticleIndex]
|
||||
.heading,
|
||||
value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
{/* Блок МЕДИА для статьи */}
|
||||
{/* <Paper elevation={1} sx={{ padding: 2, mt: 1 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
МЕДИА
|
||||
</Typography>
|
||||
{data.left.media ? (
|
||||
<Box sx={{ mb: 1 }}>
|
||||
<img
|
||||
src={data.left.media.filename}
|
||||
alt="Selected media"
|
||||
style={{
|
||||
maxWidth: "100%",
|
||||
maxHeight: "150px",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<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"
|
||||
startIcon={<ImagePlus size={18} />}
|
||||
onClick={handleOpenMediaDialog}
|
||||
>
|
||||
Выбрать/Загрузить медиа
|
||||
</Button>
|
||||
{data.left.media && (
|
||||
{/* Main content area: Article Editor or Sight Media Preview */}
|
||||
{type === "article" && currentRightArticle ? (
|
||||
<Box className="w-[80%] border border-gray-300 rounded-2xl p-3 flex flex-col gap-2 overflow-hidden">
|
||||
<Box className="flex justify-end gap-2 mb-1 flex-shrink-0">
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="warning"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
if (currentRightArticle) {
|
||||
unlinkRightAritcle(currentRightArticle.id); // Corrected function name
|
||||
setActiveArticleIndex(null);
|
||||
setType("media");
|
||||
}
|
||||
}}
|
||||
>
|
||||
Открепить
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
size="small"
|
||||
sx={{ ml: 1 }}
|
||||
onClick={() =>
|
||||
updateSightInfo(
|
||||
languageStore.language,
|
||||
{
|
||||
left: {
|
||||
heading: data.left.heading,
|
||||
body: data.left.body,
|
||||
media: null,
|
||||
},
|
||||
},
|
||||
false
|
||||
onClick={async () => {
|
||||
if (
|
||||
currentRightArticle &&
|
||||
window.confirm(
|
||||
`Удалить статью "${currentRightArticle.heading}" окончательно?`
|
||||
)
|
||||
) {
|
||||
try {
|
||||
await deleteRightArticle(currentRightArticle.id);
|
||||
setActiveArticleIndex(null);
|
||||
setType("media");
|
||||
toast.success("Статья удалена");
|
||||
} catch {
|
||||
toast.error("Не удалось удалить статью");
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Удалить
|
||||
</Button>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
overflowY: "auto",
|
||||
pt: 7,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="Название информации (правый виджет)"
|
||||
value={currentRightArticle.heading}
|
||||
onChange={(e) =>
|
||||
activeArticleIndex !== null &&
|
||||
updateRightArticleInfo(
|
||||
activeArticleIndex,
|
||||
language,
|
||||
e.target.value,
|
||||
currentRightArticle.body
|
||||
)
|
||||
}
|
||||
>
|
||||
Удалить медиа
|
||||
</Button>
|
||||
)}
|
||||
</Paper> */}
|
||||
</Box>
|
||||
</>
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
/>
|
||||
<Box sx={{ minHeight: 200, flexGrow: 1 }}>
|
||||
<ReactMarkdownEditor
|
||||
value={currentRightArticle.body}
|
||||
onChange={(mdValue) =>
|
||||
activeArticleIndex !== null &&
|
||||
updateRightArticleInfo(
|
||||
activeArticleIndex,
|
||||
language,
|
||||
currentRightArticle.heading,
|
||||
mdValue || ""
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<MediaArea
|
||||
articleId={currentRightArticle.id} // Needs a real ID
|
||||
mediaIds={currentRightArticle.media || []}
|
||||
onFilesDrop={(files) => {
|
||||
if (files.length > 0) {
|
||||
setFileToUpload(files[0]);
|
||||
setMediaTarget("rightArticle");
|
||||
handleOpenUploadMedia();
|
||||
}
|
||||
}}
|
||||
deleteMedia={deleteRightArticleMedia}
|
||||
setSelectMediaDialogOpen={() =>
|
||||
handleOpenSelectMediaDialog("rightArticle")
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
) : type === "media" ? (
|
||||
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3 relative flex justify-center items-center">
|
||||
{type === "media" && (
|
||||
<Box className="w-[80%] border border-gray-300 rounded-2xl relative">
|
||||
{sight.preview_media && (
|
||||
<>
|
||||
<Box className="absolute top-4 right-4">
|
||||
<button
|
||||
className="w-10 h-10 flex items-center justify-center"
|
||||
onClick={unlinkPreviewMedia}
|
||||
>
|
||||
<X size={20} color="red" />
|
||||
</button>
|
||||
</Box>
|
||||
|
||||
<MediaViewer
|
||||
media={{
|
||||
id: sight.preview_media || "",
|
||||
media_type: 1,
|
||||
filename: sight.preview_media || "",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!sight.preview_media && (
|
||||
<MediaAreaForSight
|
||||
onFinishUpload={(mediaId) => {
|
||||
linkPreviewMedia(mediaId);
|
||||
}}
|
||||
onFilesDrop={() => {}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{type === "media" && (
|
||||
) : (
|
||||
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3 flex justify-center items-center">
|
||||
<MediaViewer
|
||||
media={{
|
||||
id: sight.preview_media || "",
|
||||
media_type: 1,
|
||||
filename: sight.preview_media || "",
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
Выберите статью слева или секцию "Предпросмотр медиа"
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Right Column: Live Preview */}
|
||||
<Box className="w-[25%] mr-10">
|
||||
{activeArticleIndex !== null && (
|
||||
{type === "article" && currentRightArticle && (
|
||||
<Paper
|
||||
className="flex-1 flex flex-col rounded-2xl"
|
||||
elevation={2}
|
||||
sx={{ height: "75vh", overflow: "hidden" }}
|
||||
>
|
||||
<Box
|
||||
className="rounded-2xl overflow-hidden"
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "75vh",
|
||||
background: "#877361",
|
||||
borderColor: "grey.300",
|
||||
height: "100%",
|
||||
background: "#877361", // Theme background
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{type === "media" ? (
|
||||
{currentRightArticle.media &&
|
||||
currentRightArticle.media.length > 0 ? (
|
||||
<MediaViewer media={currentRightArticle.media[0]} />
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
height: 200,
|
||||
flexShrink: 0,
|
||||
backgroundColor: "rgba(0,0,0,0.1)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Typography color="white">Загрузка...</Typography>
|
||||
<ImagePlus size={48} color="white" />
|
||||
</Box>
|
||||
) : (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: 200,
|
||||
flexShrink: 0,
|
||||
backgroundColor: "rgba(0,0,0,0.1)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<ImagePlus size={48} color="white" />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
minHeight: "70px",
|
||||
background: "#877361",
|
||||
display: "flex",
|
||||
flexShrink: 0,
|
||||
alignItems: "center",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.1)",
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" color="white">
|
||||
{sight[language].right[activeArticleIndex]
|
||||
.heading || "Выберите статью"}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
px: 2,
|
||||
flexGrow: 1,
|
||||
|
||||
overflowY: "auto",
|
||||
backgroundColor: "#877361",
|
||||
color: "white",
|
||||
py: 1,
|
||||
}}
|
||||
>
|
||||
{sight[language].right[activeArticleIndex].body ? (
|
||||
<ReactMarkdownComponent
|
||||
value={
|
||||
sight[language].right[activeArticleIndex].body
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Typography
|
||||
color="rgba(255,255,255,0.7)"
|
||||
sx={{ textAlign: "center", mt: 4 }}
|
||||
>
|
||||
Предпросмотр статьи появится здесь
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
minHeight: "70px", // Fixed height for heading container
|
||||
background: "#877361", // Consistent with theme
|
||||
display: "flex",
|
||||
flexShrink: 0,
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.1)",
|
||||
px: 2,
|
||||
py: 1,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
color="white"
|
||||
noWrap
|
||||
title={currentRightArticle.heading}
|
||||
>
|
||||
{currentRightArticle.heading || "Заголовок"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
px: 2,
|
||||
py: 1,
|
||||
flexGrow: 1,
|
||||
overflowY: "auto",
|
||||
backgroundColor: "#877361",
|
||||
color: "white",
|
||||
"&::-webkit-scrollbar": { width: "8px" },
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "rgba(255,255,255,0.3)",
|
||||
borderRadius: "4px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{currentRightArticle.body ? (
|
||||
<ReactMarkdownComponent
|
||||
value={currentRightArticle.body}
|
||||
/>
|
||||
) : (
|
||||
<Typography
|
||||
color="rgba(255,255,255,0.7)"
|
||||
sx={{ textAlign: "center", mt: 4 }}
|
||||
>
|
||||
Содержимое статьи...
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
{/* Optional: Preview for sight.preview_media when type === "media" */}
|
||||
{type === "media" && sight.preview_media && (
|
||||
<Paper
|
||||
className="flex-1 flex flex-col rounded-2xl"
|
||||
elevation={2}
|
||||
sx={{ height: "75vh", overflow: "hidden" }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background: "#877361",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<MediaViewer
|
||||
media={{
|
||||
id: sight.preview_media,
|
||||
filename: sight.preview_media,
|
||||
media_type: 1,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Sticky Save Button Footer */}
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0, // ensure it spans from left
|
||||
right: 0,
|
||||
padding: 2,
|
||||
backgroundColor: "background.paper", // Ensure button is visible
|
||||
width: "100%", // Cover the full width to make it a sticky footer
|
||||
backgroundColor: "background.paper",
|
||||
borderTop: "1px solid", // Add a subtle top border
|
||||
borderColor: "divider", // Use theme's divider color
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
boxShadow: "0 -2px 5px rgba(0,0,0,0.1)", // Optional shadow
|
||||
}}
|
||||
>
|
||||
<Button variant="contained" color="success" onClick={handleSave}>
|
||||
Сохранить изменения
|
||||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
onClick={handleSave}
|
||||
size="large"
|
||||
>
|
||||
Сохранить достопримечательность
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{/*
|
||||
|
||||
{/* Modals */}
|
||||
<SelectArticleModal
|
||||
open={openedType === "article"}
|
||||
onClose={handleCloseSelectModal}
|
||||
onSelectArticle={handleSelectArticle}
|
||||
linkedArticleIds={linkedArticleIds}
|
||||
/> */}
|
||||
<SelectArticleModal
|
||||
open={articleDialogOpen}
|
||||
onClose={() => setArticleDialogOpen(false)}
|
||||
onSelectArticle={handleSelectArticle}
|
||||
open={selectArticleDialogOpen}
|
||||
onClose={() => setSelectArticleDialogOpen(false)}
|
||||
onSelectArticle={handleSelectExistingArticleAndLink}
|
||||
// Pass IDs of already linked/added right articles to exclude them from selection
|
||||
linkedArticleIds={sight[language].right.map((article) => article.id)}
|
||||
/>
|
||||
<UploadMediaDialog
|
||||
open={uploadMediaOpen} // From store
|
||||
onClose={() => {
|
||||
setUploadMediaOpen(false);
|
||||
setFileToUpload(null); // Clear file if dialog is closed without upload
|
||||
setMediaTarget(null);
|
||||
}}
|
||||
afterUpload={handleMediaUploaded} // This will use the mediaTarget
|
||||
/>
|
||||
<SelectMediaDialog
|
||||
open={isSelectMediaDialogOpen}
|
||||
onClose={() => {
|
||||
setIsSelectMediaDialogOpen(false);
|
||||
setMediaTarget(null);
|
||||
}}
|
||||
onSelectMedia={handleMediaSelectedFromDialog}
|
||||
/>
|
||||
</TabPanel>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user