diff --git a/public/images/tags/introduction.png b/public/images/tags/introduction.png new file mode 100644 index 0000000..84dc6eb Binary files /dev/null and b/public/images/tags/introduction.png differ diff --git a/src/app/create-post/page.tsx b/src/app/create-post/page.tsx index 68f2b82..5af5304 100644 --- a/src/app/create-post/page.tsx +++ b/src/app/create-post/page.tsx @@ -180,7 +180,7 @@ export default function CreatePostPage() { }; return ( - <div className="absolute flex items-top mt-40 justify-center top-0 left-0 w-screen h-screen"> + <div className="static flex items-top mt-10 justify-center top-0 left-0 gap-16"> <Form className="w-full max-w-2xl flex flex-col gap-4" validationErrors={errors} @@ -282,6 +282,7 @@ export default function CreatePostPage() { <Spacer /> + <p>Tags</p> {mounted && ( <Select styles={styles} diff --git a/src/components/editor/EditorMenuBar.tsx b/src/components/editor/EditorMenuBar.tsx index 9d853a1..960aef4 100644 --- a/src/components/editor/EditorMenuBar.tsx +++ b/src/components/editor/EditorMenuBar.tsx @@ -9,6 +9,7 @@ import { Bold, Code, Highlighter, + ImageIcon, Italic, LinkIcon, Minus, @@ -21,6 +22,8 @@ import { Undo, } from "lucide-react"; import EditorMenuButton from "./EditorMenuButton"; +import { toast } from "react-toastify"; +import { getCookie } from "@/helpers/cookie"; type EditorMenuProps = { editor: Editor | null; @@ -41,6 +44,73 @@ export default function EditorMenuBar({ editor }: EditorMenuProps) { } }; + const addImage = () => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = "image/*"; + input.style.display = "none"; + + input.addEventListener("change", handleImageUpload); + + document.body.appendChild(input); + input.click(); + + // Clean up after the dialog closes + input.addEventListener("blur", () => document.body.removeChild(input)); + }; + + async function handleImageUpload(event: Event) { + const target = event.target as HTMLInputElement; + if (!target.files || target.files.length === 0) return; + + const file = target.files[0]; + const filesize = parseInt((file.size / 1024 / 1024).toFixed(4)); + + const allowedTypes = [ + "image/jpeg", // JPEG images + "image/png", // PNG images + "image/gif", // GIF images + "image/webp", // WebP images + "image/svg+xml", // SVG images + ]; + + if (!allowedTypes.includes(file.type)) { + toast.error("Invalid file format"); + return false; + } + + if (filesize > 8) { + toast.error("Image is too big"); + return false; + } + + const formData = new FormData(); + formData.append("upload", file); + + fetch( + process.env.NEXT_PUBLIC_MODE === "PROD" + ? "https://d2jam.com/api/v1/image" + : "http://localhost:3005/api/v1/image", + { + method: "POST", + body: formData, + headers: { + authorization: `Bearer ${getCookie("token")}`, + }, + credentials: "include", + } + ).then((response) => { + if (response.ok) { + response.json().then((data) => { + toast.success(data.message); + editor?.commands.setImage({ src: data.data }); + }); + } else { + toast.error("Failed to upload image"); + } + }); + } + const buttons = [ { icon: <Bold size={20} />, @@ -90,6 +160,12 @@ export default function EditorMenuBar({ editor }: EditorMenuProps) { disabled: false, isActive: editor.isActive("link"), }, + { + icon: <ImageIcon size={20} />, + onClick: addImage, + disabled: false, + isActive: false, + }, { icon: <Minus size={20} />, onClick: () => editor.chain().focus().setHorizontalRule().run(),