9 Commits

Author SHA1 Message Date
cesnimda ea6c3650f3 refactor(api): extract JobApplications DTOs and helpers, fix N+1 aggregation
- Move inline DTOs to JobApplicationDtos.cs, pure static helpers to JobApplicationHelpers.cs
- GetStats aggregates server-side (COUNT/GROUP BY) instead of loading the full table
- Cache RuleSettings via IMemoryCache, keyed per-user (RulesEngine.GetSettings falls back
  to per-user UserRuleSettings overrides, so a single global cache key would leak settings
  across users)
- Add missing AsNoTracking() to read-only GET endpoints (GetAll, GetById, GetBoard,
  GetReminders, GetStatusSuggestion, GetMatchScore, GetCandidateFit, GetFocusPlan,
  GetInterviewPrep, GetReadiness)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 20:08:59 +02:00
cesnimda b4fd5e2f96 fix(jobs): derive attachment checklist flags from actual Attachments
CI and Deploy / test (pull_request) Successful in 2m4s
CI and Deploy / deploy (pull_request) Has been skipped
Backlog item 4 (Wave 3, first sub-item). HasResume/HasCoverLetter/HasPortfolio/
HasOtherAttachment were manually-editable checkboxes in EditJobDialog,
completely independent of whether a file was actually attached -- classic
drift: mark 'resume ready' by hand, later delete the resume attachment, flag
stays stuck true forever. User confirmed (asked directly, since removing the
manual-override capability is a product decision, not purely technical):
make them fully computed from Attachments, no manual override.

- AttachmentsController.RecomputeAttachmentFlagsAsync: the single place these
  four fields get written now, called after every attachment mutation
  (upload, delete, Purpose change) that could affect them. Deliberately kept
  as persisted columns (not [NotMapped] computed properties reading the
  Attachments navigation collection) -- ~15 query sites build JobApplication
  DTOs without .Include(Attachments), so a live-computed property would
  silently return false everywhere instead of throwing, the worst kind of
  bug. Recomputing at the one write funnel avoids touching any read path.
- Removed HasResume/etc from CreateJobApplicationRequest/
  UpdateJobApplicationRequest -- no longer client-settable.
- EditJobDialog: removed the manual checkboxes, kept the (now genuinely
  accurate) read-only status chips.
- AddJobModal: stopped sending has*-flags at job-creation time; the
  follow-up attachment upload call now sets them correctly via the same
  recompute path.

Caught a real bug while testing this: the Purpose-change path recomputed
before saving the Purpose change, so a fresh query missed the pending edit
and the flags never updated. Fixed by committing the mutation before
recomputing.

3 new backend tests (purpose-change sets flag, delete clears flag,
non-primary purpose counts as "other"). 172/172 backend, 25/25 frontend
suites (57 tests) green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:10:39 +02:00
cesnimda ae3505b877 feat: deterministic email-driven status suggestions
New EmailStatusClassifier scans a message subject/body for outcome
signals (interview invite, offer, rejection) and suggests a canonical
pipeline status. Priority-ordered so a rejection that mentions the prior
interview still classifies as Rejected. Deterministic - no AI - so it is
instant, reproducible, and safe.

- GET /api/jobapplications/{id}/status-suggestion reads the job's latest
  inbound correspondence (incl. Gmail imports) and suggests a forward
  status move, suppressed when already in/past that stage
- always human-confirmed via the existing PATCH .../status
- 7 classifier unit tests + 2 endpoint integration tests; backend green (133)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 03:48:32 +02:00
cesnimda 209528c8b5 feat(ui): instant match-score panel on the Candidate Fit tab
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>
2026-07-03 03:24:53 +02:00
cesnimda 83e6430a24 feat: structured salary fields (min/max/currency/period)
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>
2026-07-02 22:15:37 +02:00
cesnimda 517c42250d Refactor repeated test host setup 2026-04-01 10:48:28 +02:00
cesnimda 9f949ee9df Harden password reset and email send flows 2026-03-28 14:17:12 +01:00
cesnimda 13d4e29336 chore(M001/S01): auto-commit after complete-slice 2026-03-24 12:27:04 +01:00
cesnimda 1fe3a68901 feat: add editable application drafts tests and gitea deploy workflow 2026-03-22 18:42:14 +01:00