5547e555bd
Mancava il modo di dire "finito, ma da controllare prima di consegnarlo". Il nuovo stato sta fra "In corso" e "Fatto" ed e' visibile anche al cliente: il lavoro c'e' ed e' in controllo qualita', non e' fermo. Il costo non era la logica ma la dispersione: tre letterali ricopiati a mano in otto file, in tre forme diverse (allow-list a runtime, union TS, colonne kanban, opzioni della select) e nessun CHECK in DB a tenerli insieme. Invece di modificarne quattordici occorrenze, tutto deriva da TASK_STATUSES in src/lib/task-status.ts: la prossima aggiunta costa una riga. Due punti perdevano dati in silenzio, ed erano il vero motivo per centralizzare: - recomputePhaseStatus considerava "iniziato" solo in_progress|done, come lista. Una fase con tutti i task in revisione non rientrava ne' in allDone ne' in anyActive e retrocedeva a "upcoming": si leggeva "non iniziata" quando era quasi finita. Ora e' la negazione di "todo", e regge anche il prossimo stato. - ClientKanban ripartiva i task con un oggetto a tre chiavi fisse, non derivato dalle colonne: un task fuori da quelle spariva da ogni colonna e da ogni contatore, e il cliente ne vedeva meno di quanti ce n'erano, senza errore. Chiuso anche il cast non verificato al confine del portale (page.tsx), che era la causa a monte di entrambi: ora ci passa normalizeTaskStatus. Nessuna migration: tasks.status e' text senza CHECK, le righe esistenti valgono. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
30 lines
1.3 KiB
TypeScript
30 lines
1.3 KiB
TypeScript
// ── Task statuses, in one place ───────────────────────────────────────────────
|
|
// `tasks.status` is a plain text column with no CHECK constraint, and until now
|
|
// the three values were retyped by hand in eight files — as a runtime allow-list,
|
|
// as a TS union, as kanban columns, as <select> options. Two of those copies
|
|
// dropped unknown values silently. Everything derives from here instead.
|
|
//
|
|
// No db import on purpose: client components import this file.
|
|
|
|
export const TASK_STATUSES = ["todo", "in_progress", "in_review", "done"] as const;
|
|
|
|
export type TaskStatus = (typeof TASK_STATUSES)[number];
|
|
|
|
export const TASK_STATUS_LABELS: Record<TaskStatus, string> = {
|
|
todo: "Da fare",
|
|
in_progress: "In corso",
|
|
in_review: "In revisione",
|
|
done: "Fatto",
|
|
};
|
|
|
|
export function isTaskStatus(value: string): value is TaskStatus {
|
|
return (TASK_STATUSES as readonly string[]).includes(value);
|
|
}
|
|
|
|
// Guards the boundary where a raw DB string enters a typed union. Previously an
|
|
// `as` cast, which TypeScript does not check: a row holding an unknown status
|
|
// slipped through and then vanished from whichever board failed to match it.
|
|
export function normalizeTaskStatus(value: string): TaskStatus {
|
|
return isTaskStatus(value) ? value : "todo";
|
|
}
|