Files
kite/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-05-PLAN.md
T
curo1305 eaa3399ec0 docs: add shared module map to CLAUDE.md, SECURITY.md, planning artifacts
- CLAUDE.md: add Code Standards section with backend and frontend shared
  module maps, component architecture rules, duplication checklist, and
  no-dead-code enforcement rule
- SECURITY.md: Phase 02 + 03 security audit results (all threats CLOSED)
- .planning: update milestone audit, config, and add plan/UAT files for
  phases 01, 02-06, and 06.2-05

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 16:10:59 +02:00

19 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, gap_closure, requirements, must_haves
phase plan type wave depends_on files_modified autonomous gap_closure requirements must_haves
06.2 05 execute 3
06.2-04
frontend/src/views/AccountView.vue
frontend/src/components/admin/AdminUsersTab.vue
frontend/src/views/CloudFolderView.vue
frontend/src/components/admin/AuditLogTab.vue
true true
SHARE-03
ADMIN-06
truths artifacts key_links
User can see their own @handle in Account settings — enabling them to share their handle with others for document sharing
Admin can see each user's handle in the Users tab — enabling handle lookup for support and sharing
Cloud folder browser shows an actionable error when no cloud connection exists — directing user to Settings
Audit log entries display @alice style handles (@ prefix present)
Export CSV button shows active filter count when filters are set — user understands export scope before clicking
Clear filters button in Audit Log tab resets all filters and re-fetches unfiltered data
path provides contains
frontend/src/views/AccountView.vue Handle row in Account information section authStore.user?.handle
path provides contains
frontend/src/components/admin/AdminUsersTab.vue Handle column in users table user.handle
path provides contains
frontend/src/views/CloudFolderView.vue Actionable no-connection error message Settings
path provides contains
frontend/src/components/admin/AuditLogTab.vue @ prefix on handles, Clear filters button, active filter count indicator clearFilters
from to via pattern
AccountView.vue authStore.user authStore.user?.handle (already present in /api/auth/me response) authStore.user?.handle
from to via pattern
AdminUsersTab.vue user row adminListUsers() response user.handle (backend returns handle in GET /api/admin/users) user.handle
from to via pattern
AuditLogTab.vue entry.user_handle rendered cell template expression with @ prefix '@' + entry.user_handle
Close the four UAT-diagnosed gaps from 06.2-UAT.md: (1) user handle invisible in account settings and admin user list, (2) cloud folder browser shows unhelpful error when no connection exists, (3) audit log handle entries missing @ prefix, (4) CSV export gives no indication of active filters and no way to clear them.

Purpose: These gaps block real usage — users cannot share documents because handles are invisible, cloud storage is unusable with no diagnostic guidance, audit logs look wrong without @ prefixes, and CSV exports silently export filtered (possibly empty) data.

Output: Four targeted frontend changes across four files. No backend changes required.

<execution_context> @/Users/nik/Documents/Progamming/document_scanner/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-UAT.md </execution_context>

@/Users/nik/Documents/Progamming/document_scanner/.planning/ROADMAP.md @/Users/nik/Documents/Progamming/document_scanner/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-CONTEXT.md

From frontend/src/views/AccountView.vue (Account information section, lines 8-24):

Account information

Email: {{ authStore.user?.email }}
Role: {{ authStore.user?.role }}

From frontend/src/components/admin/AdminUsersTab.vue (table head, lines 113-119):

Email Role Status Created Actions

From frontend/src/views/CloudFolderView.vue (load function, lines 126-137): async function load() { loading.value = true error.value = '' try { const data = await api.getCloudFolders(provider.value, folderId.value ?? 'root') items.value = data.items ?? [] } catch (e) { error.value = e.message || 'Failed to load folder contents' } finally { loading.value = false } }

{{ error }} Retry

From frontend/src/components/admin/AuditLogTab.vue (relevant section):

{{ entry.user_handle || entry.user_id || '—' }}

const filters = reactive({ start: '', end: '', user_handle: '', event_type: '', })

Task 1: Show handle in AccountView and AdminUsersTab frontend/src/views/AccountView.vue, frontend/src/components/admin/AdminUsersTab.vue CHANGE 1 — frontend/src/views/AccountView.vue

In the "Account information" section (lines 8-24), add a Username row immediately after the Email row and before the Role row. The new row follows the same pattern as the Email row:

Username: @{{ authStore.user?.handle }}

Place this line between the email div and the role div. The @ is a literal character prepended to the handle value so users immediately recognise it as their sharing handle. No script changes needed — authStore.user?.handle is already available.

CHANGE 2 — frontend/src/components/admin/AdminUsersTab.vue

Add a "Handle" column to the users table so admins can look up other users' handles.

In the <thead> row (after the Email th and before the Role th), add:

Handle

In the <tbody> rows (after the email <td> and before the role <td>), add:

{{ user.handle ? '@' + user.handle : '—' }}

No script changes needed — adminListUsers() already returns handle in the user object (confirmed in backend/api/admin.py line 63). grep -n "authStore.user?.handle" /Users/nik/Documents/Progamming/document_scanner/frontend/src/views/AccountView.vue && grep -n "user.handle" /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/admin/AdminUsersTab.vue | grep -v "handle:" - grep "authStore.user?.handle" frontend/src/views/AccountView.vue returns a match in the template section - grep "user\.handle" frontend/src/components/admin/AdminUsersTab.vue returns a match in both thead and tbody - cd frontend && npm run build 2>&1 | grep -i "error" | grep -v "^>" returns no output

Task 2: Actionable cloud connection error and audit log @ prefix frontend/src/views/CloudFolderView.vue, frontend/src/components/admin/AuditLogTab.vue CHANGE 1 — frontend/src/views/CloudFolderView.vue

Replace the generic error handler in the load() function with one that distinguishes "no connection" from a general error. The backend returns a response whose error detail contains "No active connection" (or HTTP 404) when no cloud provider is connected.

Replace the catch block in load():

} catch (e) { const msg = e.message || '' if (msg.toLowerCase().includes('no active connection') || msg.includes('404') || msg.toLowerCase().includes('not found')) { error.value = 'No cloud provider connected. Go to Settings to connect a cloud storage account.' } else { error.value = msg || 'Failed to load folder contents.' } }

Also update the error template block (lines 36-39) to add a Settings link. Replace the existing error div with:

{{ error }}

Go to Settings Retry

Confirm router-link is usable here — useRouter and useRoute are already imported from 'vue-router' in the script setup.

CHANGE 2 — frontend/src/components/admin/AuditLogTab.vue

Change the user handle cell (line 95) from: {{ entry.user_handle || entry.user_id || '—' }} to: {{ entry.user_handle ? '@' + entry.user_handle : (entry.user_id || '—') }}

This is a template-only one-liner change. No script changes required. grep -n "No cloud provider connected" /Users/nik/Documents/Progamming/document_scanner/frontend/src/views/CloudFolderView.vue && grep -n "'@' + entry.user_handle" /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/admin/AuditLogTab.vue - grep "No cloud provider connected" frontend/src/views/CloudFolderView.vue returns a match - grep "Go to Settings" frontend/src/views/CloudFolderView.vue returns a match - grep "'@' + entry.user_handle" frontend/src/components/admin/AuditLogTab.vue returns a match - grep "entry.user_handle || entry.user_id" frontend/src/components/admin/AuditLogTab.vue returns NO match (old pattern gone) - cd frontend && npm run build 2>&1 | grep -i "error" | grep -v "^>" returns no output

Task 3: Clear filters button and active filter count indicator in AuditLogTab frontend/src/components/admin/AuditLogTab.vue Add two UX improvements to AuditLogTab.vue to make the CSV export scope transparent.

CHANGE 1 — Add clearFilters() function in the script setup section:

Add the following function after the existing applyFilters() function:

function clearFilters() { filters.start = '' filters.end = '' filters.user_handle = '' filters.event_type = '' page.value = 1 fetchLog() }

Also add a computed property (or inline expression) for active filter count. Add this computed after the clearFilters() function:

import { computed } from 'vue' // add computed to the existing vue import if not present

const activeFilterCount = computed(() => { let count = 0 if (filters.start) count++ if (filters.end) count++ if (filters.user_handle) count++ if (filters.event_type) count++ return count })

NOTE: computed must be added to the existing import { ref, reactive, onMounted } line at the top of the script. Change it to import { ref, reactive, onMounted, computed }.

CHANGE 2 — Add "Clear filters" button to filter bar in the template:

In the filter bar (the <div class="flex flex-wrap gap-3 mb-4 items-end"> block), add a "Clear filters" button immediately after the existing "Apply filters" button. Only show it when filters are active:

<button v-if="activeFilterCount > 0" @click="clearFilters" class="border border-gray-300 text-gray-500 text-sm px-4 py-2 rounded-lg hover:bg-gray-50 transition-colors"

Clear filters

CHANGE 3 — Add active filter count indicator near Export CSV button:

Wrap the existing Export CSV button in a relative container and add a badge showing the active filter count when non-zero. Replace the standalone Export CSV button block with:

Exporting… Export CSV {{ activeFilterCount }} filter{{ activeFilterCount !== 1 ? 's' : '' }} active

The amber text "N filter(s) active" sits directly below the Export CSV button so users see at a glance that the download will be scoped. The existing <p v-if="exportError"> block remains unchanged immediately after this new wrapper div. grep -n "clearFilters|activeFilterCount|Clear filters|filters active" /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/admin/AuditLogTab.vue | head -15 - grep "clearFilters" frontend/src/components/admin/AuditLogTab.vue returns at least 2 matches (definition + @click binding) - grep "activeFilterCount" frontend/src/components/admin/AuditLogTab.vue returns at least 3 matches (computed definition + v-if + template text) - grep "Clear filters" frontend/src/components/admin/AuditLogTab.vue returns a match in the template - grep "filters active" frontend/src/components/admin/AuditLogTab.vue returns a match - grep "computed" frontend/src/components/admin/AuditLogTab.vue returns a match in the import line - cd frontend && npm run build 2>&1 | grep -i "error" | grep -v "^>" returns no output

<threat_model>

Trust Boundaries

Boundary Description
AccountView → authStore.user handle is read from in-memory Pinia store — never from localStorage; no user-supplied input involved
CloudFolderView → error message error text originates from backend API response; rendered via Vue template auto-escaping (no innerHTML) — XSS risk mitigated
AuditLogTab → entry.user_handle handle value from API response rendered via Vue template auto-escaping — no innerHTML

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-06.2-05-01 Information Disclosure Handle visible in AccountView accept Handle is already a public-within-platform identifier (used as share target); displaying it to the owning user is expected and correct
T-06.2-05-02 Information Disclosure Handle visible in AdminUsersTab accept Admin already has access to email; handle is lower-sensitivity than email; admin-only endpoint already enforces get_current_admin
T-06.2-05-03 XSS Cloud error message rendered from API response mitigate Vue template auto-escaping prevents XSS; the error string is interpolated via {{ }} not v-html — no raw HTML injection possible
T-06.2-05-04 XSS @ + entry.user_handle rendered in table mitigate String concatenation in Vue template expression is auto-escaped — not v-html
T-06.2-05-SC Tampering npm/pip/cargo installs accept No new packages installed in this plan — frontend-only template and script changes only
</threat_model>
After all three tasks complete:

Build check (no errors):

cd /Users/nik/Documents/Progamming/document_scanner/frontend && npm run build 2>&1 | grep -i "error" | grep -v "^>" | head -10

Expected: no output.

Gap 1 — Handle in AccountView:

grep -n "authStore.user?.handle" /Users/nik/Documents/Progamming/document_scanner/frontend/src/views/AccountView.vue

Expected: match in template section.

Gap 1 — Handle in AdminUsersTab:

grep -n "user\.handle" /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/admin/AdminUsersTab.vue

Expected: at least 2 matches (thead + tbody).

Gap 2 — Cloud actionable error:

grep -n "No cloud provider connected\|Go to Settings" /Users/nik/Documents/Progamming/document_scanner/frontend/src/views/CloudFolderView.vue

Expected: 2 matches.

Gap 3 — Audit log @ prefix:

grep -n "'@' + entry.user_handle" /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/admin/AuditLogTab.vue

Expected: 1 match.

Gap 4 — Clear filters + filter count:

grep -c "clearFilters\|activeFilterCount" /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/admin/AuditLogTab.vue

Expected: 5 or more matches total.

Backend test suite unaffected (no backend changes):

cd /Users/nik/Documents/Progamming/document_scanner/backend && pytest -x -q 2>&1 | tail -5

Expected: exits 0, same pass count as before this plan.

<success_criteria>

  • Account settings page shows the user's own @handle in the Account information section
  • Admin Users tab includes a Handle column showing @handle for every user row
  • Cloud folder browser shows "No cloud provider connected. Go to Settings to connect a cloud storage account." (with a Settings link) when backend returns a no-connection error
  • Audit log table renders @alice style handles (@ prefix present on all non-null handles)
  • AuditLogTab has a "Clear filters" button (visible only when at least one filter is active) that resets all filters and re-fetches
  • Export CSV button area shows "N filter(s) active" in amber text when one or more filters are set
  • npm run build exits 0 with no errors
  • Backend pytest suite still passes (no regressions — this plan touches only frontend files) </success_criteria>
Create `.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-05-SUMMARY.md` when done.