import {useEffect, useRef, useState} from "react"; import {useNavigate, useParams} from "react-router-dom"; import {BytescaleUploader} from "@/components/BytescaleUploader/BytescaleUploader"; import EditorJS, {OutputData} from "@editorjs/editorjs"; import Header from "@editorjs/header"; import List from "@editorjs/list"; import ImageTool from "@editorjs/image"; import Quote from "@editorjs/quote"; import Table from "@editorjs/table"; import CodeTool from "@editorjs/code"; import Embed from "@editorjs/embed"; import Delimiter from "@editorjs/delimiter"; import axios from "axios"; import { createBlogApi, updateBlogApi, getBlogByIdApi, uploadImageApi, } from "@/api/blog"; import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"; import {Input} from "@/components/ui/input"; import {Button} from "@/components/ui/button"; export default function BlogEditorPage() { const baseURL = import.meta.env.VITE_API_URL; const {id} = useParams(); const navigate = useNavigate(); const editorRef = useRef(null); const hasInitialized = useRef(false); const hasRenderedContent = useRef(false); const [title, setTitle] = useState(""); const [writer, setWriter] = useState(""); const [coverImage, setCoverImage] = useState(""); const [loading, setLoading] = useState(false); const isEdit = Boolean(id); useEffect(() => { if (hasInitialized.current) return; hasInitialized.current = true; let editor: EditorJS; const initEditor = async () => { editor = new EditorJS({ holder: "editorjs", placeholder: "Write blog content...", tools: { header: { class: Header, inlineToolbar: true, config: { placeholder: "Enter heading", levels: [1, 2, 3, 4], defaultLevel: 2, }, }, list: { class: List, inlineToolbar: true, config: { defaultStyle: "unordered", }, }, quote: Quote, table: Table, code: CodeTool, embed: Embed, delimiter: Delimiter, image: { class: ImageTool, config: { uploader: { uploadByFile: async (file: File) => { if (file.size > 5 * 1024 * 1024) { alert("File is too large (Max 5MB)"); return {success: 0, file: {url: ""}}; } const formData = new FormData(); formData.append("file", file); formData.append("folderPath", "/blog"); try { const response = await axios.post( `${baseURL}/upload`, formData, { headers: { "Content-Type": "multipart/form-data", }, }, ); return { success: 1, file: {url: response.data.fileUrl}, }; } catch (e: any) { console.error("EditorJS Image Upload Error:", e); const errorMessage = e.response?.data?.error || e.message || "Upload failed"; alert(`Upload Error: ${errorMessage}`); return { success: 0, file: {url: ""}, }; } }, }, }, }, }, }); await editor.isReady; editorRef.current = editor; if (isEdit && id && !hasRenderedContent.current) { try { const res = await getBlogByIdApi(Number(id)); setTitle(res.title); setWriter(res.writer); setCoverImage(res.image || ""); if (res.content) { await editor.blocks.clear(); await editor.render(res.content); hasRenderedContent.current = true; } } catch (err) { console.error(err); } } }; initEditor(); }, [id, isEdit]); const handleSave = async () => { if (!editorRef.current) return; setLoading(true); try { const content: OutputData = await editorRef.current.save(); const payload = { title, writer, image: coverImage, content, isActive: true, }; if (isEdit) { await updateBlogApi(Number(id), payload); } else { await createBlogApi(payload); } navigate("/blog"); } catch (err) { console.error(err); } finally { setLoading(false); } }; return (
{isEdit ? "Edit Blog" : "Create Blog"} setTitle(e.target.value)} /> setWriter(e.target.value)} />
setCoverImage(url)} /> {coverImage && ( cover )}
); }