Files
srbhr--resume-matcher/docs/agent/features/application-tracker.md
T
wehub-resource-sync 5bdf4cc89a
Publish Docker Image / publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:36 +08:00

78 lines
3.9 KiB
Markdown

# 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
1. **Auto-create:** `POST /resumes/improve/confirm` (and the legacy
`POST /resumes/improve`) create an `applied` card 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.
2. **Manual add:** `POST /applications` creates 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).
3. **Drag/drop:** cards reorder within a column or move across columns; the
board updates optimistically and reverts on a failed `PATCH`.
4. **Detail modal:** shows the JD + the applied resume; **Edit** opens
`/builder?id=<resume_id>`. Tolerates a deleted resume (`resume: null`).
5. **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 an `applied` card), `tests/unit/test_database.py::TestApplications`.
- Frontend: `tests/tracker-reorder.test.ts` (`planMove` within/cross-column +
empty-column drop), `tests/api-tracker.test.ts` (client payloads/URLs).