Files
jobtrackingapp/job-tracker-ui/src/jobWorkflowSignals.ts
T
cesnimda 9adbde3f5e feat(S05/T01): Unified workflow trust signals across the API, table, da…
- JobTrackerApi/Controllers/JobApplicationsController.cs
- JobTrackerApi.Tests/JobApplicationsWorkflowSignalsTests.cs
- job-tracker-ui/src/jobWorkflowSignals.ts
- job-tracker-ui/src/components/JobTable.tsx
- job-tracker-ui/src/components/DashboardView.tsx
- job-tracker-ui/src/components/RemindersView.tsx
- job-tracker-ui/src/workflow-trust-signals.test.tsx
2026-03-24 14:28:01 +01:00

81 lines
2.6 KiB
TypeScript

import { JobApplication, WorkflowSignal } from "./types";
import { buildJobWorkspacePath, JOB_DETAILS_TABS } from "./jobWorkspaceRoute";
const TAB_BY_WORKSPACE: Record<string, number> = {
"tailored-cv": JOB_DETAILS_TABS.tailoredCv,
"follow-up": JOB_DETAILS_TABS.followUp,
"interview-prep": JOB_DETAILS_TABS.interviewPrep,
readiness: JOB_DETAILS_TABS.readiness,
};
export type WorkflowActionPresentation = {
key: WorkflowSignal["actionKey"];
label: string;
detail: string;
path: string;
tab: number;
followMode?: string;
};
export type ReminderGroupKey = "missingCv" | "missingInterviewNotes" | "overdueFollowUp" | "other";
export function getWorkflowSignal(job: Pick<JobApplication, "workflowSignal">): WorkflowSignal | null {
return job.workflowSignal ?? null;
}
export function buildWorkflowPath(job: Pick<JobApplication, "id" | "workflowSignal">): string {
const signal = getWorkflowSignal(job);
const tab = signal ? TAB_BY_WORKSPACE[signal.workspaceTab] ?? JOB_DETAILS_TABS.overview : JOB_DETAILS_TABS.overview;
return buildJobWorkspacePath(job.id, {
tab,
followMode: signal?.followMode ?? undefined,
});
}
export function getWorkflowAction(
job: Pick<JobApplication, "id" | "workflowSignal">,
labels: {
packageWork: string;
followUp: string;
interviewPrep: string;
readiness: string;
},
): WorkflowActionPresentation | null {
const signal = getWorkflowSignal(job);
if (!signal || !signal.needsAttention) return null;
const label = signal.actionKey === "package-work"
? labels.packageWork
: signal.actionKey === "interview-prep"
? labels.interviewPrep
: signal.actionKey === "follow-up"
? labels.followUp
: labels.readiness;
return {
key: signal.actionKey,
label,
detail: signal.reason,
path: buildWorkflowPath(job),
tab: TAB_BY_WORKSPACE[signal.workspaceTab] ?? JOB_DETAILS_TABS.overview,
followMode: signal.followMode ?? undefined,
};
}
export function needsWorkflowWork(job: Pick<JobApplication, "workflowSignal">): boolean {
const signal = getWorkflowSignal(job);
return Boolean(signal?.hasPackageGap || signal?.needsInterviewPrep);
}
export function needsInterviewPrep(job: Pick<JobApplication, "workflowSignal">): boolean {
return Boolean(getWorkflowSignal(job)?.needsInterviewPrep);
}
export function getReminderGroup(job: Pick<JobApplication, "workflowSignal">): ReminderGroupKey {
const actionKey = getWorkflowSignal(job)?.actionKey;
if (actionKey === "package-work") return "missingCv";
if (actionKey === "interview-prep") return "missingInterviewNotes";
if (actionKey === "follow-up") return "overdueFollowUp";
return "other";
}