feat(ai): AI Workspace panel on every job application
Phase 5 frontend. A new "AI Workspace" tab in the job details dialog hosts the five suggestion modules (Job Analysis, Career Match, Cover Letter with tone, Interview Prep, Application Review) with a generate flow, a dependency-free markdown renderer for results, and a history sidebar (reuse / compare / copy / delete). Everything is suggestion-only — copy to keep; nothing auto-applies. - aiWorkspace.ts (types + API), components/AiWorkspacePanel.tsx, components/Markdown.tsx (no HTML injection surface — renders React nodes) - mounted as the last tab in JobDetailsDialog (index-safe, no reindexing) - 3 tests (generate flow, cover-letter mode, markdown); tsc + build clean Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { api } from "./api";
|
||||
|
||||
export type AiInteraction = {
|
||||
id: number;
|
||||
module: string;
|
||||
mode: string | null;
|
||||
title: string;
|
||||
provider: string;
|
||||
result: { text?: string };
|
||||
createdAtUtc: string;
|
||||
};
|
||||
|
||||
export const AI_MODULES: { key: string; label: string; blurb: string }[] = [
|
||||
{ key: "job-analysis", label: "Job Analysis", blurb: "Break down the advert: skills, requirements, salary, work model, interview topics." },
|
||||
{ key: "career-match", label: "Career Match", blurb: "Your profile vs the advert: strengths, gaps, match %, suggested improvements." },
|
||||
{ key: "cover-letter", label: "Cover Letter", blurb: "A tailored cover letter in the tone you choose." },
|
||||
{ key: "interview", label: "Interview Prep", blurb: "Likely questions, STAR answers, company research, a checklist." },
|
||||
{ key: "application-review", label: "Application Review", blurb: "Grammar, gaps, weak areas, ATS issues, overall strength." },
|
||||
];
|
||||
|
||||
export const COVER_LETTER_MODES = ["professional", "friendly", "short", "detailed", "modern", "traditional"];
|
||||
|
||||
export const aiWorkspaceApi = {
|
||||
modules: (jobId: number) => api.get<{ modules: string[]; provider: string }>(`/jobapplications/${jobId}/ai/modules`).then((r) => r.data),
|
||||
generate: (jobId: number, body: { module: string; mode?: string; extraContext?: string }) =>
|
||||
api.post<AiInteraction>(`/jobapplications/${jobId}/ai/generate`, body).then((r) => r.data),
|
||||
history: (jobId: number, module?: string) =>
|
||||
api.get<AiInteraction[]>(`/jobapplications/${jobId}/ai/history`, { params: { module } }).then((r) => r.data),
|
||||
remove: (jobId: number, id: number) => api.delete(`/jobapplications/${jobId}/ai/history/${id}`),
|
||||
};
|
||||
Reference in New Issue
Block a user