import { api } from "./api"; // Mirrors WorkspaceOverviewDto. Read-only aggregate — the workspace owns no data. export type WorkspaceCv = { variantId: number | null; variantName: string | null; themeId: string | null; hasTailoredCvText: boolean; updatedAtUtc: string | null; }; export type WorkspaceActivity = { type: string; detail: string | null; at: string }; export type WorkspaceNextStep = { key: string; label: string; reason: string; section: string | null }; export type WorkspaceOverview = { id: number; jobTitle: string; company: string | null; location: string | null; salary: string | null; status: string; stageGroup: string; stageOrder: number; dateApplied: string | null; deadline: string | null; followUpAt: string | null; nextAction: string | null; jobUrl: string | null; savedAt: string; description: string | null; translatedDescription: string | null; descriptionLanguage: string | null; tags: string[]; notes: string | null; source: string | null; countryCode: string | null; hasJobDescription: boolean; cv: WorkspaceCv; hasCoverLetter: boolean; documentCount: number; hasPortfolio: boolean; aiInteractionCount: number; lastAiAtUtc: string | null; recentActivity: WorkspaceActivity[]; nextStep: WorkspaceNextStep | null; checklistProgress: ChecklistProgress | null; }; // Milestone 2 — the application checklist. One workflow surface: system items seed from the same // readiness signals the backend already computed, and the user owns everything after that. export type ChecklistStatus = "pending" | "done" | "dismissed"; export type ChecklistItem = { id: number; systemKey: string | null; title: string; description: string | null; category: string; status: ChecklistStatus; section: string | null; sortOrder: number; isSystemGenerated: boolean; isAutoCompleted: boolean; completedAt: string | null; }; export type ChecklistProgress = { total: number; completed: number; dismissed: number; percent: number }; export type Checklist = { items: ChecklistItem[]; progress: ChecklistProgress }; export const CHECKLIST_CATEGORIES: { key: string; label: string }[] = [ { key: "preparation", label: "Before applying" }, { key: "submission", label: "Submitting" }, { key: "follow-up", label: "Follow-up" }, { key: "interview", label: "Interview" }, { key: "custom", label: "Your own tasks" }, ]; // Workspace navigation. Sections map to the Phase 5 milestones; each is added as its milestone lands // so the workspace is always usable rather than a shell of placeholders. export type WorkspaceSectionKey = | "overview" | "job-details" | "analysis" | "match" | "checklist" | "cv" | "cover-letter" | "documents" | "interview" | "timeline" | "communication"; export const WORKSPACE_SECTIONS: { key: WorkspaceSectionKey; label: string }[] = [ { key: "overview", label: "Overview" }, { key: "job-details", label: "Job Details" }, { key: "analysis", label: "Analysis" }, { key: "match", label: "Match" }, { key: "checklist", label: "Checklist" }, { key: "cv", label: "CV" }, { key: "cover-letter", label: "Cover Letter" }, { key: "documents", label: "Documents" }, { key: "interview", label: "Interview Prep" }, { key: "timeline", label: "Timeline" }, { key: "communication", label: "Communication" }, ]; export const workspaceSection = (value: string | null): WorkspaceSectionKey => WORKSPACE_SECTIONS.some((section) => section.key === value) ? value as WorkspaceSectionKey : "overview"; export const applicationWorkspaceApi = { overview: (jobId: number) => api.get(`/jobapplications/${jobId}/workspace`).then((r) => r.data), }; // Phase 5.3 — Application Intelligence. All three reads are deterministic and read-only; the AI // narrative stays on the existing /ai routes, which require the user to ask for it. export type TimelineEvent = { id: number; type: string; category: string; summary: string; detail: string | null; isMilestone: boolean; at: string; }; export type TimelineDay = { date: string; label: string; events: TimelineEvent[] }; export type Timeline = { days: TimelineDay[]; milestones: TimelineEvent[]; categories: string[]; totalEvents: number; }; export type JobAnalysis = { role: string | null; company: string | null; location: string | null; employmentType: string | null; seniority: string | null; salary: string | null; technologies: string[]; skills: string[]; responsibilities: string[]; keywords: string[]; summary: string; importantRequirements: string[]; interviewTopics: string[]; missingInformation: string[]; hasJobDescription: boolean; aiSuggestionCount: number; }; export type MatchEvidence = { title: string; subtitle: string | null; matched: string[] }; export type CareerMatch = { score: number; band: string; hasEnoughSignal: boolean; hasCareerProfile: boolean; matchedSkills: string[]; missingSkills: string[]; relevantExperience: MatchEvidence[]; relevantProjects: MatchEvidence[]; suggestions: string[]; aiSuggestionCount: number; }; export const TIMELINE_CATEGORY_LABELS: Record = { lifecycle: "Lifecycle", stage: "Stage", "follow-up": "Follow-up", communication: "Communication", ai: "AI", }; export const applicationIntelligenceApi = { timeline: (jobId: number, category?: string, milestonesOnly?: boolean) => api .get(`/jobapplications/${jobId}/timeline`, { params: { category: category || undefined, milestonesOnly: milestonesOnly || undefined }, }) .then((r) => r.data), analysis: (jobId: number) => api.get(`/jobapplications/${jobId}/analysis`).then((r) => r.data), match: (jobId: number) => api.get(`/jobapplications/${jobId}/match`).then((r) => r.data), }; // Phase 5.4 — Application Assets. CV variant CRUD, preview, PDF export and version history stay on // /api/cv (the existing CV builder). These types cover only what is application-scoped. export type CvVariantSummary = { id: number; name: string; themeId: string; publicSlug: string; isPublic: boolean; version: number; jobApplicationId: number | null; updatedAtUtc: string; }; export type ApplicationCv = { attachedVariantId: number | null; attachedVariantName: string | null; attachedThemeId: string | null; attachedVersion: number | null; attachedUpdatedAtUtc: string | null; attachedIsPublic: boolean; hasTailoredCvText: boolean; availableVariants: CvVariantSummary[]; }; export type TailoringSuggestion = { kind: string; title: string; detail: string | null; items: string[] }; export type TailoringPlan = { hasJobDescription: boolean; hasCareerProfile: boolean; hasAttachedVariant: boolean; matchScore: number; suggestions: TailoringSuggestion[]; aiSuggestionCount: number; }; export type CoverLetterVersion = { version: number; source: string; aiAction: string | null; length: number; createdAtUtc: string; isCurrent: boolean; }; export type CoverLetter = { text: string | null; currentVersion: number; versions: CoverLetterVersion[]; aiSuggestionCount: number; }; export const applicationAssetsApi = { cv: (jobId: number) => api.get(`/jobapplications/${jobId}/cv`).then((r) => r.data), attachVariant: (jobId: number, variantId: number | null) => api.put(`/jobapplications/${jobId}/cv`, { variantId }).then((r) => r.data), tailoring: (jobId: number) => api.get(`/jobapplications/${jobId}/tailoring`).then((r) => r.data), coverLetter: (jobId: number) => api.get(`/jobapplications/${jobId}/cover-letter`).then((r) => r.data), saveCoverLetter: (jobId: number, text: string, source = "manual", aiAction?: string) => api.put(`/jobapplications/${jobId}/cover-letter`, { text, source, aiAction }).then((r) => r.data), restoreCoverLetter: (jobId: number, version: number) => api.post(`/jobapplications/${jobId}/cover-letter/versions/${version}/restore`).then((r) => r.data), }; // Phase 5.5 — Interview preparation and follow-up. Prep content is the user's; AI suggestions come // from the existing /ai routes and only land here once accepted. export type InterviewPrepItem = { id: number; category: string; title: string; content: string | null; source: string; isPrepared: boolean; sortOrder: number; updatedAtUtc: string; }; export type InterviewPrepGroup = { category: string; label: string; items: InterviewPrepItem[] }; export type InterviewPrepBoard = { groups: InterviewPrepGroup[]; total: number; prepared: number; percent: number; isInterviewStage: boolean; aiSuggestionCount: number; }; export type FollowUp = { followUpAt: string | null; nextAction: string | null; responseReceived: boolean; openFollowUpTasks: number; }; export const INTERVIEW_PREP_CATEGORIES: { key: string; label: string }[] = [ { key: "company-research", label: "Company research" }, { key: "technical", label: "Technical preparation" }, { key: "behavioural", label: "Behavioural questions" }, { key: "star", label: "STAR examples" }, { key: "question", label: "Questions to ask them" }, { key: "note", label: "Notes" }, ]; export const interviewPrepApi = { get: (jobId: number) => api.get(`/jobapplications/${jobId}/interview-prep`).then((r) => r.data), add: (jobId: number, body: { category?: string; title: string; content?: string; source?: string }) => api.post(`/jobapplications/${jobId}/interview-prep`, body).then((r) => r.data), update: (jobId: number, itemId: number, body: Partial>) => api.patch(`/jobapplications/${jobId}/interview-prep/${itemId}`, body).then((r) => r.data), remove: (jobId: number, itemId: number) => api.delete(`/jobapplications/${jobId}/interview-prep/${itemId}`).then(() => undefined), followUp: (jobId: number) => api.get(`/jobapplications/${jobId}/follow-up`).then((r) => r.data), setFollowUp: (jobId: number, followUpAt: string | null, nextAction?: string | null) => api.put(`/jobapplications/${jobId}/follow-up`, { followUpAt, nextAction }).then((r) => r.data), }; export const applicationChecklistApi = { get: (jobId: number) => api.get(`/jobapplications/${jobId}/checklist`).then((r) => r.data), add: (jobId: number, title: string, description?: string, category?: string) => api.post(`/jobapplications/${jobId}/checklist`, { title, description, category }).then((r) => r.data), update: (jobId: number, itemId: number, patch: Partial>) => api.patch(`/jobapplications/${jobId}/checklist/${itemId}`, patch).then((r) => r.data), remove: (jobId: number, itemId: number) => api.delete(`/jobapplications/${jobId}/checklist/${itemId}`).then(() => undefined), reorder: (jobId: number, orderedIds: number[]) => api.put(`/jobapplications/${jobId}/checklist/order`, orderedIds).then((r) => r.data), };