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.

110 lines
3.2 KiB
TypeScript

// frontend/components/tasks/ListView.tsx
"use client";
import { Icon } from "@/components/Icon";
import { cx } from "@/lib/cx";
import { COLUMNS, STATUS_META, dueDay, dueLabel, isSoon, pct, stat } from "@/lib/tasks/tree";
import type { Person, Project, Task } from "@/lib/types";
import { Av, Tag } from "./bits";
function LRow({
t,
people,
projects,
onOpen,
}: {
t: Task;
people: Record<string, Person>;
projects: Record<string, Project>;
onOpen: (id: string) => void;
}) {
const s = stat(t);
const sm = STATUS_META[t.status];
const soon = isSoon(t);
return (
<div className={cx("lrow", t.status === "done" && "done-row")} onClick={() => onOpen(t.id)}>
<div className="lt">
<span className="lt-status" style={{ background: sm.c }} />
<span className="lt-title">{t.title}</span>
</div>
<div className="lcell hide-sm">
<Tag project={projects[t.project_id]} />
</div>
<div className="lcell center hide-sm">
<Av person={t.assignee_id ? people[t.assignee_id] : null} />
</div>
<div className="lcell">
<span className={cx("due", soon && "soon")}>
<Icon name="cal" /> {dueLabel(t.due)}
</span>
</div>
<div className="lcell hide-sm">
<span className={cx("prio-flag", `prio-${t.prio}`)}>
<Icon name="flag" /> {t.prio}
</span>
</div>
<div className="lcell">
{s.total > 0 ? (
<span className="lprog">
<span className="bar">
<span style={{ width: pct(t) + "%" }} />
</span>
<span className="n">
{s.done}/{s.total}
</span>
</span>
) : (
<span className="n" style={{ color: "var(--faint)" }}>
</span>
)}
</div>
</div>
);
}
export function ListView({
tasks,
people,
projects,
onOpen,
}: {
tasks: Task[];
people: Record<string, Person>;
projects: Record<string, Project>;
onOpen: (id: string) => void;
}) {
const groups = COLUMNS.map((col) => ({
...col,
items: tasks
.filter((t) => t.status === col.id)
.sort((a, b) => dueDay(a.due) - dueDay(b.due)),
})).filter((g) => g.items.length > 0);
return (
<div className="lgroups">
{groups.map((g) => (
<div className="lgroup" key={g.id}>
<div className="lgroup-head">
<span className="lgroup-dot" style={{ background: g.accent }} />
<span className="lgroup-label">{g.label}</span>
<span className="lgroup-count">{g.items.length}</span>
</div>
<div className="ltable">
<div className="lhead">
<div></div>
<div className="hide-sm"></div>
<div className="center hide-sm"></div>
<div></div>
<div className="hide-sm"></div>
<div></div>
</div>
{g.items.map((t) => (
<LRow key={t.id} t={t} people={people} projects={projects} onOpen={onOpen} />
))}
</div>
</div>
))}
{groups.length === 0 && <div className="lempty"> </div>}
</div>
);
}