9adbde3f5e
- 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
81 lines
2.6 KiB
TypeScript
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";
|
|
}
|