Files
tfm_ainventory/.planning/phases/5-core-v2-features/5-PLAN-02.md

6.2 KiB

plan, feature, status, estimated_tasks, total_lines
plan feature status estimated_tasks total_lines
5-PLAN-02 Search & Filtering ready 6 ~520

Phase 5 Plan 02: Search & Filtering

Overview

Implement search modal with real-time search across all item text fields (Name, PN, Barcode, Description, Category, Notes). Results displayed as vertical list; tapping item opens quantity adjustment modal (leveraging Task 1 from Plan 01). No advanced filtering in Phase 5—keep it simple.

Tasks

Task 1: Backend Search Endpoint

  • File: backend/routers/items.py
  • Endpoint: GET /items/search?q={query}
  • Function: search_items(query: str, auth: User) → List[ItemResponse]
  • Lines: ~50
  • Description: Full-text search across Name, Part Number, Barcode, Description, Category, Notes fields. Case-insensitive substring matching. Return max 50 results ordered by relevance (name match > PN > barcode > description).
  • Acceptance Criteria:
    • Accepts query string parameter (min 1 char, max 100 chars)
    • Searches all text fields (case-insensitive)
    • Returns max 50 results (pagination deferred to Phase 6+)
    • Results ordered by relevance score
    • Empty query returns empty list (no "show all")
    • Authorization: users see only items in their accessible locations
    • Unit tests for: exact match, partial match, multi-field, empty results

Task 2: Create SearchModal Component (UI Layer)

  • File: frontend/components/inventory/SearchModal.tsx
  • Component: SearchModal(isOpen: boolean; onClose: () => void; onSelectItem: (item: Item) => void) → JSX.Element
  • Lines: ~180
  • Description: Modal with search input field + real-time result list. Results rendered as clickable item rows. Tapping item emits onSelectItem and closes modal.
  • Acceptance Criteria:
    • Modal opens/closes via isOpen prop
    • Search input with placeholder "Search by name, PN, barcode..."
    • Real-time search triggered on input change (debounced 300ms)
    • Results displayed as vertical list below input
    • Each result row shows: Name, PN, Barcode, current Qty
    • Clicking result: emits onSelectItem, closes modal
    • Loading spinner during API call
    • Error message if search fails
    • Escape key or X button closes modal
    • Accessibility: proper ARIA labels, keyboard navigation (arrow keys, Enter)

Task 3: Create useItemSearch Hook

  • File: frontend/hooks/useItemSearch.ts
  • Hook: useItemSearch(query: string, enabled: boolean) → { results: Item[]; isLoading: boolean; error: string | null }
  • Lines: ~100
  • Description: Custom hook handling search API calls with debouncing and caching. Manages loading/error states.
  • Acceptance Criteria:
    • Debounces API calls (300ms)
    • Caches results per query to avoid redundant calls
    • Returns empty results if query < 2 chars (client-side validation)
    • Handles network errors gracefully
    • Clears cache on component unmount
    • Unit tests: debouncing, caching, error handling

Task 4: Add Search Button to Inventory Page

  • File: frontend/app/inventory/page.tsx
  • Component: Update header/toolbar area
  • Lines: ~30
  • Description: Add prominent Search button (magnifying glass icon) in inventory page header. Toggle SearchModal open/closed on button click.
  • Acceptance Criteria:
    • Search button visible in toolbar/header (Lucide Search icon)
    • Button click opens SearchModal
    • Modal closes on cancel or item selection
    • Selected item triggers quantity adjustment UI (from Plan 01)
    • Mobile-responsive button placement
    • Focus management: focus returns to Search button after modal closes

Task 5: Quantity Adjustment Modal (Plan 01 Integration)

  • File: frontend/components/inventory/QuantityAdjustmentModal.tsx
  • Component: QuantityAdjustmentModal(item: Item | null; isOpen: boolean; onClose: () => void) → JSX.Element
  • Lines: ~140
  • Description: Modal displayed when user taps search result. Allows quick +/- adjustment and commit. Reuse QuantityDisplay component from Plan 01.
  • Acceptance Criteria:
    • Modal shows item name, current quantity
    • Uses QuantityDisplay component from Plan 01 for adjustment
    • +/- buttons, input field, or both available
    • Commit button saves changes
    • Cancel button closes without changes
    • Success toast after save
    • Error toast on failure
    • Mobile responsive

Task 6: Integration & E2E Tests

  • File: frontend/tests/inventory/search.test.ts
  • Test: Full search workflow: open search → type query → select result → adjust quantity
  • Lines: ~200
  • Description: End-to-end test of entire search feature including backend integration.
  • Acceptance Criteria:
    • Test: open search modal, input appears focused
    • Test: type query, results update (mocked API)
    • Test: click result, modal opens with item details
    • Test: adjust quantity, save succeeds, modals close
    • Test: search with no results shows message
    • Test: search with special chars/symbols works
    • Backend integration test: GET /items/search endpoint
    • All assertions pass (Vitest)

Dependencies

  • Task 1 (backend) must complete before Tasks 2-3
  • Task 2 (SearchModal) and Task 3 (hook) can run in parallel
  • Task 4 (add button) depends on Task 2 (SearchModal exists)
  • Task 5 (quantity modal) depends on Plan 01 completion
  • Task 6 (tests) depends on all other tasks

Testing Strategy

  • Unit tests: SearchModal component, useItemSearch hook (Vitest)
  • Integration tests: Full search workflow with mocked API (Vitest)
  • Backend tests: GET /items/search endpoint with various queries (Pytest)
  • E2E: Manual verification: search for item, results appear, select item, adjust quantity

Blockers & Workarounds

  • Real-time search performance: Debounce aggressively (300ms+) to avoid excessive API calls. Cache results to reduce load.
  • Mobile keyboard: SearchModal input should auto-focus and show keyboard on mobile. Test on actual device.
  • Empty state messaging: If query matches 0 items, show friendly message (not blank list).
  • Query length validation: Enforce min 2 chars (client + server) to prevent broad searches.