2026-08-14 11:18:27 +05:30

292 lines
9.5 KiB
TypeScript

"use client";
import IconTrashLines from "@/components/icon/icon-trash-lines";
import dynamic from "next/dynamic";
import React, { useMemo, useState } from "react";
import "react-quill/dist/quill.snow.css";
import axios from "axios";
import Cookies from "universal-cookie";
import { useRouter } from "next/navigation";
import { showMessage } from "@/utils/CommonFunction.utils";
import { buildApiUrl } from "@/utils/BaseUrl.utils";
const ReactQuill = dynamic(() => import("react-quill"), {
ssr: false,
});
const formats = [
"header",
"bold",
"italic",
"underline",
"strike",
"blockquote",
"code-block",
"list",
"bullet",
"align",
"link",
"image",
"video",
];
const PostForm = () => {
const router = useRouter();
const [formData, setFormData] = useState({
title: "",
slug: "",
coverImage: null as File | null,
description: "",
date: "",
author: "",
});
const modules = useMemo(
() => ({
toolbar: {
container: [
[{ header: [1, 2, 3, false] }],
["bold", "italic", "underline", "strike"],
[{ list: "ordered" }, { list: "bullet" }],
["blockquote", "code-block"],
[{ align: [] }],
["link", "image", "video"],
["clean"],
],
handlers: {
image: function (this: any) {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.click();
input.onchange = async () => {
const file = input.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = () => {
const quill = this.quill;
const range = quill.getSelection(true);
quill.insertEmbed(
range.index,
"image",
reader.result
);
};
reader.readAsDataURL(file);
}
};
},
},
},
}),
[]
);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0] || null;
setFormData((prev) => ({
...prev,
coverImage: file,
}));
};
const handleRemoveImage = () => {
setFormData((prev) => ({
...prev,
coverImage: null,
}));
};
const handleDescriptionChange = (value: string) => {
setFormData((prev) => ({
...prev,
description: value,
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!formData.title.trim()) {
showMessage("Title is required", "error");
return;
}
try {
const cookies = new Cookies();
const token = cookies.get('token');
if (!token) {
showMessage('Access denied. Please sign in first.', 'error');
router.push('/login');
return;
}
let coverImageUrl = "";
if (formData.coverImage) {
const data = new FormData();
data.append("file", formData.coverImage);
const imageUpload = await axios.post(buildApiUrl('upload/single'), data, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${token}`,
},
});
if (imageUpload.data && imageUpload.data.success) {
coverImageUrl = imageUpload.data.data.fullUrl;
}
}
// Generate clean slug from title if not specified
const cleanSlug = formData.slug.trim()
? formData.slug.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^\w\-]+/g, '')
: formData.title.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^\w\-]+/g, '');
const createData = {
title: formData.title,
slug: cleanSlug,
image: coverImageUrl,
description: formData.description,
date: formData.date,
author: formData.author,
};
const res = await axios.post(buildApiUrl('blogs'), createData, {
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log("Create blog response:", res.data);
showMessage("Blog Created Successfully", "success");
router.push("/blog");
} catch (error: any) {
console.error("Error creating blog:", error);
showMessage(error?.response?.data?.message || "Error creating blog", "error");
}
};
return (
<form
onSubmit={handleSubmit}
className="space-y-5 max-w-4xl mx-auto p-6 bg-white rounded shadow-md"
>
<h2 className="text-xl font-bold mb-4">Create Blog</h2>
<div>
<label className="block font-medium mb-1">Blog Title</label>
<input
type="text"
name="title"
value={formData.title}
onChange={handleChange}
placeholder="Enter blog title"
className="w-full border rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 outline-none"
/>
</div>
<div>
<label className="block font-medium mb-1">Slug</label>
<input
type="text"
name="slug"
value={formData.slug}
onChange={handleChange}
placeholder="Enter slug"
className="w-full border rounded-md px-3 py-2 focus:ring-2 focus:ring-green-500 outline-none"
/>
</div>
<div>
<label className="block font-medium mb-1">Date</label>
<input
type="text"
name="date"
value={formData.date}
onChange={handleChange}
placeholder="e.g. 12 Aug 2026"
className="w-full border rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 outline-none"
/>
</div>
<div>
<label className="block font-medium mb-1">Author</label>
<input
type="text"
name="author"
value={formData.author}
onChange={handleChange}
placeholder="e.g. Gisselle"
className="w-full border rounded-md px-3 py-2 focus:ring-2 focus:ring-purple-500 outline-none"
/>
</div>
<div>
<label className="block font-medium mb-1">Cover Image</label>
<input
type="file"
accept="image/*"
onChange={handleImageChange}
className="w-full border rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 outline-none"
/>
</div>
{formData.coverImage && (
<div className="mt-3">
<p className="text-sm font-medium">Preview:</p>
<div className="relative inline-block mt-2">
<img
src={URL.createObjectURL(formData.coverImage)}
alt="Selected"
className="w-48 h-32 object-cover rounded border"
/>
<button
type="button"
onClick={handleRemoveImage}
className="absolute top-1 right-1 bg-red-600 text-white text-xs px-2 py-1 rounded hover:bg-red-700"
>
<IconTrashLines className="shrink-0" />
</button>
</div>
</div>
)}
<div className="mb-5">
<ReactQuill
value={formData.description}
onChange={handleDescriptionChange}
modules={modules}
formats={formats}
placeholder="Write your description..."
className="bg-white text-black rounded-md"
/>
</div>
<button
type="submit"
className="bg-blue-600 text-white px-6 py-2 mt-5 rounded-md hover:bg-blue-700 transition"
>
Submit
</button>
</form>
);
};
export default PostForm;