# Slice 8 — Authentik Users + Messaging tabs (worker output) ## Files changed | File | Status | Lines | |------|--------|-------| | `backend/src/media_library_viewer_api/routers/authentik_users.py` | modified | +66/-8 (added MessageRequest model + status + message endpoints) | | `frontend/src/api/authentik.ts` | new | 65 | | `frontend/src/hooks/useAuthentik.ts` | new | 43 | | `frontend/src/pages/service-tabs/UsersTab.tsx` | new | 136 | | `frontend/src/pages/service-tabs/MessagingTab.tsx` | new | 131 | | `frontend/src/pages/service-tabs/__tests__/UsersTab.test.tsx` | new | 49 | | `frontend/src/pages/service-tabs/__tests__/MessagingTab.test.tsx` | new | 47 | | `frontend/src/pages/service-tabs/index.ts` | modified | +4/-4 | | `frontend/src/pages/service-tabs/stubs.tsx` | modified | -8 (removed UsersTab/MessagingTab stubs) | **Total: ~539 lines** (471 new files + 68 modified diff). Over the 400-line budget; dominated by the new-build UsersTab + MessagingTab (no existing UI to lift — built from scratch against the Authentik endpoint). ## Backend message endpoint (Option A — implemented) Added to `routers/authentik_users.py`: ``` GET /api/services/authentik/{service_id}/message/status POST /api/services/authentik/{service_id}/message ``` **POST body** (`MessageRequest`): ```json { "recipient_emails": ["alice@example.com"], "subject": "...", "html_body": "..." } ``` **Response** (success): ```json { "status": "queued", "request_id": "abc123", "recipient_count": 1 } ``` **Response** (error — service not configured / no recipients / SMTP invalid): ```json { "status": "error", "error": "description" } ``` The endpoint resolves the Authentik service record, validates SMTP settings, then enqueues via the existing `mail_queue.enqueue()`. The GET status endpoint proxies `mail_queue.status()`. Both are service-id scoped and return graceful errors matching the directory endpoint's pattern. ## UsersTab columns | Column | Source field | Notes | |--------|-------------|-------| | Name | `user.name` | Falls back to "—" | | Username | `user.username` | | | Email | `user.email` | Falls back to "—" | | Status | `user.is_active` | Badge: "Active" (default) / "Inactive" (secondary) | Features: search input (committed on Enter/click), pagination (25 per page), error-Alert when endpoint returns an error field. ## MessagingTab Compose form with: - Recipient search + toggle buttons (from Authentik users with emails) - Subject input - HTML body textarea (default template) - Send button wired to POST `/api/services/authentik/{id}/message` - Success/error Alert on mutation result - Recipient count display ## Validation ``` cd backend && .venv/bin/ruff check src/ tests/ → All checks passed! cd backend && .venv/bin/python -m pytest tests/ → 271 passed, 2 warnings cd frontend && npm run lint → 0 errors, 2 pre-existing warnings cd frontend && npm run build → ✓ built (tsc -b + vite) cd frontend && npm run test → 32 files / 100 tests passed (was 96; +4 new) ``` ## Deviations from design 1. **Over 400-line budget.** The UsersTab and MessagingTab are built from scratch (no existing Users UI to lift — the old page was Jellyfin-backed and deleted). Could not shrink without dropping functionality. 2. **MessagingTab is simplified vs. the old compose UI.** The old UsersPage had rich-text formatting toolbar (bold/italic/link/bullet), attachment upload, email preview iframe, and detailed queue-status banners. This slice implements a minimal but functional compose (recipient selection + subject + HTML body + send + result alert). Rich-text toolbar + attachments are follow-ups. The backend endpoint accepts the core fields (recipient_emails, subject, html_body) but not attachments yet. 3. **No attachment upload.** The mail_queue.enqueue() accepts attachments, but the POST endpoint does not accept multipart yet. Attachments are a follow-up (requires multipart handling on the endpoint + attachment UI). 4. **Queue status polled via a dedicated hook.** `useAuthentikMessageStatus(serviceId)` polls `/api/services/authentik/{id}/message/status` every 5s. The MessagingTab does not yet display the queue status banner (minimal UI); the hook + endpoint exist for the follow-up that adds the queue indicator. ## skill_resolution `none` — no project/user SKILL.md paths were injected, and no `.atl/skill-registry.md` was found. ## Residual risks - **MessagingTab lacks rich-text toolbar + attachment upload + queue-status banner.** These are follow-ups; the core send flow works. - **Old UsersPage.impl.tsx + its test file still pass** (rendered directly, not via routing). Deleted in Slice 11 cleanup. - **Backend message endpoint returns 200 on error** (not 4xx/5xx), matching the directory endpoint's pattern. The frontend checks the `status`/`error` field.