feat(contacts): distinct title/department values endpoint for autocomplete (PMS-583) #411

Merged
longjacksonle merged 1 commit from feat/PMS-583-contact-field-suggestions into main 2026-06-30 18:21:30 +02:00

PMS-583 (backend half)

The contact form's Title and Department stay free-text, but should suggest from the tenant's own past entries (server-sourced, not browser cache, not a fixed dropdown). This adds the endpoint that supplies those suggestions.

Endpoint

GET /api/v1/contacts/field-values?field=title|department&q=<substring>

  • Returns a plain ["..."] string list of the tenant's distinct non-empty values for the field.
  • Ranked by frequency (most-used first), then alphabetically; capped at 20.
  • q is an optional case-insensitive substring filter; blank/absent means no filter.

Safety

  • field is a closed enum (ContactSuggestField) mapping only to the whitelisted title / department columns, so the interpolated column name is never user-controlled. The q term is always a bound parameter.
  • Tenant-scoped via begin_with_tenant, like every other contacts query.
  • A DB-free unit test asserts the enum rejects anything outside the two variants and maps to the right column.

No migration

The columns are already VARCHAR(100) free text. Existing values just become the suggestion corpus. No schema change, no data movement.

Companion

The frontend half (a reusable free-text-with-suggestions input wired into Title and Department) is a separate mokosh-apps PR.

🤖 Generated with Claude Code

## PMS-583 (backend half) The contact form's Title and Department stay free-text, but should suggest from the tenant's own past entries (server-sourced, not browser cache, not a fixed dropdown). This adds the endpoint that supplies those suggestions. ## Endpoint `GET /api/v1/contacts/field-values?field=title|department&q=<substring>` - Returns a plain `["..."]` string list of the tenant's distinct non-empty values for the field. - Ranked by frequency (most-used first), then alphabetically; capped at 20. - `q` is an optional case-insensitive substring filter; blank/absent means no filter. ## Safety - `field` is a closed enum (`ContactSuggestField`) mapping only to the whitelisted `title` / `department` columns, so the interpolated column name is never user-controlled. The `q` term is always a bound parameter. - Tenant-scoped via `begin_with_tenant`, like every other contacts query. - A DB-free unit test asserts the enum rejects anything outside the two variants and maps to the right column. ## No migration The columns are already `VARCHAR(100)` free text. Existing values just become the suggestion corpus. No schema change, no data movement. ## Companion The frontend half (a reusable free-text-with-suggestions input wired into Title and Department) is a separate mokosh-apps PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(contacts): distinct title/department values endpoint for autocomplete (PMS-583)
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 1m22s
Check / fmt + clippy + build + tests (pull_request) Successful in 1m49s
Integration / integration tests (pull_request) Successful in 8m36s
Create release / Gate (release-branch merges only) (pull_request) Successful in 3s
Create release / Create release from merged PR (pull_request) Has been skipped
cd8b6a8b40
Adds GET /api/v1/contacts/field-values?field=title|department&q=<substring>, returning the tenant's existing distinct non-empty values for that free-text field, ranked by frequency (most-used first), then alphabetically, capped at 20. This backs the contact form's free-text autocomplete: Title and Department stay open free-text fields, but suggestions are drawn from what the tenant has already entered (server-sourced, shared across users and devices, not browser cache).

`field` is a closed enum (ContactSuggestField) that maps only to the whitelisted `title` / `department` columns, so the interpolated column name is never user-controlled; the `q` substring is always bound. Tenant scoping goes through begin_with_tenant like every other contacts query.

No schema change and no data migration: the columns are already VARCHAR(100) free text, and existing values simply become the suggestion corpus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/PMS-583-contact-field-suggestions 2026-06-30 18:21:31 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!411
No description provided.