3.9 KiB
Application Tracker Feature
A Kanban board for managing the job-application pipeline, auto-populated from the tailor flow.
Overview
The Application Tracker (/tracker) gives each tailored resume a place in a
seven-column Kanban pipeline. Tailoring a resume to a job auto-creates an
applied card; users can also add cards manually from a pasted job
description. Cards are drag-and-drop reorderable within and across columns.
Columns (stable keys, decoupled from i18n labels)
saved · applied · no_response · response · interview · accepted · rejected
Auto-created cards from the tailor flow land in applied. Manual cards
default to applied but can be created as saved.
How It Works
- Auto-create:
POST /resumes/improve/confirm(and the legacyPOST /resumes/improve) create anappliedcard after persisting the tailored resume — best-effort (a tracker failure never breaks tailoring). Company/role come from the cached keyword-extraction pass, so there is no extra LLM call on this path. - Manual add:
POST /applicationscreates the job from the pasted JD then the card; when company/role aren't supplied it runs one best-effort extraction call (falls back to blank/editable). - Drag/drop: cards reorder within a column or move across columns; the
board updates optimistically and reverts on a failed
PATCH. - Detail modal: shows the JD + the applied resume; Edit opens
/builder?id=<resume_id>. Tolerates a deleted resume (resume: null). - Bulk actions: multi-select cards to move or delete in one request.
Data Model
Application (SQLite, apps/backend/app/models.py): application_id (PK),
job_id, resume_id (the applied/tailored resume), master_resume_id
(optional base — powers the "shared resume" badge), status (7-key enum),
company, role, applied_at, notes, position (per-column order,
server-renumbered on PATCH), created_at, updated_at. create_application
dedupes on (job_id, resume_id) to survive double-submit.
API (prefix=/applications, mounted under /api/v1)
| Method | Path | Purpose |
|---|---|---|
| GET | /applications |
All cards grouped by column (all 7 keys present) |
| POST | /applications |
Manual add (creates job + card; best-effort extraction) |
| GET | /applications/{id} |
Card + embedded JD + resume (resume null if deleted) |
| PATCH | /applications/{id} |
Update status/position/notes/company/role/applied_at |
| PATCH | /applications/bulk |
Move many cards to one column |
| DELETE | /applications/{id} |
Delete one card |
| POST | /applications/bulk-delete |
Delete many cards |
Key Files
| File | Purpose |
|---|---|
apps/backend/app/models.py |
Application ORM model |
apps/backend/app/schemas/applications.py |
Pydantic request/response schemas + status enum |
apps/backend/app/routers/applications.py |
The tracker endpoints |
apps/backend/app/database.py |
Facade CRUD/bulk/reorder methods |
apps/backend/app/routers/resumes.py |
_auto_create_tracker_application hook (both confirm paths) |
apps/backend/app/services/improver.py + app/prompts/templates.py |
Company/role added to keyword extraction |
apps/frontend/app/(default)/tracker/page.tsx |
Route |
apps/frontend/components/tracker/* |
Board, column, card, detail modal, bulk bar, manual-add dialog |
apps/frontend/components/tracker/reorder.ts |
Pure drag-end resolution (planMove) |
apps/frontend/lib/api/tracker.ts |
Typed API client |
Tests
- Backend:
tests/integration/test_applications_api.py(CRUD, grouping, detail tolerance, bulk),tests/integration/test_tracker_autocreate.py(confirm auto-creates anappliedcard),tests/unit/test_database.py::TestApplications. - Frontend:
tests/tracker-reorder.test.ts(planMovewithin/cross-column + empty-column drop),tests/api-tracker.test.ts(client payloads/URLs).