import { Modal, Box, Button, TextField, Typography } from "@mui/material"; import { articleStore } from "../../../store/ArticleStore"; import { observer } from "mobx-react-lite"; import { useForm } from "@refinedev/react-hook-form"; import { Controller } from "react-hook-form"; import "easymde/dist/easymde.min.css"; import { memo, useMemo, useEffect, useCallback, useState } from "react"; import { MarkdownEditor } from "../../MarkdownEditor"; import { Edit } from "@refinedev/mui"; import { EVERY_LANGUAGE, languageStore } from "../../../store/LanguageStore"; import { LanguageSwitch } from "../../LanguageSwitch/index"; import { useDropzone } from "react-dropzone"; import { ALLOWED_IMAGE_TYPES, ALLOWED_VIDEO_TYPES, } from "../../media/MediaFormUtils"; import { TOKEN_KEY, axiosInstance } from "@providers"; import { LinkedItems } from "../../../components/LinkedItems"; import { mediaFields, MediaItem } from "../../../pages/article/types"; const MemoizedSimpleMDE = memo(MarkdownEditor); type MediaFile = { file: File; preview: string; uploading: boolean; media_id?: number; }; const style = { marginLeft: "auto", marginRight: "auto", //position: "absolute", //top: "50%", //left: "50%", //transform: "translate(-50%, -50%)", width: "60%", bgcolor: "background.paper", border: "2px solid #000", boxShadow: 24, p: 4 }; export const ArticleEditModal = observer(() => { const { language } = languageStore; const [articleData, setArticleData] = useState({ heading: EVERY_LANGUAGE(language), body: EVERY_LANGUAGE(language), }); const { articleModalOpen, setArticleModalOpenAction, selectedArticleId } = articleStore; const [mediaFiles, setMediaFiles] = useState([]); const [refresh, setRefresh] = useState(0); useEffect(() => { return () => { setArticleModalOpenAction(false); }; }, []); // Load existing media files when editing an article const loadExistingMedia = async () => { console.log("Called loadExistingMedia") if (selectedArticleId) { try { const response = await axiosInstance.get( `${ import.meta.env.VITE_KRBL_API }/article/${selectedArticleId}/media` ); const existingMedia = response.data; // Convert existing media to MediaFile format const mediaFiles = await Promise.all( existingMedia.map(async (media: any) => { const response = await fetch( `${import.meta.env.VITE_KRBL_MEDIA}${ media.id }/download?token=${localStorage.getItem(TOKEN_KEY)}` ); const blob = await response.blob(); const file = new File([blob], media.filename, { type: media.media_type === 1 ? "image/jpeg" : "video/mp4", }); return { file, preview: URL.createObjectURL(blob), uploading: false, mediaId: media.id, }; }) ); setMediaFiles(mediaFiles); setRefresh(refresh+1); } catch (error) { console.error("Error loading existing media:", error); } } }; useEffect(() => { loadExistingMedia(); }, [selectedArticleId]); const { register, control, formState: { errors }, saveButtonProps, reset, setValue, watch, } = useForm({ refineCoreProps: { resource: "article", id: selectedArticleId ?? undefined, action: "edit", redirect: false, onMutationSuccess: async () => { try { // Upload new media files const newMediaFiles = mediaFiles.filter((file) => !file.media_id); const existingMediaAmount = mediaFiles.filter((file) => file.media_id).length; const mediaIds = await Promise.all( newMediaFiles.map(async (mediaFile) => { return await uploadMedia(mediaFile); }) ); // Associate all media with the article await Promise.all( mediaIds.map((mediaId, index) => axiosInstance.post( `${ import.meta.env.VITE_KRBL_API }/article/${selectedArticleId}/media/`, { media_id: mediaId, media_order: index + existingMediaAmount + 1, } ) ) ); setArticleModalOpenAction(false); reset(); window.location.reload(); } catch (error) { console.error("Error handling media:", error); } }, meta: { headers: { "Accept-Language": language, }, }, }, }); useEffect(() => { if (articleData.heading[language]) { setValue("heading", articleData.heading[language]) } if (articleData.body[language]) { setValue("body", articleData.body[language]) } }, [language, articleData, setValue]); const handleLanguageChange = () => { setArticleData((prevData) => ({ ...prevData, heading: { ...prevData.heading, [language]: watch("heading") ?? "" }, body: { ...prevData.body, [language]: watch("body") ?? "" } })); }; const simpleMDEOptions = useMemo( () => ({ placeholder: "Введите контент в формате Markdown...", spellChecker: false, }), [] ); const onDrop = useCallback((acceptedFiles: File[]) => { const newFiles = acceptedFiles.map((file) => ({ file, preview: URL.createObjectURL(file), uploading: false, })); setMediaFiles((prev) => [...prev, ...newFiles]); }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { "image/*": ALLOWED_IMAGE_TYPES, "video/*": ALLOWED_VIDEO_TYPES, }, multiple: true, }); const uploadMedia = async (mediaFile: MediaFile) => { const formData = new FormData(); formData.append("media_name", mediaFile.file.name); formData.append("filename", mediaFile.file.name); formData.append( "type", mediaFile.file.type.startsWith("image/") ? "1" : "2" ); formData.append("file", mediaFile.file); const response = await axiosInstance.post( `${import.meta.env.VITE_KRBL_API}/media`, formData ); return response.data.id; }; const removeMedia = async (index: number) => { const mediaFile = mediaFiles[index]; // If it's an existing media file (has mediaId), delete it from the server if (mediaFile.media_id) { try { await axiosInstance.delete( `${import.meta.env.VITE_KRBL_API}/media/${mediaFile.media_id}` ); } catch (error) { console.error("Error deleting media:", error); return; // Don't remove from UI if server deletion failed } } // Remove from UI and cleanup setMediaFiles((prev) => { const newFiles = [...prev]; URL.revokeObjectURL(newFiles[index].preview); newFiles.splice(index, 1); return newFiles; }); }; return ( setArticleModalOpenAction(false)} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" sx={{overflow: "auto"}} > Редактирование статьи} headerProps={{ sx: { fontSize: "50px", }, }} saveButtonProps={saveButtonProps} > ( )} /> {selectedArticleId && ( type="edit" parentId={selectedArticleId} parentResource="article" childResource="media" fields={mediaFields} title="медиа" dontRecurse onUpdate={loadExistingMedia} /> )} {/* Dropzone для медиа файлов */} {isDragActive ? "Перетащите файлы сюда..." : "Перетащите файлы сюда или кликните для выбора"} {/* Превью загруженных файлов */} {mediaFiles.map((mediaFile, index) => ( {mediaFile.file.type.startsWith("image/") ? ( {mediaFile.file.name} ) : ( {mediaFile.file.name} )} ))} ); });