bo5tym52p: List and create comments on post cards

This commit is contained in:
2026-10-19 14:29:07 -06:00
parent de535a500d
commit 5a33546af2
11 changed files with 596 additions and 2 deletions
+61
View File
@@ -0,0 +1,61 @@
import type { CommentItem } from "@/lib/comment-model";
import { relativeTime } from "@/lib/feed-model";
export interface CommentsPanelProps {
comments: CommentItem[];
loading?: boolean;
/** Error text naming the action that failed. The list stays shown. */
error?: string | null;
value: string;
busy?: boolean;
onChange?: (v: string) => void;
onSubmit?: () => void;
onRetry?: () => void;
now?: number;
}
export function CommentsPanel(p: CommentsPanelProps) {
const empty = !p.value.trim();
return (
<div className="comments" data-testid="comments-panel">
{p.loading ? <span className="spinner" role="status" aria-label="Loading comments" /> : null}
{!p.loading && p.comments.length === 0 && !p.error ? (
<p className="comments-empty">No comments yet.</p>
) : null}
<ul className="comment-list">
{p.comments.map((c) => (
<li key={c.key} className={c.removed ? "comment removed" : "comment"} data-testid="comment">
<div className="comment-head">
<strong>{c.authorName}</strong>
<time dateTime={c.createdAt ?? undefined}>{relativeTime(c.createdAt, p.now)}</time>
</div>
{c.removed ? <p className="comment-removed">This comment was removed.</p> : <p>{c.content}</p>}
</li>
))}
</ul>
{p.error ? (
<p className="comment-error" role="alert">
{p.error}{" "}
{p.onRetry ? <button type="button" className="link-btn" onClick={p.onRetry}>Try again</button> : null}
</p>
) : null}
<form
className="comment-form"
onSubmit={(e) => {
e.preventDefault();
if (!empty && !p.busy) p.onSubmit?.();
}}
>
<input
type="text"
aria-label="Add a comment"
placeholder="Add a comment"
value={p.value}
maxLength={5000}
onChange={(e) => p.onChange?.(e.target.value)}
/>
<button type="submit" disabled={empty || p.busy}>{p.busy ? "Posting" : "Post"}</button>
</form>
</div>
);
}