48 lines
3.3 KiB
TypeScript
48 lines
3.3 KiB
TypeScript
import { useState } from "react";
|
|
|
|
import type { CommentRecord } from "@/features/comments/model/types";
|
|
import { formatSeoulDateTime } from "@/shared/lib/formatters";
|
|
|
|
export function CommentItem({ canDelete = true, canEdit, comment, isSaving, onDelete, onEdit, onShowReplies, replyActionLabel }: { readonly canDelete?: boolean; readonly canEdit: boolean; readonly comment: CommentRecord; readonly isSaving: boolean; readonly onDelete: () => void; readonly onEdit: (comment: string) => void; readonly onShowReplies?: () => void; readonly replyActionLabel?: "답글 보기" | "답글 작성" }) {
|
|
const [draft, setDraft] = useState(comment.comment);
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const label = `${comment.comment}`;
|
|
|
|
function saveEdit() {
|
|
const nextComment = draft.trim();
|
|
if (nextComment.length === 0) {
|
|
return;
|
|
}
|
|
onEdit(nextComment);
|
|
setIsEditing(false);
|
|
}
|
|
|
|
return (
|
|
<article className="rounded-lg border border-border bg-card p-3" aria-label={`${comment.nickname} 댓글`}>
|
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
|
<div className="min-w-0">
|
|
<p className="text-sm font-semibold text-foreground">{comment.nickname}</p>
|
|
<p className="text-xs text-muted-foreground">{formatSeoulDateTime(comment.date)}{comment.isSecret ? " · 비밀" : ""}</p>
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{replyActionLabel !== undefined && onShowReplies !== undefined ? <button className="rounded-md border border-input bg-card px-3 py-2 text-sm font-semibold hover:bg-accent disabled:opacity-60" disabled={isSaving} onClick={onShowReplies} type="button">{label} {replyActionLabel}</button> : null}
|
|
{canEdit ? <button className="rounded-md border border-input bg-card px-3 py-2 text-sm font-semibold hover:bg-accent disabled:opacity-60" disabled={isSaving} onClick={() => setIsEditing(true)} type="button">{label} 수정</button> : null}
|
|
{canDelete ? <button className="rounded-md border border-destructive bg-card px-3 py-2 text-sm font-semibold text-destructive hover:bg-accent disabled:opacity-60" disabled={isSaving} onClick={onDelete} type="button">{label} 삭제</button> : null}
|
|
</div>
|
|
</div>
|
|
{isEditing ? (
|
|
<div className="mt-3 flex flex-col gap-2">
|
|
<label className="flex flex-col gap-2 text-sm font-semibold">
|
|
댓글 수정 내용
|
|
<textarea className="min-h-24 rounded-md border border-input bg-card px-3 py-2 text-base font-normal" onChange={(event) => setDraft(event.currentTarget.value)} value={draft} />
|
|
</label>
|
|
<div className="flex flex-wrap gap-2">
|
|
<button className="rounded-md border border-input bg-primary px-4 py-2 font-semibold text-primary-foreground hover:bg-[var(--button-bg-hover)] active:bg-[var(--button-bg-active)] disabled:opacity-60" disabled={isSaving || draft.trim().length === 0} onClick={saveEdit} type="button">수정 저장</button>
|
|
<button className="rounded-md border border-input bg-card px-4 py-2 font-semibold hover:bg-accent" onClick={() => { setDraft(comment.comment); setIsEditing(false); }} type="button">취소</button>
|
|
</div>
|
|
</div>
|
|
) : <p className="mt-3 break-words text-sm">{comment.comment}</p>}
|
|
</article>
|
|
);
|
|
}
|