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:
@@ -30,6 +30,7 @@ import { Company, JobImportResult } from "../types";
|
||||
import { invalidateCompaniesCache, useCompanies } from "../hooks/useCompanies";
|
||||
import { useToast } from "../toast";
|
||||
import { useI18n } from "../i18n/I18nProvider";
|
||||
import { PIPELINE_STATUSES, statusLabel as pipelineStatusLabel } from "../pipeline";
|
||||
import TagsInput from "./TagsInput";
|
||||
|
||||
interface Props {
|
||||
@@ -60,7 +61,6 @@ type CreatedJobResponse = {
|
||||
type AttachmentBucketKey = "resume" | "coverLetter" | "portfolio" | "other";
|
||||
type AttachmentBuckets = Record<AttachmentBucketKey, File[]>;
|
||||
|
||||
const STATUS_OPTIONS = ["Applied", "Waiting", "Interview", "Offer", "Rejected", "Ghosted"] as const;
|
||||
const ACCEPTED_DOCUMENT_TYPES = ".pdf,.doc,.docx,.txt,.md,image/*,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/plain,text/markdown";
|
||||
const FIELD_SX = { "& .MuiInputBase-root": { minHeight: 56 } };
|
||||
const PICKER_TEXT_FIELD_PROPS = { fullWidth: true, sx: FIELD_SX };
|
||||
@@ -115,7 +115,7 @@ export default function AddJobModal({ open, onClose, onCreated }: Props) {
|
||||
|
||||
const [dateApplied, setDateApplied] = useState(() => getTodayIso());
|
||||
const [jobTitle, setJobTitle] = useState("");
|
||||
const [status, setStatus] = useState<(typeof STATUS_OPTIONS)[number]>("Applied");
|
||||
const [status, setStatus] = useState<(typeof PIPELINE_STATUSES)[number]>("Applied");
|
||||
const [location, setLocation] = useState("");
|
||||
const [salary, setSalary] = useState("");
|
||||
const [salaryMin, setSalaryMin] = useState("");
|
||||
@@ -350,18 +350,6 @@ export default function AddJobModal({ open, onClose, onCreated }: Props) {
|
||||
}));
|
||||
};
|
||||
|
||||
const statusLabel = (value: typeof STATUS_OPTIONS[number]) => {
|
||||
const map = {
|
||||
Applied: t("statusApplied"),
|
||||
Waiting: t("statusWaiting"),
|
||||
Interview: t("statusInterview"),
|
||||
Offer: t("statusOffer"),
|
||||
Rejected: t("statusRejected"),
|
||||
Ghosted: t("statusGhosted"),
|
||||
} as const;
|
||||
return map[value];
|
||||
};
|
||||
|
||||
const filesLabel = (files: File[]) => {
|
||||
if (files.length === 0) return t("addJobModalNoFilesSelected");
|
||||
if (files.length === 1) return files[0].name;
|
||||
@@ -479,9 +467,9 @@ export default function AddJobModal({ open, onClose, onCreated }: Props) {
|
||||
/>
|
||||
|
||||
<TextField select label={t("addJobModalStatus")} value={status} onChange={(e) => setStatus(e.target.value as any)} sx={FIELD_SX}>
|
||||
{STATUS_OPTIONS.map((s) => (
|
||||
{PIPELINE_STATUSES.map((s) => (
|
||||
<MenuItem key={s} value={s}>
|
||||
{statusLabel(s)}
|
||||
{pipelineStatusLabel(t, s)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
|
||||
Reference in New Issue
Block a user