feat: fruit search by name/synonym and type filter (story #06) #6

Merged
julia merged 1 commits from feature/06-fruit-search into main 2026-06-18 10:58:37 +00:00
Owner

Summary

  • Backend: List gains name (ILIKE with wildcard escaping, synonym LEFT JOIN, SELECT DISTINCT) and types (ANY cast) params; handler parses ?name= and ?type=, resolves combined-type aliases, validates plain types against validFruitTypes to prevent Postgres enum cast errors; ORDER BY f.name, f.id for stable pagination
  • Frontend: listFruits gains optional {name, type} params; store adds searchName/searchType state and setSearch action; FruitList.vue wires text input (debounced 300 ms + Enter) and flat type dropdown (17 enum values + 4 aliases per spec §5 order)

Test plan

  • make test passes (backend handler + repo + frontend + scripts)
  • Search "Boskop" → only matching fruits shown
  • Search "boskop" (lowercase) → same results (ILIKE case-insensitive)
  • Filter "Birnensorten" → only Birnensorten
  • Filter "Birnen- und Quittensorten" → both Birnensorten and Quittensorten
  • Combined name + type → intersection
  • Empty search + no type → all fruits
  • Search with % or _ in name → treated as literal, not SQL wildcard
  • Unknown ?type= value → no 500 (graceful empty result)
  • Typing then navigating away → no spurious fetch after unmount

🤖 Generated with Claude Code

## Summary - Backend: `List` gains `name` (ILIKE with wildcard escaping, synonym LEFT JOIN, SELECT DISTINCT) and `types` (ANY cast) params; handler parses `?name=` and `?type=`, resolves combined-type aliases, validates plain types against `validFruitTypes` to prevent Postgres enum cast errors; ORDER BY `f.name, f.id` for stable pagination - Frontend: `listFruits` gains optional `{name, type}` params; store adds `searchName`/`searchType` state and `setSearch` action; `FruitList.vue` wires text input (debounced 300 ms + Enter) and flat type dropdown (17 enum values + 4 aliases per spec §5 order) ## Test plan - [ ] `make test` passes (backend handler + repo + frontend + scripts) - [ ] Search "Boskop" → only matching fruits shown - [ ] Search "boskop" (lowercase) → same results (ILIKE case-insensitive) - [ ] Filter "Birnensorten" → only Birnensorten - [ ] Filter "Birnen- und Quittensorten" → both Birnensorten and Quittensorten - [ ] Combined name + type → intersection - [ ] Empty search + no type → all fruits - [ ] Search with `%` or `_` in name → treated as literal, not SQL wildcard - [ ] Unknown `?type=` value → no 500 (graceful empty result) - [ ] Typing then navigating away → no spurious fetch after unmount 🤖 Generated with [Claude Code](https://claude.com/claude-code)
julia added 1 commit 2026-06-18 10:51:38 +00:00
Backend: List repo query gains name (ILIKE with wildcard escaping on
name + synonym LEFT JOIN, SELECT DISTINCT) and types (ANY cast) params;
handler parses ?name= and ?type=, resolves combined-type aliases from
domain.FruitTypeAliases, validates plain types against validFruitTypes
to prevent Postgres enum cast errors. ORDER BY f.name, f.id for stable
pagination.

Frontend: listFruits gains optional {name, type} params; fruitStore adds
searchName/searchType state and setSearch action (resets offset, refetches);
FruitList.vue wires text input (debounced 300 ms + Enter) and flat type
dropdown (17 enum values + 4 aliases per spec §5 order); debounce timer
cleared on unmount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
julia merged commit 2525e8b68d into main 2026-06-18 10:58:37 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: julia/osdb-1-claude-opusplan#6