|
| 1 | +# Search Apprentice Feature - Implementation Plan |
| 2 | + |
| 3 | +## Overview |
| 4 | +Add a Search Apprentice card to the admin dashboard that allows users to quickly search for apprentices by name and navigate directly to their detail page. |
| 5 | + |
| 6 | +## User Flow |
| 7 | +1. User types in search field on admin dashboard |
| 8 | +2. System shows live search results matching the query |
| 9 | +3. User clicks on an apprentice from results |
| 10 | +4. System navigates to apprentice detail page (`/admin/apprentices/[id]`) |
| 11 | + |
| 12 | +## Technical Architecture |
| 13 | + |
| 14 | +### 1. Search API Endpoint |
| 15 | +**File**: `src/routes/api/apprentices/search/+server.ts` |
| 16 | + |
| 17 | +- **Method**: GET |
| 18 | +- **Query params**: `q` (search query) |
| 19 | +- **Returns**: Array of matching apprentices with id, name, email, cohort |
| 20 | +- **Search logic**: |
| 21 | + - Case-insensitive search |
| 22 | + - Matches against apprentice name |
| 23 | + - Returns up to 10 results |
| 24 | + - Orders by relevance/alphabetical |
| 25 | + |
| 26 | +### 2. Search Component |
| 27 | +**File**: `src/lib/components/SearchApprentice.svelte` |
| 28 | + |
| 29 | +**Features**: |
| 30 | +- Text input field with search icon |
| 31 | +- Debounced search (300ms) to avoid excessive API calls |
| 32 | +- Loading state while searching |
| 33 | +- Dropdown results list |
| 34 | +- Keyboard navigation (arrow keys + enter) |
| 35 | +- Click outside to close |
| 36 | +- Empty state when no results |
| 37 | +- Error handling |
| 38 | + |
| 39 | +**Props**: |
| 40 | +- No props needed (self-contained) |
| 41 | + |
| 42 | +**State**: |
| 43 | +- `searchQuery`: Current search text |
| 44 | +- `searchResults`: Array of apprentice results |
| 45 | +- `isSearching`: Loading state |
| 46 | +- `showResults`: Dropdown visibility |
| 47 | +- `selectedIndex`: For keyboard navigation |
| 48 | + |
| 49 | +### 3. Dashboard Integration |
| 50 | +**File**: `src/routes/admin/+page.svelte` |
| 51 | + |
| 52 | +**Add new card**: |
| 53 | +```svelte |
| 54 | +<a href="/admin/apprentices" class="group block..."> |
| 55 | + <SearchApprentice /> |
| 56 | +</a> |
| 57 | +``` |
| 58 | + |
| 59 | +**Card design**: |
| 60 | +- Title: "Search Apprentice" |
| 61 | +- Description: "Find and view apprentice details" |
| 62 | +- Icon: 🔍 (magnifying glass) |
| 63 | +- Color scheme: Purple (to differentiate from other cards) |
| 64 | + |
| 65 | +### 4. Data Types |
| 66 | +**File**: `src/lib/types/apprentice.ts` |
| 67 | + |
| 68 | +```typescript |
| 69 | +interface ApprenticeSearchResult { |
| 70 | + id: string; |
| 71 | + name: string; |
| 72 | + email: string; |
| 73 | + cohortNumber?: number; |
| 74 | + status: 'Active' | 'On Leave' | 'Off-boarded'; |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +## Implementation Steps |
| 79 | + |
| 80 | +### Phase 1: Backend |
| 81 | +1. Create search API endpoint |
| 82 | +2. Implement Airtable search logic |
| 83 | +3. Add proper error handling |
| 84 | +4. Test with various search queries |
| 85 | + |
| 86 | +### Phase 2: Search Component |
| 87 | +1. Create SearchApprentice component structure |
| 88 | +2. Implement search input with debouncing |
| 89 | +3. Add API integration |
| 90 | +4. Implement results dropdown |
| 91 | +5. Add keyboard navigation |
| 92 | +6. Style component to match existing design |
| 93 | + |
| 94 | +### Phase 3: Dashboard Integration |
| 95 | +1. Add new card to admin dashboard |
| 96 | +2. Integrate SearchApprentice component |
| 97 | +3. Style card to match existing cards |
| 98 | +4. Test navigation to apprentice details |
| 99 | + |
| 100 | +### Phase 4: Polish & Testing |
| 101 | +1. Add loading states |
| 102 | +2. Implement error handling |
| 103 | +3. Add empty states |
| 104 | +4. Test edge cases |
| 105 | +5. Ensure mobile responsiveness |
| 106 | + |
| 107 | +## Component Structure |
| 108 | + |
| 109 | +```svelte |
| 110 | +<div class="search-apprentice"> |
| 111 | + <div class="search-input-wrapper"> |
| 112 | + <input |
| 113 | + type="text" |
| 114 | + placeholder="Search apprentices..." |
| 115 | + bind:value={searchQuery} |
| 116 | + on:input={handleSearch} |
| 117 | + /> |
| 118 | + <svg class="search-icon">...</svg> |
| 119 | + </div> |
| 120 | +
|
| 121 | + {#if showResults} |
| 122 | + <div class="search-results"> |
| 123 | + {#if isSearching} |
| 124 | + <div class="loading">Searching...</div> |
| 125 | + {:else if searchResults.length > 0} |
| 126 | + <ul> |
| 127 | + {#each searchResults as result, index} |
| 128 | + <li> |
| 129 | + <a href="/admin/apprentices/{result.id}"> |
| 130 | + <span class="name">{result.name}</span> |
| 131 | + <span class="email">{result.email}</span> |
| 132 | + {#if result.cohortNumber} |
| 133 | + <span class="cohort">Cohort {result.cohortNumber}</span> |
| 134 | + {/if} |
| 135 | + </a> |
| 136 | + </li> |
| 137 | + {/each} |
| 138 | + </ul> |
| 139 | + {:else if searchQuery} |
| 140 | + <div class="no-results">No apprentices found</div> |
| 141 | + {/if} |
| 142 | + </div> |
| 143 | + {/if} |
| 144 | +</div> |
| 145 | +``` |
| 146 | + |
| 147 | +## Styling Considerations |
| 148 | +- Match existing card styles (rounded corners, shadows, hover effects) |
| 149 | +- Use purple color scheme for differentiation |
| 150 | +- Dropdown should have z-index to appear above other content |
| 151 | +- Mobile-first responsive design |
| 152 | +- Smooth transitions for dropdown appearance |
| 153 | + |
| 154 | +## API Response Example |
| 155 | +```json |
| 156 | +{ |
| 157 | + "success": true, |
| 158 | + "apprentices": [ |
| 159 | + { |
| 160 | + "id": "recXXXXXXXXXXXXX", |
| 161 | + "name": "John Doe", |
| 162 | + "email": "john.doe@example.com", |
| 163 | + "cohortNumber": 12, |
| 164 | + "status": "Active" |
| 165 | + } |
| 166 | + ] |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +## Error Handling |
| 171 | +- Network errors: Show "Failed to search" message |
| 172 | +- Empty results: Show "No apprentices found" |
| 173 | +- Rate limiting: Implement debouncing |
| 174 | +- Invalid input: Minimum 2 characters to search |
| 175 | + |
| 176 | +## Performance Considerations |
| 177 | +- Debounce search input (300ms) |
| 178 | +- Limit results to 10 items |
| 179 | +- Cancel previous search requests if new one initiated |
| 180 | +- Cache recent searches (optional enhancement) |
| 181 | + |
| 182 | +## Accessibility |
| 183 | +- Proper ARIA labels for search input |
| 184 | +- Keyboard navigation support |
| 185 | +- Screen reader announcements for results |
| 186 | +- Focus management when opening/closing dropdown |
| 187 | + |
| 188 | +## Testing Checklist |
| 189 | +- [ ] Search returns correct results |
| 190 | +- [ ] Clicking result navigates to apprentice page |
| 191 | +- [ ] Keyboard navigation works |
| 192 | +- [ ] Click outside closes dropdown |
| 193 | +- [ ] Debouncing prevents excessive API calls |
| 194 | +- [ ] Error states display correctly |
| 195 | +- [ ] Loading state shows during search |
| 196 | +- [ ] Mobile responsive design works |
| 197 | +- [ ] Accessibility features work |
| 198 | + |
| 199 | +## Future Enhancements |
| 200 | +- Search by email as well as name |
| 201 | +- Search by cohort number |
| 202 | +- Recent searches history |
| 203 | +- Advanced filters (status, cohort, etc.) |
| 204 | +- Fuzzy matching for typos |
0 commit comments