---
phase: 04-folders-sharing-quotas-document-ux
plan: 08
type: execute
wave: 6
depends_on:
  - "04-07"
files_modified:
  - frontend/src/api/client.js
  - frontend/src/stores/folders.js
  - frontend/src/stores/documents.js
  - frontend/src/router/index.js
autonomous: true
requirements:
  - FOLD-01
  - FOLD-02
  - FOLD-03
  - FOLD-04
  - FOLD-05
  - SHARE-01
  - SHARE-02
  - SHARE-03
  - SHARE-04
  - SHARE-05
  - DOC-01
  - DOC-02

must_haves:
  truths:
    - "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"
  artifacts:
    - path: "frontend/src/api/client.js"
      provides: "listFolders, createFolder, renameFolder, deleteFolder, moveDocument, createShare, deleteShare, listShares, getSharedWithMe, getMyPreferences, updateMyPreferences, getDocumentContentUrl"
    - path: "frontend/src/stores/folders.js"
      provides: "useFoldersStore: folders, currentFolderId, breadcrumb, loading, error + folder CRUD actions"
    - path: "frontend/src/stores/documents.js"
      provides: "extended with folderId, searchQuery, sort, order state + share actions"
    - path: "frontend/src/router/index.js"
      provides: "/folders/:folderId route + /shared route"
  key_links:
    - from: "frontend/src/stores/folders.js"
      to: "frontend/src/api/client.js"
      via: "all folder CRUD actions call api.* functions"
      pattern: "import.*api"
    - from: "frontend/src/router/index.js"
      to: "frontend/src/views/FolderView.vue"
      via: "route /folders/:folderId component lazy-loaded"
      pattern: "FolderView"
---

<objective>
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.
</objective>

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

<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
</context>

<interfaces>
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')).
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Add Phase 4 API functions to client.js and create folders store</name>
  <files>frontend/src/api/client.js, frontend/src/stores/folders.js</files>
  <read_first>
    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
  </read_first>
  <action>
    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 } })`.
  </action>
  <verify>
    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.
  </verify>
  <acceptance_criteria>
    - 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)
  </acceptance_criteria>
  <done>client.js has all Phase 4 API wrappers; folders store is created and exported.</done>
</task>

<task type="auto">
  <name>Task 2: Extend documents store with folder/search/share actions + add Vue Router routes</name>
  <files>frontend/src/stores/documents.js, frontend/src/router/index.js</files>
  <read_first>
    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
  </read_first>
  <action>
    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.
  </action>
  <verify>
    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.
  </verify>
  <acceptance_criteria>
    - 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)
  </acceptance_criteria>
  <done>Documents store extended with folder/search/share state; router has two new protected routes.</done>
</task>

</tasks>

<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>

<verification>
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
</verification>

<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>

<output>
Create `.planning/phases/04-folders-sharing-quotas-document-ux/04-08-SUMMARY.md` when done.
</output>
