Files
clienthub/src/lib/task-status.ts
T
simone 5547e555bd feat(tasks): stato "In revisione", con gli stati finalmente in un posto solo
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>
2026-08-20 16:10:52 +02:00

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";
}