- Adds a 'Median time in stage' block to the conversion-funnel card
showing median days and active count per stage from the enriched
analytics-overview endpoint.
- Funnel bar labels and stage names now render through the shared
pipeline statusLabel (localized; the funnel also now includes Waiting).
- EN/NB translations. Full frontend suite green (20 suites / 46 tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New pure StageAnalytics.TimeInStage: median days jobs have spent in
each active pipeline stage (entry time from the last StatusChanged
event into that stage, else applied date). Closed/success stages
excluded since 'how long stuck' only applies to actionable stages.
- analytics-overview now derives the funnel from JobPipeline (includes
the previously-omitted Waiting stage, normalizes legacy spellings) and
returns TimeInStage.
- 4 unit tests; full backend suite green (124).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Introduces pipeline.ts (mirrors backend JobPipeline) as the one frontend
source of truth for canonical stages, synonym normalization, tone, and
localized labels. Replaces the status list/logic previously duplicated
across KanbanBoard, JobTable, AddJobModal and EditJobDialog.
- KanbanBoard/AddJobModal/EditJobDialog render from PIPELINE_STATUSES
- JobTable uses shared statusTone + statusLabel (status chips now
localized; NB gets proper labels, English unchanged)
- Edit dialog status dropdown is now localized too
- 5 unit tests; full frontend suite green (19 suites / 41 tests)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New JobPipeline: ordered canonical stages (Applied, Waiting, Interview,
Offer, Rejected, Ghosted) with category grouping and a Normalize() that
canonicalizes casing and known synonyms (Interviewing->Interview,
declined->Rejected, ...) while preserving unknown custom statuses.
- normalize status on every write path (Create/Update/PATCH status) so
the stored value stays canonical without destroying custom values
- GET /api/jobapplications/pipeline exposes the ordered stages so the UI
renders from one source instead of duplicated hardcoded lists
- 14 unit tests; full backend suite green (120)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a MatchScoreCard at the top of the Candidate Fit tab that loads the
deterministic /match-score endpoint independently of the slow AI
narrative, so users see a reproducible score, matched/missing keyword
chips, and per-section coverage immediately.
- MatchScore types + cached, attachment-independent load effect
- graceful 'not enough signal' state
- EN/NB translations
- frontend panel test (matched/missing/section + degraded state)
- backend integration tests for GetMatchScore (happy path + missing CV)
- README endpoint reference
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New JobCvMatchService: a pure, AI-free keyword-coverage scorer that
returns a stable, reproducible 0-100 match score plus matched/missing
keyword lists and per-CV-section coverage. Unlike candidate-fit (AI
narrative), it makes no model calls, so results are instant and
identical for identical inputs - the Jobscan-style differentiator.
- GET /api/jobapplications/{id}/match-score
- keywords = curated SkillTagger tags (high weight) + salient posting
terms (title terms boosted); word-boundary matching avoids false hits
- section coverage shows where CV evidence is concentrated
- fix(SkillTagger): punctuation-tolerant C#/.NET patterns; the old \b
boundaries silently missed 'C#,' and '.NET,' everywhere they are used
- 7 unit tests on the pure scorer; full backend suite green (104)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds SalaryMin/SalaryMax/SalaryCurrency/SalaryPeriod alongside the
existing free-text Salary field (kept for back-compat and display).
- JobApplication model + idempotent column bridging for SQLite and MySQL
- Create/Update DTOs with NormalizeSalary (clamps negatives, swaps
inverted min/max, uppercases currency, whitelists period)
- JobApplicationDto exposes the fields; CSV export gains 4 columns
- UI: add/edit dialogs get min/max/currency/period inputs; job table
renders a formatted range via shared salary.ts formatter (falls back
to free-text when structured values are absent)
- EN/NB translations; backend + full frontend suites green
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AddOpenApi/MapOpenApi (anonymous, Development environment only).
- security: mark ProfileCvController.ProcessQueuedRunAsync [NonAction] -
the controller-level [Route] exposed this background-service hook as a
routable any-verb endpoint, which also broke OpenAPI generation.
96 endpoint paths documented.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The whitelist silently skipped new suites; two regressions in
non-whitelisted suites reached main unnoticed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Raise testing-library asyncUtilTimeout to 4s and jest timeout to 30s:
heavy MUI views exceeded the 1s default on slower machines
(profile-page, daily-control-loop double-mount).
- end-to-end-trust-loop: mock the /tailored-cv-draft endpoint the
redesigned Tailored CV tab now loads, and assert on the structured
draft instead of the removed legacy tailoredCvText textarea.
Full suite now green locally: 18/18 suites, 39/39 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New DatabaseBackupHostedService + SqliteDatabaseBackupRunner:
- daily VACUUM INTO snapshot to <Data:Root>/backups (safe with WAL)
- catch-up backup at startup when none exists from the last 24h
- retention pruning (Backups:RetainCount, default 14)
- warns and stays idle on MySQL/MariaDB where external backups apply
Production previously had no automated database backup on Linux
(the /api/backup endpoint is Windows-DPAPI-only).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the latest load callback in a ref so reload() always invokes the
current fetcher without changing its own identity on every render.
Reduces full-suite test failures from 5 to 3 (remaining are pre-existing).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1-3 deliverables: full architecture/security/tech-debt map,
2026 market research with feature matrix, and tiered execution roadmap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- git rm --cached on committed DataProtection key XMLs (keys/, JobTrackerApi/keys/)
and daily export JSON snapshots; extend .gitignore so runtime data
(keys, exports, CV artifacts/exports/benchmarks) can never be committed again.
- Delete root Controller/ stubs: an early prototype compiled by no project
(JobTrackerApi excludes them; JobTrackerBackend globs only JobTrackerApi/Controllers).
- NOTE: the removed key XMLs remain in git history; rotating DataProtection
keys on the server is recommended.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>