forked from UKSOURCE/hailearning.edu.vn
200 lines
6.0 KiB
TypeScript
200 lines
6.0 KiB
TypeScript
"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 [authorEmail, setAuthorEmail] = useState("");
|
|
const [authorPhone, setAuthorPhone] = useState("");
|
|
const [authorAddress, setAuthorAddress] = useState("");
|
|
const [authorDate, setAuthorDate] = 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;
|
|
}
|
|
|
|
// Basic email validation if provided
|
|
if (authorEmail.trim() && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(authorEmail.trim())) {
|
|
setError("Please enter a valid email address.");
|
|
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(),
|
|
...(authorEmail.trim() ? { authorEmail: authorEmail.trim() } : {}),
|
|
...(authorPhone.trim() ? { authorPhone: authorPhone.trim() } : {}),
|
|
...(authorAddress.trim() ? { authorAddress: authorAddress.trim() } : {}),
|
|
...(authorDate.trim() ? { authorDate: authorDate.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("");
|
|
setAuthorEmail("");
|
|
setAuthorPhone("");
|
|
setAuthorAddress("");
|
|
setAuthorDate("");
|
|
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-4">
|
|
<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}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="col-lg-4">
|
|
<div className="form-clt">
|
|
<span>Your Email</span>
|
|
<input
|
|
type="email"
|
|
name="authorEmail"
|
|
placeholder="Your email"
|
|
value={authorEmail}
|
|
onChange={(e) => setAuthorEmail(e.target.value)}
|
|
disabled={isPending}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="col-lg-4">
|
|
<div className="form-clt">
|
|
<span>Your Phone</span>
|
|
<input
|
|
type="text"
|
|
name="authorPhone"
|
|
placeholder="Phone Number"
|
|
value={authorPhone}
|
|
onChange={(e) => setAuthorPhone(e.target.value)}
|
|
disabled={isPending}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="col-lg-6">
|
|
<div className="form-clt">
|
|
<span>Your Address</span>
|
|
<input
|
|
type="text"
|
|
name="authorAddress"
|
|
placeholder="Address Now"
|
|
value={authorAddress}
|
|
onChange={(e) => setAuthorAddress(e.target.value)}
|
|
disabled={isPending}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="col-lg-6">
|
|
<div className="form-clt">
|
|
<span>Your Date</span>
|
|
<input
|
|
type="text"
|
|
name="authorDate"
|
|
placeholder="Date"
|
|
value={authorDate}
|
|
onChange={(e) => setAuthorDate(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}
|
|
required
|
|
></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>
|
|
);
|
|
}
|
|
|