Archived
Restore the app files (prior commit mis-scoped its tar)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { useState } from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import { getComments, postComment } from '../api';
|
||||
import { useAsync } from '../lib/useAsync';
|
||||
|
||||
export default function CommentSection({ postId }: { postId: string }) {
|
||||
const [reloadKey, setReloadKey] = useState(0);
|
||||
const { data: comments, loading } = useAsync(() => getComments(postId), [postId, reloadKey]);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [comment, setComment] = useState('');
|
||||
const [state, setState] = useState<'idle' | 'busy' | 'error'>('idle');
|
||||
|
||||
async function onSubmit(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!name.trim() || !email.trim() || !comment.trim()) return;
|
||||
setState('busy');
|
||||
try {
|
||||
await postComment({ postId, name: name.trim(), email: email.trim(), comment: comment.trim() });
|
||||
setName(''); setEmail(''); setComment('');
|
||||
setState('idle');
|
||||
setReloadKey((k) => k + 1);
|
||||
} catch {
|
||||
setState('error');
|
||||
}
|
||||
}
|
||||
|
||||
const list = comments ?? [];
|
||||
|
||||
return (
|
||||
<section className="mt-12">
|
||||
<h2 className="mb-6 border-b border-primary-200 pb-2 text-2xl font-bold">
|
||||
{list.length === 1 ? '1 Comment' : `${list.length} Comments`}
|
||||
</h2>
|
||||
|
||||
{loading && <p className="text-primary-500">Loading comments…</p>}
|
||||
|
||||
<ul className="mb-10 space-y-4">
|
||||
{list.map((c) => (
|
||||
<li key={c.id} className="card">
|
||||
<div className="mb-1 flex items-center text-sm text-primary-500">
|
||||
<span className="font-semibold text-primary-700">{c.name}</span>
|
||||
{c.createdAt && (
|
||||
<>
|
||||
<span className="mx-2">•</span>
|
||||
<time dateTime={c.createdAt}>{format(new Date(c.createdAt), 'MMMM d, yyyy')}</time>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<p className="whitespace-pre-line text-primary-700">{c.body}</p>
|
||||
</li>
|
||||
))}
|
||||
{!loading && list.length === 0 && (
|
||||
<li className="text-primary-500">No comments yet — be the first to share a thought.</li>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
<div className="card">
|
||||
<h3 className="mb-4 text-xl font-bold">Leave a comment</h3>
|
||||
<form onSubmit={onSubmit} className="space-y-3">
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<input className="field" placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} required />
|
||||
<input className="field" type="email" placeholder="Email (not published)" value={email}
|
||||
onChange={(e) => setEmail(e.target.value)} required />
|
||||
</div>
|
||||
<textarea className="field min-h-32" placeholder="Your comment" value={comment}
|
||||
onChange={(e) => setComment(e.target.value)} required />
|
||||
<button type="submit" className="button" disabled={state === 'busy'}>
|
||||
{state === 'busy' ? 'Posting…' : 'Post Comment'}
|
||||
</button>
|
||||
{state === 'error' && <p className="text-sm text-red-700">Something went wrong. Please try again.</p>}
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user