48 Commits

Author SHA1 Message Date
curo1305 cce70b2ef6 refactor(frontend): extract shared modules, thin views, delete dead code
Shared utilities:
- Add src/utils/formatters.js — formatDate, formatSize, providerColor,
  providerBg, providerLabel; all components import from here, no inline duplicates
- Add src/components/ui/TreeItem.vue — generic expand/collapse tree node;
  FolderTreeItem, CloudFolderTreeItem, CloudProviderTreeItem now wrap it
- Add src/components/storage/StorageBrowser.vue — unified file browser grid
  used by both FileManagerView and CloudFolderView

View refactor (thin data-providers):
- FileManagerView.vue: stripped to props + event wiring; all layout moved to StorageBrowser
- CloudFolderView.vue: same treatment — feeds props into StorageBrowser
- All tree sidebar components delegate expand/collapse to TreeItem.vue

Dead code removed:
- Delete HomeView.vue — no active route, replaced by FileManagerView
- Delete FolderView.vue — no active route, logic merged into FileManagerView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 16:10:47 +02:00
curo1305 2686fde2d7 feat(06.2): log attempted email on failed login and surface it in audit log
- auth.py: store attempted_email in metadata_ and link user_id when the account exists (wrong password case); previously logged no PII at all
- AuditLogTab: Email column falls back to metadata_.attempted_email in amber with "(attempted)" label when no confirmed user_email is available

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 21:02:37 +02:00
curo1305 7027347597 fix(06.2): audit log — add email column, remove @ prefix from handles
- Backend: add user_email to _build_filtered_query_with_handles (UserSubject join) and _audit_to_dict_with_handles; propagate through JSON viewer and CSV export including empty-result path
- Frontend: AuditLogTab adds Email column between User and Action Type; removes @ prefix from handle cell
- Test: update expected CSV header to include user_email

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 20:59:09 +02:00
curo1305 d771f0805d fix(06.2): shared badge and recipient handle missing in FileManagerView
- shares.py grant_share: include recipient_handle in response so ShareModal shows the name immediately without reload
- FileManagerView: add Shared pill badge next to document name (badge only existed in DocumentCard, not the main file manager view)
- FileManagerView ShareModal: wire @unshared to clear is_shared flag when last recipient is removed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 19:47:00 +02:00
curo1305 089da94d8b fix(security): apply two findings from sharing security review
- get_document: strip extracted_text for share recipients (T-04-04-03 consistency)
- ShareModal: emit 'unshared' when last recipient is revoked; DocumentCard clears is_shared badge

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 19:41:15 +02:00
curo1305 a0f6c2f663 fix(06.2): resolve four sharing UX issues found in re-test UAT
- AccountView: remove hardcoded @ prefix so handle matches what share dialog expects
- documents store: set is_shared=true optimistically after successful share so badge shows without refetch
- GET /api/documents/{id}: allow recipients of an active share to view the document (was returning 404 for non-owners)
- ShareModal: move Share button to its own full-width row so it no longer overflows the input area

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 19:32:51 +02:00
curo1305 0505beb0a4 test(phase-02): add Nyquist validation tests for plan 06 gaps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 15:17:25 +02:00
curo1305 9e8f8d5bbc fix(06.2): WR-06 use URLSearchParams in listShares for consistent encoding 2026-06-01 14:31:50 +02:00
curo1305 1cba903c34 fix(06.2): WR-01 replace fixed-suffix password generation with fully-random positional injection 2026-06-01 14:31:00 +02:00
curo1305 2542c81602 fix(06.2): WR-03 WR-04 fix pagination off-by-one and surface daily exports load errors 2026-06-01 14:27:47 +02:00
curo1305 3fa7e8b866 fix(06.2): CR-04 WR-05 audit export functions use 401-refresh-retry and safe URL.revokeObjectURL 2026-06-01 14:26:05 +02:00
curo1305 c08ea42b1b feat(02-06): Account tab in SettingsView + QR code in TotpEnrollment (GAPs 3, 5)
- frontend/package.json: add qrcode@1.5.4 to runtime dependencies
- TotpEnrollment.vue: import QRCode; generate data URL in startSetup(); render img tag
- SettingsAccountTab.vue: new component with all AccountView content (2FA, password, sessions)
- SettingsView.vue: add Account tab rendering SettingsAccountTab; import SettingsAccountTab
2026-05-31 20:40:28 +02:00
curo1305 aa957d6c50 feat(02-06): auth layout switching + admin role guard (GAPs 2, 3, 4)
- App.vue: conditionally renders AuthLayout for auth routes, app shell otherwise
- router/index.js: meta.layout='auth' on all four auth routes
- router/index.js: meta.requiresAdmin=true on /admin route
- router/index.js: beforeEach role check redirects non-admin to /
- router/index.js: /account redirects to /settings
2026-05-31 20:37:46 +02:00
curo1305 5d457d68bf feat(06.2-05): clear filters button and active filter count in AuditLogTab
- Add clearFilters() function that resets all filter fields and refetches
- Add activeFilterCount computed property (counts non-empty filter fields)
- Add "Clear filters" button (visible only when activeFilterCount > 0)
- Wrap Export CSV button with filter count indicator (amber text below button)
- Add computed to vue import
2026-05-31 20:11:02 +02:00
curo1305 f5e111bfa2 feat(06.2-05): actionable cloud error + audit log @ prefix
- CloudFolderView: detect no-connection error and show actionable message
  directing user to Settings; add router-link to /settings and Retry button
- AuditLogTab: prefix user handles with @ in the User column
2026-05-31 20:10:22 +02:00
curo1305 045e723f7a feat(06.2-05): show @handle in AccountView and AdminUsersTab
- Add Username row (@handle) to Account information section in AccountView.vue
- Add Handle column (th + td with @prefix) to users table in AdminUsersTab.vue
- Both use existing data already present in API responses (no backend changes)
2026-05-31 20:09:50 +02:00
curo1305 0647e6e9bf feat(06.2-04): frontend — user_handle filter, fetch+Blob export, daily-export section
- adminListAuditLog: rename user_id param to user_handle (backend API change)
- adminExportAuditLogCsv(): fetch+Blob pattern — sends Bearer header (D-13, T-06.2-04-03)
- adminListDailyExports(): raw fetch returning JSON for daily export listing (D-17)
- adminDownloadDailyExport(date): fetch+Blob download with audit-{date}.csv filename (D-17)
- AuditLogTab: rename filters.user_id to filters.user_handle + label 'User handle' (D-12, C-5)
- AuditLogTab: exportCsv() replaced with async fetch+Blob call, exportingCsv loading state
- AuditLogTab: daily exports section below pagination — date dropdown + Download button (D-17, C-4)
- window.location.href removed from AuditLogTab (broken auth bypass closed)
- Build exits 0, full backend suite: 337 passed, 1 pre-existing failure
2026-05-31 15:21:23 +02:00
curo1305 cce8586235 feat(06.2-03): frontend — CloudDeleteWarningModal + remove_only path in DocumentView
- api/client.js: deleteDocument gains removeOnly param; deleteDocumentRemoveOnly wrapper added
- DocumentView.vue: confirmDelete inspects response.cloud_delete_failed, shows modal on failure
- DocumentView.vue: inline CloudDeleteWarningModal (C-3 contract) with Remove from app / Cancel
- confirmRemoveOnly() calls DELETE ?remove_only=true and navigates to /

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:11:31 +02:00
curo1305 34b18a9f08 feat(06.2-02): frontend — is_shared badge fix + permission dropdown + View/Edit toggle
- DocumentCard.vue: fix Shared pill to read doc.is_shared (was doc.share_count > 0)
- ShareModal.vue: add permission select between handle input and submit button
- ShareModal.vue: replace static "view" span with View/Edit toggle group per share row
- ShareModal.vue: add handlePermissionChange with optimistic update + rollback on error
- documents.js: update shareDocument(docId, handle, permission='view') signature
- documents.js: add updateSharePermission(shareId, permission) action
- api/client.js: pass permission in createShare POST body
- api/client.js: add updateSharePermission PATCH helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:07:04 +02:00
curo1305 d98e3ab7a1 test(phase-02): add Nyquist validation tests — fill SEC-05, AUTH-08, SEC-03 and frontend gaps
8 test files, 60 new tests (14 backend + 46 frontend). All green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 12:04:21 +02:00
curo1305 10175ee4b5 fix(05-12): close 3 UAT gaps — OAuth 400 preflight, 502 cloud fallback, upload hint
- oauth_initiate: pre-flight check returns 400 with env-var hint when
  GOOGLE_CLIENT_ID/SECRET or ONEDRIVE_CLIENT_ID/SECRET are not configured,
  preventing opaque MSAL/OAuth library 500 errors on misconfigured servers
- stream_document_content: broad except-clause catches non-CloudConnectionError
  exceptions and returns 502 with user-friendly message (was raw 500)
- docker-compose.yml: add volumes: - ./backend:/app to celery-worker so code
  changes are picked up by docker compose restart without a rebuild
- CloudStorageView: upload hint paragraph directs users to navigate into a
  cloud folder; no DropZone added (no folder context at overview level)
- 3 new backend tests pass; 2 existing tests patched with credential monkeypatch;
  full suite: 293 passed, 0 new failures, 1 pre-existing (test_extract_docx)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 17:55:08 +02:00
curo1305 5250895587 feat(05): cloud folder browser views, routing, and sidebar nav
Add CloudStorageView (/cloud) and CloudFolderView (/cloud/:provider/:folderId).
Tree items filter to directories only (is_dir) to hide files in the nav tree.
CloudProviderTreeItem root click navigates to /cloud/{provider}/root instead
of /settings. AppSidebar Cloud Storage link upgraded to router-link with
active-class highlighting. Router registers both cloud routes with requiresAuth.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:58:08 +02:00
curo1305 34f012b4e8 fix(05): resolve 5 critical code review findings
CR-01: add Field(min_length=1) to UserDeleteConfirm.admin_password
CR-02: add folder ownership check in PATCH /documents/{id} — prevents IDOR
        when folder_id belongs to another user
CR-03: add min_length=1, max_length=255, and path-separator validator to
        DocumentPatch.filename — prevents empty and path-traversal filenames
CR-04: fetchDocumentContent now throws on non-ok responses instead of
        silently returning the error Response
CR-05: object URL revoke in DocumentView uses pagehide + load events with
        120s fallback instead of unreliable 60s blind timer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:51:54 +02:00
curo1305 72687212a1 feat(05-11): add adminDeleteUser API function + inline delete confirmation panel
- Export adminDeleteUser(id, adminPassword) from client.js — sends JSON body to DELETE /api/admin/users/{id}
- AdminUsersTab: add confirmDelete, deletePassword, deleteError state refs
- AdminUsersTab: add startDelete, cancelDelete, confirmDoDelete functions (mutually exclusive with deactivate panel)
- AdminUsersTab: Delete button added to active and deactivated user rows
- AdminUsersTab: inline password confirmation panel with Argon2 verification via backend
2026-05-30 11:39:10 +02:00
curo1305 87de148a59 feat(05-10): OAuth fetch + Nextcloud edit fix + Edit on ERROR + text overflow
- client.js: add initiateOAuth() and getConnectionConfig() helpers
- SettingsCloudTab: replace window.location.href with initiateOAuth() + fetch/JWT
- SettingsCloudTab: add Edit button to ACTIVE and ERROR blocks for non-OAuth providers
- SettingsCloudTab: wrap ConfirmBlock in w-full overflow-hidden div
- CloudCredentialModal: add existing prop, edit-mode pre-population via /config endpoint
- CloudCredentialModal: add showAdvanced + customEndpoint for Nextcloud custom paths
- ConfirmBlock: add break-words class to message paragraph
- cloud.py: add GET /api/cloud/connections/{id}/config endpoint (non-secret fields)
2026-05-30 11:30:13 +02:00
curo1305 4a42ccee5a feat(05-09): authenticated document preview via fetch + Blob URL
- Add fetchDocumentContent() to client.js: fetch with Bearer auth, 401 refresh
  retry pattern, returns raw Response (not parsed JSON) for blob() calls
- Replace iframe :src=proxyUrl (unauthenticated) in DocumentPreviewModal.vue
  with authenticated fetch → blob → URL.createObjectURL; loading/error states;
  URL.revokeObjectURL on unmount to prevent memory leaks
- Replace window.open(rawUrl) in DocumentView.vue openPdf() with
  fetchDocumentContent → blob → objectUrl → window.open; 60s auto-revoke
- Frontend build exits 0 with zero errors
- Closes T-05-09-04: no persistent unauthenticated content exposure
2026-05-30 11:18:01 +02:00
curo1305 98576ac298 feat(05-08): add Cloud Storage collapsible section to AppSidebar
- Import CloudProviderTreeItem and useCloudConnectionsStore
- Add cloudExpanded ref (default true) and activeCloudConnections/loadingCloudConnections computed
- Insert Cloud Storage section between Folders and Topics sections
- Fetch connections on mount; render one CloudProviderTreeItem per ACTIVE connection
- Empty state: 'No cloud storage connected'; loading state: 'Loading...'
2026-05-29 08:33:33 +02:00
curo1305 34b0593782 feat(05-08): add cloud tree components and getCloudFolders API function
- Add getCloudFolders(provider, folderId) to api/client.js (GET /api/cloud/folders/{provider}/{folderId})
- Create CloudProviderTreeItem.vue: lazy-load folder tree per connection, providerIconColor computed, expand/collapse arrow, loading/error states
- Create CloudFolderTreeItem.vue: recursive folder tree node with is_dir expand arrow, lazy-load children, depth padding
2026-05-29 08:32:19 +02:00
curo1305 63a68296a5 feat(05-07): 3-tab SettingsView, SettingsCloudTab, CloudCredentialModal
- Convert SettingsView to 3-tab layout (Preferences/AI/Cloud) matching AdminView pattern
- Extract SettingsPreferencesTab.vue and SettingsAiTab.vue from original SettingsView
- Create SettingsCloudTab.vue with all 4 providers, status badges, action buttons
- Create CloudCredentialModal.vue for WebDAV/Nextcloud credential input
- Handle OAuth callback query params (cloud_connected/cloud_error) in SettingsView.onMounted
- Add success toast (auto-dismiss 5s) and persistent error banner for OAuth results
- Fix pre-existing build failure: add build.target=esnext to vite.config.js for top-level await support
- 2 SettingsCloudTab mount tests passing (W4 — CLAUDE.md)
2026-05-29 08:12:36 +02:00
curo1305 612d542c06 feat(05-07): cloud connections Pinia store + API client functions
- Create useCloudConnectionsStore with connections/loading/error refs
- fetchConnections, disconnect(id), disconnectAll() actions
- Append listCloudConnections, disconnectCloud, connectWebDav, updateDefaultStorage to api/client.js
- Add vitest test script to package.json
- 4 unit tests passing (W4 — CLAUDE.md)
2026-05-29 08:05:59 +02:00
curo1305 d6f742a3c1 chore(phase-4): UAT complete — Phase 4 marked done, sidebar collapse, duplicate-folder fix
UAT: 14/15 passed. Bug fixed: folders/rootFolders array alias in fetchFolders caused
duplicate folder row on creation (rootFolders = [...list] breaks the shared reference).
Sidebar: Folders section now has a collapse/expand chevron, collapsed by default.
State: Phase 4 complete, Phase 5 (Cloud Storage Backends) is next.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:34:07 +02:00
curo1305 87a32b7ee8 feat(phase-4): complete UX redesign — FileManagerView, FolderTreeItem, test suite, and all Phase 4 fixes
Adds the unified file manager view (Windows Explorer-style), collapsible
folder tree sidebar item, full vitest test suite (55 tests, 4 files), and
commits all Phase 4 backend/frontend fixes that were staged but uncommitted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:10:52 +02:00
curo1305 81da251669 fix(phase-4): add Move to folder dropdown on DocumentCard hover 2026-05-25 23:17:35 +02:00
curo1305 31f8c00970 fix(phase-4): request() skip res.json() for 204 No Content — fixes delete not updating UI 2026-05-25 22:34:54 +02:00
curo1305 a7c6c9612b fix(phase-4): folders store unwrap .items from list response (push is not a function) 2026-05-25 22:31:41 +02:00
curo1305 a3f5fc2e69 feat(phase-4-09): wire components into views — sidebar, cards, home, folder, shared, settings, admin
- AppSidebar: add 'Shared with me' entry (purple icon, count badge) and Folders section with New folder CTA
- DocumentCard: add group class, hover-reveal share button, ShareModal v-if, shared indicator pill
- HomeView: add SearchBar + SortControls above document list; fetchFolders on mount
- FolderView: new view with FolderBreadcrumb, FolderRow list, inline new-subfolder input, document list
- SharedView: new view fetching /api/shares/received with owner_handle display and empty state
- DocumentView: add PDF preview logic (in_app=DocumentPreviewModal, new_tab=window.open); load preferences on mount
- SettingsView: add Document Preferences card with pdf_open_mode radio buttons, auto-save on change
- AdminView: add Audit Log tab alongside Users/Quotas/AI Config tabs
2026-05-25 22:14:12 +02:00
curo1305 36721575a5 feat(phase-4-09): create new components — FolderRow, FolderBreadcrumb, FolderDeleteModal, ShareModal, DocumentPreviewModal, SearchBar, SortControls, AuditLogTab
- FolderRow: inline rename, three-dot menu, delete/rename callbacks, outside-click close
- FolderBreadcrumb: truncation at depth > 4, nav aria-label, ol structure
- FolderDeleteModal: role=dialog, warning icon, doc count in body, Keep/Delete buttons
- ShareModal: handle input, recipients list with revoke, 404/409 error handling
- DocumentPreviewModal: iframe with proxy URL only (never presigned), Escape/overlay close
- SearchBar: role=search, aria-label, Escape clears
- SortControls: aria-pressed, direction indicator, toggle vs switch logic
- AuditLogTab: filters, paginated table, CSV export via window.location.href
- api/client.js: add adminListAuditLog function
2026-05-25 22:10:23 +02:00
curo1305 5417f26b93 feat(phase-4): frontend data layer — API client (13 new functions), folders store, documents store extensions, routes
- Extended listDocuments to accept folderId, q, sort, order query params
- Added 6 folder API functions: listFolders, createFolder, getFolder, renameFolder, deleteFolder, moveDocument
- Added 4 share API functions: createShare, listShares, deleteShare, getSharedWithMe
- Added 2 preference API functions: getMyPreferences, updateMyPreferences
- Added getDocumentContentUrl helper (returns URL string, no fetch)
- Created useFoldersStore with full CRUD, navigation state, and breadcrumb support
- Extended useDocumentsStore with currentFolderId, searchQuery, sortField, sortOrder refs
- Added debounced searchQuery watcher (300ms, 2-char minimum, T-04-08-03)
- Added shareDocument, revokeShare, listShares actions to documents store
- Added /folders/:folderId and /shared routes with requiresAuth guard
2026-05-25 21:58:38 +02:00
curo1305 a5f202b069 Fix Phase 3 UAT blockers: MinIO presigned URL hostname, CORS, admin flush→commit, auth refresh race
Bugs fixed:
- minio_backend.py: generate_presigned_put_url and presigned_get_url used internal
  _client (minio:9000) instead of _public_client (localhost:9000). Browser received
  ERR_NAME_NOT_RESOLVED. Fixed by using _public_client with region='us-east-1' to
  skip region-discovery HTTP request from inside the container.

- docker-compose.yml: MINIO_API_CORS_ALLOW_ORIGIN was set from CORS_ORIGINS which
  uses pydantic JSON list format '["http://localhost:5173"]'. MinIO expected a plain
  string and never matched the origin. Fixed to use FRONTEND_URL instead.

- admin.py: All write handlers (create_user, update_user_status, update_user_quota,
  update_ai_config) used session.flush() without session.commit(). Changes appeared
  to succeed (response reflected in-memory state) but rolled back on session close.
  Fixed by replacing flush() with commit() in all four write handlers.

- auth.js: Concurrent refresh() calls from QuotaBar and App.vue on page reload caused
  a token rotation race — first call rotated the cookie, second arrived with stale
  cookie and cleared accessToken. Fixed by deduplicating with a shared in-flight
  promise (_refreshInFlight).

Phase 3 UAT: 9/10 pass. UAT-3 (QuotaBar visual) pending browser confirmation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:30:41 +02:00
curo1305 b5dde2aad9 wip: Phase 3 UAT in progress — 2/10 tests pass, upload XHR bug open
Fixes applied this session:
- frontend/src/api/client.js: noRefreshPaths exclusion prevents auth 401s
  from triggering session-expired error on login/register/refresh
- frontend/src/router/index.js: async beforeEach with silent refresh()
  restores session from httpOnly cookie on page reload

UAT state: 2 pass (cold-start, admin block), 1 open (XHR upload network error),
7 pending. MinIO PUT fails in browser — needs console output to diagnose.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 21:26:50 +02:00
curo1305 23c568ae89 feat(03-05): create QuotaBar.vue; embed in AppSidebar between topics nav and footer
- QuotaBar.vue: new sidebar quota widget — onMounted calls authStore.fetchQuota(); computed pct/barColor/labelColor/label from authStore.quota; color thresholds <80% indigo-500, 80-95% amber-500, >=95% red-500; skeleton loading state (animate-pulse); hides on loadFailed (v-if); role=progressbar with aria-valuenow/aria-valuemin/aria-valuemax/aria-label
- AppSidebar.vue: import QuotaBar; insert <QuotaBar /> between </nav> and settings footer div
- Frontend build exits 0 (verified)
2026-05-23 20:49:07 +02:00
curo1305 eb18428d07 feat(03-05): 3-step presigned upload + quota state in auth store + progress UI
- api/client.js: extend request() to attach .status and .payload on 413 structured errors; remove legacy uploadDocument multipart function
- stores/auth.js: add quota ref({used_bytes:0, limit_bytes:0}) and fetchQuota() action (silent catch); expose in store return
- stores/documents.js: replace single upload() with uploadToMinIO XHR helper + 3-step async action (getUploadUrl→XHR PUT→confirmUpload); track uploadProgress map keyed by filename+timestamp (T-03-25); call fetchQuota after upload success and document delete
- components/upload/UploadProgress.vue: add aria progressbar per row, percentage label, quota rejection error block (role=alert, red-50/red-200) from item.quotaError; use plain anchor for Manage storage link
2026-05-23 20:46:24 +02:00
curo1305 349912cac3 feat(03-04): replace settings UI with admin-managed placeholder; update API client
- views/SettingsView.vue: Replace full form with static placeholder card. No store
  imports, no API calls. Shows "AI configuration is managed by your administrator."
  (D-12, T-03-21)
- stores/settings.js: Deleted — only consumed by SettingsView; no other imports
- api/client.js: Remove getSettings, patchSettings, testProvider, getDefaultPrompt
  (// Settings section deleted). Add getMyQuota() for quota bar (Plan 03-05).
  Add getUploadUrl() and confirmUpload() for presigned upload flow (Plan 03-05).
2026-05-23 20:34:15 +02:00
curo1305 92e3d755d0 feat(02-05): AppSidebar admin link and user identity footer
- Add conditional Admin nav link (v-if authStore.user?.role === 'admin') with shield SVG icon
- Add user identity footer: initials avatar (bg-indigo-100), email (truncate flex-1), sign-out icon button (aria-label="Sign out")
- Import useAuthStore alongside existing topicsStore; add useRouter for post-logout redirect
- All existing nav links, topicsStore reference, and scoped styles preserved unchanged
2026-05-22 20:09:16 +02:00
curo1305 9137f41537 feat(02-05): admin tab components and AdminView
- AdminView.vue: tabbed layout (Users | Quotas | AI Config) with UI-SPEC tab strip classes
- AdminUsersTab.vue: user table with create form (crypto.getRandomValues password), inline deactivation confirmation, reactivate, reset-password, row-level spinner, empty state
- AdminQuotasTab.vue: quota inline edit with MB display, usage %, warning when limit < usage
- AdminAiConfigTab.vue: AI provider/model per-user with 1.5s "Saved" confirmation
- client.js: fix adminDeactivateUser/adminReactivateUser to use PATCH /status endpoint, fix adminResetUserPassword to /password-reset, fix adminUpdateAiConfig to send ai_provider/ai_model, add adminGetUserQuota
- No impersonation UI in any admin component (T-02-31)
2026-05-22 20:09:05 +02:00
curo1305 d73e2f6112 feat(02-03): TOTP enrollment flow, backup codes, AccountView, ConfirmBlock
- TotpEnrollment.vue: three-step enrollment (setup → verify → backup-codes); emits 'enrolled'
- BackupCodesDisplay.vue: 2-column grid, copy-all clipboard, acknowledgment checkbox
- ConfirmBlock.vue: reusable inline confirmation block with 'confirmed'/'cancelled' emits
- AccountView.vue: TOTP section (enrollment or disable), change-password with breach/wrong-pw error handling, sign-out-all with ConfirmBlock
- npm run build exits 0
2026-05-22 19:54:53 +02:00
curo1305 3b7d362600 feat(02-02): frontend auth store, router guard, Login/Register views
- frontend/src/stores/auth.js: useAuthStore with accessToken in memory
  only (never browser storage); login() accepts options.backupCode
- frontend/src/api/client.js: extended with Bearer token injection,
  401 auto-refresh retry, all auth/admin API functions, changePassword
- frontend/src/router/index.js: auth routes added (/login, /register,
  /password-reset, /account, /admin); beforeEach guard redirects
  unauthenticated users to /login with redirect param
- frontend/src/layouts/AuthLayout.vue: centered bare layout for auth pages
- frontend/src/views/auth/LoginView.vue: three-step flow (password, TOTP,
  backup code); "Use a backup code instead" link; UI-SPEC copywriting
- frontend/src/views/auth/RegisterView.vue: registration with
  PasswordStrengthBar; HIBP error display; UI-SPEC copywriting
- frontend/src/components/auth/PasswordStrengthBar.vue: 4-segment bar
- frontend/src/components/ui/AppSpinner.vue: animate-spin SVG spinner
- Stub views: PasswordResetView, NewPasswordView, AccountView, AdminView
- .gitignore: exclude frontend/node_modules, dist, package-lock.json

npm run build exits 0. All acceptance criteria verified.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 19:45:21 +02:00
curo1305 7a34807fa0 chore: initial commit — existing single-user document scanner codebase
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 08:53:28 +02:00