refactor(ui): drive status from a single shared pipeline module
Introduces pipeline.ts (mirrors backend JobPipeline) as the one frontend source of truth for canonical stages, synonym normalization, tone, and localized labels. Replaces the status list/logic previously duplicated across KanbanBoard, JobTable, AddJobModal and EditJobDialog. - KanbanBoard/AddJobModal/EditJobDialog render from PIPELINE_STATUSES - JobTable uses shared statusTone + statusLabel (status chips now localized; NB gets proper labels, English unchanged) - Edit dialog status dropdown is now localized too - 5 unit tests; full frontend suite green (19 suites / 41 tests) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,41 +19,22 @@ import ViewStateNotice from "./ViewStateNotice";
|
||||
import { JobApplication } from "../types";
|
||||
import { useI18n } from "../i18n/I18nProvider";
|
||||
import { useViewResource } from "../hooks/useViewResource";
|
||||
import { PIPELINE_STATUSES, PipelineStatus, normalizeStatus, statusLabel, statusTone } from "../pipeline";
|
||||
|
||||
const STATUSES = ["Applied", "Waiting", "Interview", "Offer", "Rejected", "Ghosted"] as const;
|
||||
type Status = (typeof STATUSES)[number];
|
||||
const STATUSES = PIPELINE_STATUSES;
|
||||
type Status = PipelineStatus;
|
||||
|
||||
function normalizeStatus(status: string): Status | "Other" {
|
||||
if (status === "Interviewing") return "Interview";
|
||||
if ((STATUSES as readonly string[]).includes(status)) return status as Status;
|
||||
return "Other";
|
||||
}
|
||||
const TONE_PALETTE: Record<string, (theme: any) => string> = {
|
||||
error: (theme) => theme.palette.error.main,
|
||||
warning: (theme) => theme.palette.warning.main,
|
||||
success: (theme) => theme.palette.success.main,
|
||||
info: (theme) => alpha(theme.palette.primary.main, 0.95),
|
||||
primary: (theme) => theme.palette.primary.main,
|
||||
default: (theme) => theme.palette.primary.main,
|
||||
};
|
||||
|
||||
function toneColor(theme: any, status: Status | "Other"): string {
|
||||
if (status === "Rejected") return theme.palette.error.main;
|
||||
if (status === "Waiting" || status === "Ghosted") return theme.palette.warning.main;
|
||||
if (status === "Offer") return theme.palette.success.main;
|
||||
if (status === "Interview") return alpha(theme.palette.primary.main, 0.95);
|
||||
return theme.palette.primary.main;
|
||||
}
|
||||
|
||||
function statusLabel(t: (key: any, params?: any) => string, status: Status): string {
|
||||
switch (status) {
|
||||
case "Applied":
|
||||
return t("statusApplied");
|
||||
case "Waiting":
|
||||
return t("statusWaiting");
|
||||
case "Interview":
|
||||
return t("statusInterview");
|
||||
case "Offer":
|
||||
return t("statusOffer");
|
||||
case "Rejected":
|
||||
return t("statusRejected");
|
||||
case "Ghosted":
|
||||
return t("statusGhosted");
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
return TONE_PALETTE[statusTone(status)](theme);
|
||||
}
|
||||
|
||||
export default function KanbanBoard() {
|
||||
|
||||
Reference in New Issue
Block a user