Skip to content

fix(search): make the "QSL Pending" filter actually filter - #238

Merged
patrickrb merged 1 commit into
mainfrom
optio/task-4b6792ef-6608-4a7c-bd53-72b68ee44a1f
Jul 24, 2026
Merged

fix(search): make the "QSL Pending" filter actually filter#238
patrickrb merged 1 commit into
mainfrom
optio/task-4b6792ef-6608-4a7c-bd53-72b68ee44a1f

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Problem

The contact search page exposes a QSL Status → Pending option in its Advanced Filters dropdown (src/app/search/page.tsx), and the active-filter chip labels it "QSL Pending". But the shared query builder behind GET /api/contacts/search (src/lib/contact-search.ts) only handled confirmed and not_confirmed. The pending value fell straight through the switch and added no predicate.

Result: selecting "Pending" was a silent no-op — the search returned every contact rather than the ones awaiting a QSL reply. The existing test even codified the wrong behavior ('pending' "contributes nothing"), so the gap between UI and backend went unnoticed.

Solution

  • Add a SENT_QSL_SQL expression: true when a QSL has been sent, requested, or queued on any channel — paper (qsl_sent), eQSL (eqsl_qsl_sent), LoTW (lotw_qsl_sent), or QRZ (qrz_qsl_sent) — per the ADIF QSL-status enumeration (Y/R/Q). It's COALESCE-wrapped to a never-NULL boolean, mirroring the existing CONFIRMED_QSL_SQL, so it composes cleanly.
  • Wire qslStatus = 'pending' to (SENT_QSL_SQL AND NOT CONFIRMED_QSL_SQL) — i.e. you've done your part but no source has confirmed yet. This is a strict subset of not_confirmed.
  • Like the other QSL-status cases it is predicate-only — it binds no parameter, so the $N placeholder numbering (the subject of fix(search): keep SQL placeholders aligned when QSL status + DXCC filters combine #236/fix(search): repair QSL-status filter that referenced a nonexistent confirmed column #237) stays intact when combined with value-bound filters like DXCC.

No UI, schema, or API-shape changes — the frontend already sends qslStatus=pending; it just now does something.

Testing

  • Updated tests/contact-search.spec.ts:
    • New test asserting pending emits (SENT_QSL_SQL AND NOT CONFIRMED_QSL_SQL) with no bound param.
    • New test asserting SENT_QSL_SQL references only the real *_qsl_sent columns (never a received column or a bare confirmed).
    • Retargeted the "unrecognized status contributes nothing" test to a genuinely bogus value, since pending is now recognized.
  • npx playwright test contact-search — 13 passed.
  • npm run typecheck, npm run lint, npm run build — all clean.

Follow-up (out of scope)

  • The search page's QSL-status dropdown could surface distinct "sent-not-confirmed" vs "never-actioned" states now that the backend distinguishes them, but the current three-way (confirmed / pending / not confirmed) is coherent as-is.

🤖 Generated with Claude Code

The search UI offers a QSL Status → Pending option, but the query builder
only handled `confirmed` and `not_confirmed`. `pending` fell through the
switch and added no predicate, so selecting it silently returned every
contact instead of the ones awaiting a reply.

Add a NULL-safe SENT_QSL_SQL expression (a QSL sent/requested/queued on
paper, eQSL, LoTW, or QRZ per the ADIF Y/R/Q states) and wire `pending`
to `SENT_QSL_SQL AND NOT CONFIRMED_QSL_SQL` — a strict subset of
not_confirmed. Predicate-only, so it binds no parameter and keeps the
placeholder numbering intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nodelog Ready Ready Preview, Comment Jul 24, 2026 4:06am

Request Review

@patrickrb
patrickrb merged commit 75d0226 into main Jul 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant