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:
@@ -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
|
||||
Reference in New Issue
Block a user