You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
// frontend/components/tasks/Comments.tsx
|
|
"use client";
|
|
import { useState } from "react";
|
|
import { Icon } from "@/components/Icon";
|
|
import type { Person, TaskComment } from "@/lib/types";
|
|
|
|
function relTime(iso: string): string {
|
|
if (!iso) return "방금";
|
|
const then = new Date(iso).getTime();
|
|
if (Number.isNaN(then)) return "방금";
|
|
const diff = Date.now() - then;
|
|
const m = Math.floor(diff / 60000);
|
|
if (m < 1) return "방금";
|
|
if (m < 60) return `${m}분 전`;
|
|
const h = Math.floor(m / 60);
|
|
if (h < 24) return `${h}시간 전`;
|
|
const d = Math.floor(h / 24);
|
|
if (d === 1) return "어제";
|
|
if (d < 7) return `${d}일 전`;
|
|
const dt = new Date(iso);
|
|
return `${dt.getMonth() + 1}/${dt.getDate()}`;
|
|
}
|
|
|
|
export function Comments({
|
|
list,
|
|
people,
|
|
me,
|
|
onAdd,
|
|
}: {
|
|
list: TaskComment[];
|
|
people: Record<string, Person>;
|
|
me: Person;
|
|
onAdd: (text: string) => void;
|
|
}) {
|
|
const [txt, setTxt] = useState("");
|
|
const submit = () => {
|
|
if (txt.trim()) {
|
|
onAdd(txt.trim());
|
|
setTxt("");
|
|
}
|
|
};
|
|
return (
|
|
<div className="cmt">
|
|
<div className="cmt-list">
|
|
{list.length === 0 && (
|
|
<div className="cmt-empty">아직 코멘트가 없어요. 첫 의견을 남겨보세요.</div>
|
|
)}
|
|
{list.map((c) => {
|
|
const p = people[c.person_id] || me;
|
|
return (
|
|
<div className="cmt-row" key={c.id}>
|
|
<div className="av" style={{ background: p.color }}>
|
|
{p.initial}
|
|
</div>
|
|
<div className="cmt-bub">
|
|
<div className="cmt-top">
|
|
<b>
|
|
{p.name}
|
|
{p.is_me ? " (나)" : ""}
|
|
</b>
|
|
<span className="t">{relTime(c.created_at)}</span>
|
|
</div>
|
|
<div className="cmt-text">{c.text}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
<div className="cmt-compose">
|
|
<div className="av" style={{ background: me.color }}>
|
|
{me.initial}
|
|
</div>
|
|
<input
|
|
value={txt}
|
|
placeholder="코멘트 추가…"
|
|
onChange={(e) => setTxt(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") submit();
|
|
}}
|
|
/>
|
|
<button className="cmt-send" onClick={submit} disabled={!txt.trim()} aria-label="코멘트 등록">
|
|
<Icon name="send" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|