Files
PacketStation/frontend/src/packet/ChecklistTab.tsx
T

93 lines
3.2 KiB
TypeScript
Raw Normal View History

import { useState } from "react";
import { ApiError } from "../api/client";
import { packetsApi } from "../api/packets";
import type { PacketRead } from "../api/types";
import { errorText, fmtTime } from "../lib/format";
import type { Write } from "../warehouse/PullTable";
import { WeldRecord } from "./WeldRecord";
import type { Mutate } from "./tabs";
/**
* The stage checklist, grouped as the department's template, with the weld record beside it. Each tick is one PUT
* for its key; ticks of different keys never clash.
*/
export function ChecklistTab({
p,
write,
mutate,
reload,
onError,
}: {
p: PacketRead;
write: Write;
mutate: Mutate;
reload: () => Promise<void>;
onError: (text: string) => void;
}) {
const editable = !!p.stage && p.can.includes("edit_checklist");
const [pending, setPending] = useState<Record<string, boolean>>({});
const state = p.stage?.checklist ?? {};
const groups: Array<{ group: string; items: PacketRead["checklist_items"] }> = [];
for (const it of p.checklist_items) {
const g = groups.find((x) => x.group === it.group);
if (g) g.items.push(it);
else groups.push({ group: it.group, items: [it] });
}
const tick = async (key: string, done: boolean) => {
setPending((o) => ({ ...o, [key]: done }));
const r = await write((g) => packetsApi.setChecklist(p.id, key, done, g));
setPending((o) => {
const n = { ...o };
delete n[key];
return n;
});
if (!r.ok && r.err !== undefined) {
if (r.err instanceof ApiError && (r.err.status === 409 || r.err.status === 412)) await reload();
onError(errorText(r.err));
}
};
const weld = p.dept.weld !== false;
return (
<div className="cklay">
<div className="ckmain">
{groups.length ? null : <div className="empty">No checklist for this department.</div>}
{groups.map((g) => (
<section className="sec" key={g.group} aria-label={g.group}>
<div className="sec-h">
<h2>{g.group}</h2>
</div>
<div className="ck">
{g.items.map((i) => {
const c = state[i.key];
const done = pending[i.key] ?? !!c?.done;
const by = c?.done ? [c.stamp || c.by_name, fmtTime(c.at)].filter(Boolean).join(" · ") : "";
return (
<label key={i.key} className={`ck-item${done ? " done" : ""}`} aria-disabled={!editable}>
<input
type="checkbox"
checked={done}
disabled={!editable || pending[i.key] !== undefined}
onChange={(e) => void tick(i.key, e.target.checked)}
/>
<span className="t">{i.text}</span>
<span className="by">{by}</span>
</label>
);
})}
</div>
</section>
))}
{!editable && groups.length ? <div className="small muted">Checklist is completed at the stall.</div> : null}
</div>
{weld ? (
<div className="ckside">
<WeldRecord p={p} mutate={mutate} />
</div>
) : null}
</div>
);
}