forked from UKSOURCE/hailearning.edu.vn
feat: Refactor blog components and add pagination
This commit is contained in:
123
app/blog/[slug]/components/CommentForm.tsx
Normal file
123
app/blog/[slug]/components/CommentForm.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
interface CommentFormProps {
|
||||
slug: string;
|
||||
parentId?: string | null;
|
||||
replyToName?: string | null;
|
||||
initialContent?: string;
|
||||
onSubmitted?: () => void;
|
||||
}
|
||||
|
||||
export default function CommentForm({
|
||||
slug,
|
||||
parentId = null,
|
||||
replyToName = null,
|
||||
initialContent = "",
|
||||
onSubmitted,
|
||||
}: CommentFormProps) {
|
||||
const router = useRouter();
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
const [authorName, setAuthorName] = useState("");
|
||||
const [content, setContent] = useState(initialContent);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";
|
||||
|
||||
const onSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setSuccess(null);
|
||||
|
||||
if (!authorName.trim() || !content.trim()) {
|
||||
setError("Please enter your name and comment.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsPending(true);
|
||||
const res = await fetch(`${apiUrl}/api/blog/${slug}/comments`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
authorName: authorName.trim(),
|
||||
content: content.trim(),
|
||||
...(parentId ? { parentId } : {}),
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data?.success) {
|
||||
throw new Error(data?.message || "Failed to submit comment");
|
||||
}
|
||||
|
||||
setAuthorName("");
|
||||
setContent("");
|
||||
setSuccess("Comment submitted.");
|
||||
|
||||
// Re-fetch server data (blog detail is no-store) to show new comment immediately
|
||||
router.refresh();
|
||||
|
||||
onSubmitted?.();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Failed to submit comment");
|
||||
} finally {
|
||||
setIsPending(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mb-3">
|
||||
{parentId ? (replyToName ? `Reply to @${replyToName}` : "Reply") : "Leave A Comment"}
|
||||
</h3>
|
||||
|
||||
{error && <p className="text-danger mb-3">{error}</p>}
|
||||
{success && <p className="text-success mb-3">{success}</p>}
|
||||
|
||||
<form onSubmit={onSubmit} className="contact-form-items">
|
||||
<div className="row g-4">
|
||||
<div className="col-lg-6">
|
||||
<div className="form-clt">
|
||||
<span>Your Name</span>
|
||||
<input
|
||||
type="text"
|
||||
name="authorName"
|
||||
placeholder="Your name"
|
||||
value={authorName}
|
||||
onChange={(e) => setAuthorName(e.target.value)}
|
||||
disabled={isPending}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-lg-12">
|
||||
<div className="form-clt">
|
||||
<textarea
|
||||
name="content"
|
||||
placeholder="Type your comment"
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
disabled={isPending}
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-lg-12 wow fadeInUp" data-wow-delay=".3s">
|
||||
<button type="submit" className="theme-btn" disabled={isPending}>
|
||||
{isPending ? "Sending..." : "Send Comment"}
|
||||
<i className="fa-solid fa-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user