feat: add web UI, query engine, session management, and 20 E2E tests

- Web UI: login, dashboard, links CRUD, collections, API keys, admin pages
- Query engine: AND/OR/XOR with field filters, tag search, preview endpoint
- Session management: token expiry detection, 401 interceptor, expiry banner
- Links search: tags included, multi-word AND, query mode with set operations
- Collections: static/dynamic, query builder with preview, public tree view
- Save as Collection: convert search results (static) or query (dynamic)
- Dashboard stats: resilient loading with allSettled pattern
- Login page: redesigned with public collections tree view
- Bug fix: query executor None fields crash (notes/description/url/title)
- E2E tests: 20 Playwright tests covering all critical user flows
- All 104 tests passing (84 unit/integration + 20 E2E)
This commit is contained in:
DavidSaylor
2026-05-22 07:46:53 -05:00
parent 77b076c7d7
commit fe4cbc3537
29 changed files with 1410 additions and 78 deletions

View File

@@ -386,4 +386,59 @@ Priority based on sync mode:
- Mobile-first CSS
- Breakpoints: 768px, 1024px
- Touch-friendly UI controls
- Touch-friendly UI controls
## UI/UX Design
### Session Management
#### Token Lifecycle
1. **Login** - stores JWT in localStorage with user info
2. **Page load** - checks token expiry via JWT `exp` claim
3. **API calls** - validates expiry before each request
4. **401 response** - clears storage and redirects to `/login?expired=1`
5. **Expiry warning** - shows banner if token expires in <2 minutes
#### Implementation
- `main.js`: `isTokenExpired()`, `getTokenExpirySeconds()`, `redirectToLogin()`
- `apiFetch()`: Pre-flight expiry check + 401 interceptor
- `dashboard.js`: Expiry warning banner on page load
- `login.html`: Shows expiry message when redirected with `?expired=1`
### Search Architecture
#### Simple Search (Links Page)
- **Input**: Free text with space-separated terms
- **Behavior**: Each term must match at least one field (AND logic)
- **Fields searched**: title, URL, description, notes, tags
- **Backend**: Multiple `OR` clauses per term, combined with `AND` between terms
#### Query Mode (Links Page + Collections)
- **Input**: Query expression with operators
- **Syntax**: `term1 AND term2 OR term3 NOT term4`
- **Operators**: AND (intersection), OR (union), XOR (difference), NOT (negation)
- **Parentheses**: Grouping for precedence control
- **Field filters**: `tag:value`, `url:value`, `title:value`, `description:value`, `path:value`
- **Term sets**: `(term1, term2)` matches any term in the set
### Collection Query Builder
#### Dynamic Collection Modal
- **Type selector** toggles query section visibility
- **Query input** with syntax hint
- **Preview button** fetches matching links via `/api/queries/preview`
- **Results display** shows count and first 10 matching links
- **Save** stores `query_expression: { expression: "..." }` for dynamic collections
#### Save as Collection (Links Page)
- **Static mode**: Saves current result set link IDs
- **Dynamic mode**: Saves the active query expression
- **Modal** pre-fills name based on current search/query
- **Public toggle** controls collection visibility
### Error Handling
- **API errors**: Structured error responses with user-friendly messages
- **Network errors**: Graceful fallbacks with retry options
- **Form validation**: Client-side validation before submission
- **Server errors**: Logged with stack traces in debug mode