API additions (dev-api.py split across this and next commit):
- /api/jobs/{job_id}/qa GET/PATCH/suggest — Interview Prep answer storage + LLM suggestions
- /api/settings/ui-preference POST — persist streamlit/vue preference to user.yaml
- cancel_task() added to scripts/db.py (per-task cancel for Danger Zone)
Vue / UI:
- AppNav: "⚡ Classic" button to switch back to Streamlit UI (writes cookie + persists to user.yaml)
- ApplyWorkspace: Resume Highlights panel (collapsible skills/domains/keywords with job-match highlighting)
- SettingsView: hide Data tab in cloud mode (showData guard)
- ResumeProfileView: minor improvements
- useApi.ts: error handling improvements
Infra:
- compose.cloud.yml: add api service (uvicorn dev_api running in cloud container)
- docker/web/nginx.conf: proxy /api/* to api service in cloud mode
- README.md: Vue SPA now listed as Free tier feature
#50 Job Review list view — sort + filter controls:
- Sort by best match / newest first / company A-Z (client-side computed)
- Remote-only checkbox filter
- Job count indicator; filters reset on tab switch
- Remote badge on list items
#61 Cover letter generation from approved tab:
- '✨ Draft' button on each approved-list item → /apply/:id
- No extra API call; ApplyWorkspace handles generation from there
#54 Company research modal (all API endpoints already existed):
- CompanyResearchModal.vue: 3-state machine (empty→generating→ready)
polling /research/task every 3s, displays all 7 research sections
(company, leadership, talking points, tech, funding, red flags,
accessibility), copy-to-clipboard for talking points, ↺ Refresh
- InterviewCard: new 'research' emit + '🔍 Research' button for
phone_screen/interviewing/offer stages
- InterviewsView: wires modal with researchJobId/Title/AutoGen state;
auto-opens modal with autoGenerate=true when a job is moved to
phone_screen (mirrors Streamlit behaviour)
ApplyWorkspace.vue: kept HEAD (vue-spa) version for resume optimizer panel,
cl-error__actions wrapper, and ResumeOptimizerPanel import. main's older
version lacked these additions.
- Fix 1: Add missing `:` binding prefix to aria-label on score badge
(was emitting literal backtick template string to DOM)
- Fix 2: Remove unused `watch` import from InterviewPrepView.vue
- Fix 3: guardAndLoad now checks interviewsStore.error after fetchAll;
shows pageError banner instead of silently redirecting to /interviews
on network failure; job is now a ref set explicitly in the guard
- Fix 4: Remove unconditional research-badge from InterviewCard.vue
(added in this branch; card has no access to prep store so badge
always showed regardless of whether research exists)
When a new job prop arrives after approve/reject, the watch cleared the
exit-transform inline style while the spring transition was still active,
causing the card to animate from offscreen back to center before the new
card rendered. Fix: set transition:none before clearing the style, then
restore it on the next rAF — browser paints the new position first.