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.

80 lines
2.1 KiB
TypeScript

// frontend/components/tasks/KanbanCard.tsx
"use client";
import { Icon } from "@/components/Icon";
import { cx } from "@/lib/cx";
import { dueLabel, isSoon, pct, stat } from "@/lib/tasks/tree";
import type { Person, Project, Task } from "@/lib/types";
import { Av, Tag } from "./bits";
export function KanbanCard({
task,
people,
projects,
onOpen,
onDragStart,
onDragEnd,
dragging,
}: {
task: Task;
people: Record<string, Person>;
projects: Record<string, Project>;
onOpen: (id: string) => void;
onDragStart?: (e: React.DragEvent) => void;
onDragEnd?: () => void;
dragging?: boolean;
}) {
const s = stat(task);
const soon = isSoon(task);
const proj = projects[task.project_id];
const person = task.assignee_id ? people[task.assignee_id] : null;
return (
<div
className={cx("kcard", dragging && "dragging", task.status === "done" && "done-card")}
draggable
onDragStart={onDragStart}
onDragEnd={onDragEnd}
onClick={() => onOpen(task.id)}
>
<div className="kcard-top">
<Tag project={proj} />
<button
className="kmore"
onClick={(e) => {
e.stopPropagation();
onOpen(task.id);
}}
aria-label="더보기"
>
<Icon name="more" />
</button>
</div>
<div className="kcard-title">{task.title}</div>
{s.total > 0 && (
<div className="ksub">
<div className="bar">
<span style={{ width: pct(task) + "%" }} />
</div>
<span className="ksub-num">
<Icon name="branch" /> {s.done}/{s.total}
</span>
</div>
)}
<div className="kfoot">
<span className={cx("due", soon && "soon")}>
<Icon name="cal" /> {dueLabel(task.due)}
</span>
<span className={cx("prio-flag", `prio-${task.prio}`)}>
<Icon name="flag" /> {task.prio}
</span>
{task.delegated && (
<span className="deleg-chip">
<Icon name="users" />
</span>
)}
<Av person={person} />
</div>
</div>
);
}