Files
kite/.planning/phases/04-folders-sharing-quotas-document-ux/04-08-PLAN.md
T
curo1305 747303246a docs(04): create phase 4 plan (9 plans, 7 waves)
Folders, Sharing, Quotas & Document UX — plans verified (0 blockers,
2 non-blocking warnings). Covers FOLD-01..05, SHARE-01..05, SEC-08/09,
ADMIN-06, DOC-01/02.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 18:20:16 +02:00

14 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
04-folders-sharing-quotas-document-ux 08 execute 6
04-07
frontend/src/api/client.js
frontend/src/stores/folders.js
frontend/src/stores/documents.js
frontend/src/router/index.js
true
FOLD-01
FOLD-02
FOLD-03
FOLD-04
FOLD-05
SHARE-01
SHARE-02
SHARE-03
SHARE-04
SHARE-05
DOC-01
DOC-02
truths artifacts key_links
All backend API endpoints have matching client.js wrapper functions
useFoldersStore exposes state and actions for folder CRUD and navigation
useDocumentsStore extended with search, sort, folderId, shareDocument, revokeShare, listShares
Vue Router has routes for /folders/:folderId and /shared
Access token injected via existing request() helper — no new auth logic added
path provides
frontend/src/api/client.js listFolders, createFolder, renameFolder, deleteFolder, moveDocument, createShare, deleteShare, listShares, getSharedWithMe, getMyPreferences, updateMyPreferences, getDocumentContentUrl
path provides
frontend/src/stores/folders.js useFoldersStore: folders, currentFolderId, breadcrumb, loading, error + folder CRUD actions
path provides
frontend/src/stores/documents.js extended with folderId, searchQuery, sort, order state + share actions
path provides
frontend/src/router/index.js /folders/:folderId route + /shared route
from to via pattern
frontend/src/stores/folders.js frontend/src/api/client.js all folder CRUD actions call api.* functions import.*api
from to via pattern
frontend/src/router/index.js frontend/src/views/FolderView.vue route /folders/:folderId component lazy-loaded FolderView
Wire the frontend data layer: API client functions, Pinia stores, and router routes for all Phase 4 backend endpoints. This plan creates no UI components — those are in plan 04-09. Stores and API client are the contracts the UI components depend on.

Purpose: Establish frontend data contracts before writing view and component code. Output: client.js additions + folders store + extended documents store + new routes.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/phases/04-folders-sharing-quotas-document-ux/04-CONTEXT.md @.planning/phases/04-folders-sharing-quotas-document-ux/04-PATTERNS.md @.planning/phases/04-folders-sharing-quotas-document-ux/04-UI-SPEC.md @frontend/src/api/client.js @frontend/src/stores/documents.js @frontend/src/stores/topics.js @frontend/src/router/index.js From frontend/src/api/client.js: read actual file to confirm request() signature. Pattern: all exported functions call the internal request() helper which injects the access token from the auth store and handles 401 refresh.

From frontend/src/stores/topics.js: full defineStore pattern to replicate. State refs: typed refs, loading, error. Actions: async try/catch/finally pattern.

From frontend/src/router/index.js: read to find the route array and lazy-import pattern for view components (e.g., () => import('../views/SomeView.vue')).

Task 1: Add Phase 4 API functions to client.js and create folders store frontend/src/api/client.js, frontend/src/stores/folders.js frontend/src/api/client.js — read the ENTIRE file; identify the request() helper signature; find the last exported function; note if any listFolders or share functions already exist frontend/src/stores/topics.js — read ENTIRE file for the exact defineStore + ref + async action pattern frontend/src/stores/documents.js — read lines 1-30 to confirm existing imports Modify frontend/src/api/client.js: APPEND these named export functions after existing functions. Follow the existing named-export style exactly.
Folder API: listFolders(parentId = null) → GET /api/folders with optional ?parent_id= query param; createFolder(name, parentId = null) → POST /api/folders with JSON body {name, parent_id: parentId || null}; getFolder(folderId) → GET /api/folders/{folderId}; renameFolder(folderId, name) → PATCH /api/folders/{folderId} with JSON {name}; deleteFolder(folderId) → DELETE /api/folders/{folderId}; moveDocument(docId, folderId) → PATCH /api/documents/{docId}/folder with JSON {folder_id: folderId || null}.

Share API: createShare(docId, recipientHandle) → POST /api/shares with JSON {document_id: docId, recipient_handle: recipientHandle}; listShares(docId) → GET /api/shares?document_id={docId}; deleteShare(shareId) → DELETE /api/shares/{shareId}; getSharedWithMe() → GET /api/shares/received.

Preference API: getMyPreferences() → GET /api/auth/me/preferences; updateMyPreferences(payload) → PATCH /api/auth/me/preferences with JSON payload.

PDF proxy URL helper: getDocumentContentUrl(docId) → returns the string `/api/documents/${docId}/content` — pure string, no fetch. Used for iframe src or window.open.

For listDocuments (the existing function): read its current signature. If it does not already accept folderId, q, sort, order params (added in plan 04-03), update it to pass these as query params via URLSearchParams, including params only when non-null and non-empty.

Create frontend/src/stores/folders.js as a NEW file. Follow the exact useTopicsStore pattern.
Imports: `import { defineStore } from 'pinia'`, `import { ref } from 'vue'`, `import * as api from '../api/client.js'`.
State refs: folders (ref([])), currentFolderId (ref(null)), breadcrumb (ref([])), loading (ref(false)), error (ref(null)).
Actions:
- fetchFolders(parentId = null): calls api.listFolders(parentId); sets folders.value
- createFolder(name, parentId = null): calls api.createFolder(name, parentId); pushes result to folders.value
- renameFolder(folderId, name): calls api.renameFolder; updates matching entry in folders.value
- deleteFolder(folderId): calls api.deleteFolder; removes from folders.value
- navigateTo(folderId): sets currentFolderId.value = folderId; if folderId is not null, calls api.getFolder(folderId) and sets breadcrumb.value = response.breadcrumb; if folderId is null, sets breadcrumb.value = []
All actions follow the loading/error/finally pattern from topics store.
Export: `export const useFoldersStore = defineStore('folders', () => { ... return { folders, currentFolderId, breadcrumb, loading, error, fetchFolders, createFolder, renameFolder, deleteFolder, navigateTo } })`.
Run: grep -n "listFolders\|createShare\|getMyPreferences\|getDocumentContentUrl\|getSharedWithMe" /Users/nik/Documents/Progamming/document_scanner/frontend/src/api/client.js
Expected: all five function names appear in the grep output.

Also: grep -n "useFoldersStore\|navigateTo\|currentFolderId" /Users/nik/Documents/Progamming/document_scanner/frontend/src/stores/folders.js

Expected: all three identifiers appear.
- frontend/src/api/client.js contains: listFolders, createFolder, getFolder, renameFolder, deleteFolder, moveDocument, createShare, listShares, deleteShare, getSharedWithMe, getMyPreferences, updateMyPreferences, getDocumentContentUrl (13 new functions — grep confirms each name) - frontend/src/stores/folders.js exists and exports useFoldersStore - useFoldersStore returns: folders, currentFolderId, breadcrumb, loading, error, fetchFolders, createFolder, renameFolder, deleteFolder, navigateTo - listDocuments in client.js accepts folderId, q, sort, order params - No existing API functions are modified (only appended to) client.js has all Phase 4 API wrappers; folders store is created and exported. Task 2: Extend documents store with folder/search/share actions + add Vue Router routes frontend/src/stores/documents.js, frontend/src/router/index.js frontend/src/stores/documents.js — read the ENTIRE file; identify fetchDocuments signature; find existing actions (upload, remove, etc.); find the return statement at the end to know what to add to it frontend/src/router/index.js — read the ENTIRE file; identify the routes array; find the lazy import pattern; note auth guard usage (requiresAuth meta, beforeEach hook) to replicate for new routes Modify frontend/src/stores/documents.js:
Add new refs (insert after existing state refs, before existing actions):
- currentFolderId: ref(null)
- searchQuery: ref('')
- sortField: ref('date')  — 'name' | 'date' | 'size'
- sortOrder: ref('desc')  — 'asc' | 'desc'

Add debounced search watcher (RESEARCH.md Pattern 10 — no lodash):
After existing watch declarations (or before return), add:
let _searchTimer = null
watch(searchQuery, (newVal) => {
  clearTimeout(_searchTimer)
  if (newVal.length < 2) { fetchDocuments({ folderId: currentFolderId.value }); return }
  _searchTimer = setTimeout(() => { fetchDocuments({ q: newVal, folderId: currentFolderId.value, sort: sortField.value, order: sortOrder.value }) }, 300)
})

Update fetchDocuments() to accept and pass {folderId, q, sort, order} params to api.listDocuments().

Add new actions (append before return statement):
- shareDocument(docId, recipientHandle): try { return await api.createShare(docId, recipientHandle) } catch (e) { throw e }
- revokeShare(shareId): try { await api.deleteShare(shareId) } catch (e) { throw e }
- listShares(docId): try { return await api.listShares(docId) } catch (e) { throw e }

Add new state/actions to the return statement: currentFolderId, searchQuery, sortField, sortOrder, shareDocument, revokeShare, listShares.

Modify frontend/src/router/index.js:
Add two new route objects to the routes array. Follow the exact lazy-import pattern from existing routes. Apply the same requiresAuth guard as existing protected routes.

Route 1: { path: '/folders/:folderId', name: 'folder', component: () => import('../views/FolderView.vue'), meta: { requiresAuth: true } }
Route 2: { path: '/shared', name: 'shared', component: () => import('../views/SharedView.vue'), meta: { requiresAuth: true } }

Note: FolderView.vue and SharedView.vue do not exist yet (created in plan 04-09). The router import is lazy-loaded so missing files do not cause import errors at router init — only at navigation time.
Run: grep -n "currentFolderId\|searchQuery\|sortField\|shareDocument\|revokeShare" /Users/nik/Documents/Progamming/document_scanner/frontend/src/stores/documents.js
Expected: all five identifiers appear.

Also: grep -n "FolderView\|SharedView\|/folders\|/shared" /Users/nik/Documents/Progamming/document_scanner/frontend/src/router/index.js

Expected: both view names and both paths appear.
- documents.js contains: currentFolderId ref, searchQuery ref, sortField ref, sortOrder ref - documents.js contains debounced watch on searchQuery with 300ms timeout and 2-char minimum - documents.js return statement includes: currentFolderId, searchQuery, sortField, sortOrder, shareDocument, revokeShare, listShares - router/index.js contains /folders/:folderId route and /shared route - Both new routes use lazy component import and requiresAuth meta (grep confirms) - No existing routes modified (only new routes appended) Documents store extended with folder/search/share state; router has two new protected routes.

<threat_model>

Trust Boundaries

Boundary Description
Frontend store → backend API All requests go through request() helper which injects JWT; no auth logic duplicated in new stores
Vue Router → view components New routes use same requiresAuth guard as existing protected routes

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-04-08-01 Broken Access Control /folders/:folderId route without auth guard mitigate requiresAuth: true meta applied to both new routes; same router beforeEach guard as existing protected routes
T-04-08-02 Information Disclosure Access token stored in localStorage via new store accept All token handling is in existing auth store + request() helper; folders store and documents store extension do not touch auth state
T-04-08-03 Tampering Debounced search fires before 2-char minimum mitigate searchQuery watch checks newVal.length < 2 before firing API call; short inputs restore full list without API call
T-04-SC Tampering npm/pip/cargo installs accept No new packages installed in this plan
</threat_model>
1. API functions: grep -c "export function" /Users/nik/Documents/Progamming/document_scanner/frontend/src/api/client.js 2. Folders store: grep -n "useFoldersStore\|navigateTo\|breadcrumb" /Users/nik/Documents/Progamming/document_scanner/frontend/src/stores/folders.js 3. Documents store: grep -n "currentFolderId\|shareDocument\|searchQuery" /Users/nik/Documents/Progamming/document_scanner/frontend/src/stores/documents.js 4. Router: grep -n "FolderView\|SharedView" /Users/nik/Documents/Progamming/document_scanner/frontend/src/router/index.js

<success_criteria>

  • All 13 new API functions exist in client.js (verified by grep)
  • useFoldersStore is created and exports folder CRUD actions + navigation state
  • documents store return includes all new state/action exports
  • Vue Router has /folders/:folderId and /shared routes with requiresAuth guard </success_criteria>
Create `.planning/phases/04-folders-sharing-quotas-document-ux/04-08-SUMMARY.md` when done.