6.2 KiB
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
onSelectItemand closes modal. - Acceptance Criteria:
- Modal opens/closes via
isOpenprop - 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)
- Modal opens/closes via
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.