API additions (dev-api.py):
- GET /api/tasks — list active background tasks
- DELETE /api/tasks/{task_id} — per-task cancel
- POST /api/tasks/kill — kill all stuck tasks
- POST /api/tasks/discovery|email-sync|enrich|score|sync — queue/trigger each workflow
- POST /api/jobs/archive — archive by statuses array
- POST /api/jobs/purge — hard delete by statuses or target (email/non_remote/rescrape)
- POST /api/jobs/add — queue URL imports
- POST /api/jobs/upload-csv — upload CSV with URL column
- GET /api/config/setup-banners — list undismissed onboarding hints
- POST /api/config/setup-banners/{key}/dismiss — dismiss a banner
HomeView.vue:
- 4th WorkflowButton: "Fill Missing Descriptions" (always visible, not gated on enrichment_enabled)
- Danger Zone redesign: scope radio (pending-only vs pending+approved), Archive & reset (primary)
vs Hard purge (secondary), inline confirm dialogs, active task list with per-task cancel,
Kill all stuck button, More Options (email purge / non-remote / wipe+rescrape)
- Setup banners: dismissible onboarding hints pulled from /api/config/setup-banners,
5-second polling for active task list to stay live
app/Home.py:
- Danger Zone redesign: same scope radio + archive/purge with confirm steps
- Background task list with per-task cancel and Kill all stuck button
- More options expander (email purge, non-remote, wipe+rescrape)
- Setup banners section at page bottom
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)
- _user_yaml_path(): remove dangerous fallback to /devl/job-seeker/
config/user.yaml (Meg's legacy profile); a missing user.yaml now
returns an empty dict via load_user_profile, never another user's data
- RESUME_PATH: replace hardcoded relative Path('config/plain_text_
resume.yaml') with _resume_path() that derives from _user_yaml_path()
so resume file is always co-located with the correct user.yaml
- upload_resume: was passing a file path string to structure_resume()
which expects raw text; now extracts bytes, dispatches to the correct
extractor (pdf/odt/docx), then passes text — matches Streamlit wizard
- WizardResumeStep.vue: upload response is {ok, data: {experience…}}
but component was reading data.experience (top level); fixed to
read resp.data.experience to match the actual API envelope