feat(workspace): Application Workspace foundation (Phase 5 milestone 1)
Every JobApplication gets a dedicated workspace at /applications/{id} — a
surface, not a new data store. It owns no data and duplicates none: CV comes
from the Phase 4 CvVariant lens, analysis/match/interview from the existing
AiWorkspacePanel, documents from Attachments, communication from
Correspondence, activity from JobEvent, stage semantics from JobPipeline. No
career data is copied and nothing here writes.
- GET /api/jobapplications/{id}/workspace: one aggregate read (role, company,
stage, dates, attached CV variant, cover letter, documents, AI history,
recent activity) replacing the page fanning out across endpoints
- Next recommended action: ordered rules answering "what do I do next?", the
core product principle for this phase
- ApplicationWorkspacePage: left nav + linkable ?section=, reusing the existing
component for each domain; later-milestone sections say so rather than faking
- Entry point from the job dialog via an optional onOpenWorkspace callback —
the dialog must not depend on router context (it is mounted without a
<Router> in several suites), so the caller owns navigation
- 8 backend tests (aggregate, CV variant surfacing, counts, activity ordering,
next-step rules, tenant scoping)
Local: 314 backend, 88/88 frontend (31 suites), tsc clean, production build ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ const QuickCommandDialog = lazy(() => import("./components/QuickCommandDialog"))
|
||||
const ProfilePage = lazy(() => import("./views/ProfilePage"));
|
||||
const CareerWorkspacePage = lazy(() => import("./views/CareerWorkspacePage"));
|
||||
const CvBuilderPage = lazy(() => import("./views/CvBuilderPage"));
|
||||
const ApplicationWorkspacePage = lazy(() => import("./views/ApplicationWorkspacePage"));
|
||||
const CvBuilderEditor = lazy(() => import("./views/CvBuilderEditor"));
|
||||
const PublicCvPage = lazy(() => import("./views/PublicCvPage"));
|
||||
const ConnectedAccountsPage = lazy(() => import("./views/ConnectedAccountsPage"));
|
||||
@@ -326,6 +327,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
|
||||
<Route path="/correspondence" element={<CorrespondenceInboxPage />} />
|
||||
<Route path="/correspondence/review" element={<GmailReviewPage />} />
|
||||
<Route path="/profile" element={<ProfilePage />} />
|
||||
<Route path="/applications/:id" element={<ApplicationWorkspacePage />} />
|
||||
<Route path="/career" element={<CareerWorkspacePage />} />
|
||||
<Route path="/career/builder" element={<CvBuilderPage />} />
|
||||
<Route path="/career/builder/:id" element={<CvBuilderEditor />} />
|
||||
|
||||
Reference in New Issue
Block a user