import { Box, Button, Typography, TextField, Slider, Stack, } from "@mui/material"; import { BackButton, createSightStore, editSightStore, languageStore, TabPanel, SelectMediaDialog, UploadMediaDialog, Media, } from "@shared"; import { LanguageSwitcher, MediaArea, MediaAreaForSight, ReactMarkdownEditor, DeleteModal, } from "@widgets"; import { Plus, Save, Trash2, Unlink, X } from "lucide-react"; import { observer } from "mobx-react-lite"; import { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { MediaViewer } from "../../MediaViewer/index"; import { toast } from "react-toastify"; import { authInstance } from "@shared"; import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; import { SightFramePreview } from "../RightWidgetTab/SightFramePreview"; type MediaItemShared = { id: string; filename: string; media_name?: string; media_type: number; }; export const CreateRightTab = observer( ({ value, index }: { value: number; index: number }) => { const { sight, createNewRightArticle, updateRightArticleInfo, linkPreviewMedia, unlinkPreviewMedia, createLinkWithRightArticle, deleteRightArticleMedia, unlinkRightAritcle, deleteRightArticle, createSight, updateRightArticles, updateSightInfo, } = createSightStore; const { language } = languageStore; const navigate = useNavigate(); const { setFileToUpload, setUploadMediaOpen, uploadMediaOpen } = editSightStore; const [activeArticleIndex, setActiveArticleIndex] = useState( null, ); const [type, setType] = useState<"article" | "media">("media"); const [previewSection, setPreviewSection] = useState(-1); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [isSelectMediaDialogOpen, setIsSelectMediaDialogOpen] = useState(false); const [mediaTarget, setMediaTarget] = useState< "sightPreview" | "rightArticle" | null >(null); const [previewMedia, setPreviewMedia] = useState(null); const shortNameRef = useRef(null); const insertNewline = () => { const input = shortNameRef.current; const currentValue = sight[language].name || ""; if (!input) { updateSightInfo({ name: currentValue + "\n" }, language); return; } const start = input.selectionStart ?? currentValue.length; const end = input.selectionEnd ?? start; const newValue = currentValue.slice(0, start) + "\n" + currentValue.slice(end); updateSightInfo({ name: newValue }, language); requestAnimationFrame(() => { if (shortNameRef.current) { shortNameRef.current.selectionStart = start + 1; shortNameRef.current.selectionEnd = start + 1; shortNameRef.current.focus(); } }); }; useEffect(() => { if (sight.preview_media) { const fetchMedia = async () => { const response = await authInstance.get( `/media/${sight.preview_media}`, ); setPreviewMedia(response.data); }; fetchMedia(); } }, [sight.preview_media]); useEffect(() => { if ( activeArticleIndex !== null && activeArticleIndex >= sight[language].right.length ) { setActiveArticleIndex(null); setType("media"); setPreviewSection(-1); } }, [language, sight[language].right, activeArticleIndex]); const handleSave = async () => { try { const newSightId = await createSight(language); console.log("[handleSave] newSightId:", newSightId, "needLeaveAgree:", createSightStore.needLeaveAgree); toast.success("Достопримечательность успешно создана!"); navigate(`/sight/${newSightId}/edit`); console.log("[handleSave] navigate called to:", `/sight/${newSightId}/edit`); } catch (error) { console.error("Failed to save sight:", error); toast.error("Ошибка при создании достопримечательности."); } }; const handleDisplayArticleFromList = (idx: number) => { setActiveArticleIndex(idx); setType("article"); setPreviewSection(idx); }; const handleCreateNewLocalArticle = async () => { try { const newArticleId = await createNewRightArticle(); const newIndex = sight[language].right.findIndex( (a) => a.id === newArticleId, ); const resolvedIndex = newIndex > -1 ? newIndex : sight[language].right.length - 1; setActiveArticleIndex(resolvedIndex); setType("article"); setPreviewSection(resolvedIndex); } catch (error) { toast.error("Не удалось создать новую статью."); } }; const currentRightArticle = activeArticleIndex !== null && sight[language].right[activeArticleIndex] ? sight[language].right[activeArticleIndex] : null; 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 handleUnlinkPreviewMedia = async () => { await unlinkPreviewMedia(); setPreviewMedia(null); }; const handleMediaUploaded = async (media: MediaItemShared) => { setUploadMediaOpen(false); setFileToUpload(null); if (mediaTarget === "sightPreview") { linkPreviewMedia(media.id); } else if (mediaTarget === "rightArticle" && currentRightArticle) { await createLinkWithRightArticle(media, currentRightArticle.id); } setMediaTarget(null); }; const handleDragEnd = (result: any) => { const { source, destination } = result; if (!destination) return; const sourceIndex = source.index; const destinationIndex = destination.index; if (sourceIndex === destinationIndex) return; const newRightArticles = [...sight[language].right]; const [movedArticle] = newRightArticles.splice(sourceIndex, 1); newRightArticles.splice(destinationIndex, 0, movedArticle); updateRightArticles(newRightArticles); }; return (

{sight[language].name}

{ setType("media"); setPreviewSection(-1); }} className={`w-full p-4 rounded-2xl cursor-pointer text-sm transition-all duration-300 ${ type === "media" ? "bg-blue-400 text-white" : "bg-gray-200 hover:bg-gray-300" }`} > Предпросмотр медиа {(provided) => ( {sight[language].right.length > 0 ? sight[language].right.map( (article, index) => ( {(provided, snapshot) => ( { handleDisplayArticleFromList( index, ); }} > {article.heading} )} ), ) : null} {provided.placeholder} )} {type === "article" && currentRightArticle ? ( activeArticleIndex !== null && updateRightArticleInfo( activeArticleIndex, language, e.target.value, currentRightArticle.body, ) } variant="outlined" fullWidth /> activeArticleIndex !== null && updateRightArticleInfo( activeArticleIndex, language, currentRightArticle.heading, mdValue || "", ) } /> { if (files.length > 0) { setFileToUpload(files[0]); setMediaTarget("rightArticle"); handleOpenUploadMedia(); } }} deleteMedia={deleteRightArticleMedia} setSelectMediaDialogOpen={() => handleOpenSelectMediaDialog("rightArticle") } /> ) : ( {sight.preview_media && ( {previewMedia && ( <> )} )} {!sight.preview_media && ( { linkPreviewMedia(mediaId); }} onFilesDrop={() => {}} contextObjectName={sight[language].name} contextType="sight" isArticle={false} /> )} )} {type === "media" && ( { const raw = e.target.value; if (raw === "") { updateSightInfo({ preview_font_size: undefined }); return; } const val = Math.max( 1, Math.min(300, Math.round(Number(raw))), ); if (Number.isFinite(val)) { updateSightInfo({ preview_font_size: val }); } }} slotProps={{ input: { min: 1, max: 300 } }} sx={{ width: "200px" }} /> { if (typeof newValue === "number") { updateSightInfo({ preview_font_size: newValue }); } }} sx={{ flexGrow: 1 }} /> )} updateSightInfo({ name: e.target.value }, language) } inputRef={shortNameRef} sx={{ flexGrow: 1 }} /> { handleDisplayArticleFromList(idx); }} previewFontSize={sight.preview_font_size} selectedSection={previewSection} onSectionChange={(section) => { setPreviewSection(section); if (section === -1) { setType("media"); } else { handleDisplayArticleFromList(section); } }} />
{ setUploadMediaOpen(false); setFileToUpload(null); setMediaTarget(null); }} contextObjectName={sight[language].name} contextType="sight" isArticle={mediaTarget === "rightArticle"} articleName={ mediaTarget === "rightArticle" && activeArticleIndex !== null ? sight[language].right[activeArticleIndex].heading : undefined } afterUpload={handleMediaUploaded} /> { setIsSelectMediaDialogOpen(false); setMediaTarget(null); }} onSelectMedia={handleMediaSelectedFromDialog} /> { try { const idx = activeArticleIndex ?? 0; await deleteRightArticle(currentRightArticle?.id || 0); setIsDeleteModalOpen(false); if (idx > 0) { setActiveArticleIndex(idx - 1); setPreviewSection(idx - 1); setType("article"); } else { setActiveArticleIndex(null); setPreviewSection(-1); setType("media"); } toast.success("Статья удалена"); } catch { toast.error("Не удалось удалить статью"); } }} onCancel={() => setIsDeleteModalOpen(false)} />
); }, );