---
phase: 05-cloud-storage-backends
plan: 07
subsystem: ui
tags: [cloud-storage, vue3, pinia, vitest, settings, webdav, oauth, tailwind]

# Dependency graph
requires:
  - phase: 05-cloud-storage-backends
    plan: 06
    provides: "backend/api/cloud.py with all 7 endpoints (list, disconnect, OAuth initiate/callback, WebDAV connect, status update) — consumed by frontend API client"

provides:
  - "frontend/src/stores/cloudConnections.js: useCloudConnectionsStore with connections/loading/error state and fetchConnections, disconnect, disconnectAll actions"
  - "frontend/src/api/client.js: listCloudConnections, disconnectCloud, connectWebDav, updateDefaultStorage API functions"
  - "frontend/src/views/SettingsView.vue: 3-tab layout (Preferences/AI Configuration/Cloud Storage) with OAuth callback handling and success/error toast"
  - "frontend/src/components/settings/SettingsCloudTab.vue: all 4 provider rows with status badges, action buttons, REQUIRES_REAUTH banner, disconnect-all"
  - "frontend/src/components/cloud/CloudCredentialModal.vue: WebDAV/Nextcloud credential modal with authMethod radio toggle"
  - "frontend/src/components/settings/SettingsPreferencesTab.vue and SettingsAiTab.vue: extracted from original SettingsView"
affects: [05-08]

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Pinia composition API store pattern: defineStore with ref() state, async actions — matches existing folders.js pattern"
    - "vi.mock for Pinia store in component tests: mock the store module directly (no @pinia/testing) — same approach as folders.test.js"
    - "OAuth callback via URL query params: window.location.search parsed in onMounted; router.replace cleans params after read"
    - "OAuth initiation via window.location.href redirect: no fetch call needed — FastAPI handles the OAuth code exchange"

key-files:
  created:
    - frontend/src/stores/cloudConnections.js
    - frontend/src/stores/__tests__/cloudConnections.test.js
    - frontend/src/components/settings/SettingsPreferencesTab.vue
    - frontend/src/components/settings/SettingsAiTab.vue
    - frontend/src/components/settings/SettingsCloudTab.vue
    - frontend/src/components/settings/__tests__/SettingsCloudTab.test.js
    - frontend/src/components/cloud/CloudCredentialModal.vue
  modified:
    - frontend/src/api/client.js
    - frontend/src/views/SettingsView.vue
    - frontend/package.json
    - frontend/vite.config.js

key-decisions:
  - "Used vi.mock for store in component tests instead of @pinia/testing (not installed, not in package.json). Mock returns a plain object matching the store's public API — avoids dependency on @pinia/testing while satisfying CLAUDE.md testing requirement (W4)"
  - "Fixed pre-existing Vite build failure (top-level await in main.js) by adding build.target='esnext' to vite.config.js — esnext natively supports top-level await, cleanest fix with no code changes needed"
  - "REQUIRES_REAUTH row renders both Reconnect and Remove buttons per UI-SPEC Surface 2; Remove button triggers same ConfirmBlock pattern as ACTIVE/ERROR rows"

patterns-established:
  - "Cloud provider row pattern: 4 providers always shown; connectionFor(providerKey) returns store connection or null; status badge + action button vary by status"
  - "Inline ConfirmBlock: confirmRemoveId ref tracks which row is in confirm mode; v-if/v-else renders either the action button or ConfirmBlock inline"
  - "SettingsView OAuth callback: onMounted reads URLSearchParams, sets activeTab='cloud', router.replace clears params, success auto-dismisses via setTimeout(5000)"

requirements-completed:
  - CLOUD-01
  - CLOUD-03
  - CLOUD-04
  - CLOUD-05
  - CLOUD-06

# Metrics
duration: 14min
completed: 2026-05-29
---

# Phase 5 Plan 07: Cloud Storage Frontend UI Summary

**Pinia cloudConnections store, 3-tab SettingsView with OAuth callback handling, SettingsCloudTab with 4 provider rows and status badges, and CloudCredentialModal for WebDAV/Nextcloud credential input**

## Performance

- **Duration:** 14 min
- **Started:** 2026-05-29T06:01:00Z
- **Completed:** 2026-05-29T06:15:23Z
- **Tasks:** 2
- **Files modified:** 11

## Accomplishments

- Created `useCloudConnectionsStore` Pinia store with `connections`, `loading`, `error` state and `fetchConnections()`, `disconnect(id)`, `disconnectAll()` actions — follows same composition API pattern as `useFoldersStore`
- Added 4 cloud API functions to `frontend/src/api/client.js`: `listCloudConnections`, `disconnectCloud`, `connectWebDav`, `updateDefaultStorage`
- Rewrote `SettingsView.vue` to a 3-tab layout (Preferences / AI Configuration / Cloud Storage) mirroring `AdminView.vue` tab strip verbatim; `onMounted` reads `?cloud_connected=` and `?cloud_error=` query params and shows toast/banner accordingly
- Built `SettingsCloudTab.vue` showing all 4 providers (Google Drive, OneDrive, Nextcloud, WebDAV server) with inline status badges, per-status action buttons, `REQUIRES_REAUTH` yellow banner, inline `ConfirmBlock` for remove confirmation, and "Disconnect all" action
- Built `CloudCredentialModal.vue` with server URL, username, `authMethod` radio (app_password / account_password), and password fields; escape/overlay-click dismiss; spinner during save
- Extracted `SettingsPreferencesTab.vue` and `SettingsAiTab.vue` from the original flat `SettingsView`

## Task Commits

1. **Task 1: cloudConnections store + API client** - `612d542` (feat)
2. **Task 2: 3-tab SettingsView + all components** - `63a6829` (feat)

## Files Created/Modified

- `frontend/src/stores/cloudConnections.js` — Pinia store for cloud connections state
- `frontend/src/stores/__tests__/cloudConnections.test.js` — 4 Vitest unit tests (W4)
- `frontend/src/api/client.js` — Added cloud storage section (listCloudConnections, disconnectCloud, connectWebDav, updateDefaultStorage)
- `frontend/src/views/SettingsView.vue` — Rewritten as 3-tab layout with OAuth callback handling
- `frontend/src/components/settings/SettingsPreferencesTab.vue` — Extracted from SettingsView
- `frontend/src/components/settings/SettingsAiTab.vue` — Extracted from SettingsView
- `frontend/src/components/settings/SettingsCloudTab.vue` — Provider card list with status badges, action buttons, modals
- `frontend/src/components/settings/__tests__/SettingsCloudTab.test.js` — 2 mount tests (W4)
- `frontend/src/components/cloud/CloudCredentialModal.vue` — WebDAV/Nextcloud credential modal
- `frontend/package.json` — Added `"test": "vitest run"` script
- `frontend/vite.config.js` — Added `build.target: 'esnext'` to fix pre-existing top-level await build failure

## Decisions Made

- `@pinia/testing` is not installed and not in `package.json`. Used `vi.mock('../../../stores/cloudConnections.js', ...)` to mock the store in `SettingsCloudTab.test.js` — same approach as `folders.test.js` uses `vi.mock` for the API. No dependency installation needed.
- Pre-existing `npm run build` failure (top-level `await router.isReady()` in `main.js` incompatible with default esbuild targets). Fix: `build.target = 'esnext'` in `vite.config.js` — esnext natively supports module-level await. Zero code change to `main.js`.
- OAuth initiation for Google Drive and OneDrive uses `window.location.href = /api/cloud/oauth/initiate/{provider}` — no fetch call — matching the backend FastAPI `RedirectResponse` pattern.

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Fixed pre-existing Vite build failure (top-level await)**
- **Found during:** Task 2 verification (`npm run build`)
- **Issue:** `main.js` uses `await router.isReady()` at module top-level, which esbuild's default target (`chrome87`/`es2020`) does not support. This caused every build to fail with "Top-level await is not available in the configured target environment".
- **Fix:** Added `build: { target: 'esnext' }` to `frontend/vite.config.js`. No code changes to `main.js` required.
- **Files modified:** `frontend/vite.config.js`
- **Verification:** `npm run build` exits 0, bundle output 185 kB.
- **Committed in:** `63a6829` (Task 2 commit)

---

**Total deviations:** 1 auto-fixed (Rule 1 — pre-existing bug)
**Impact on plan:** Fix was required for the plan's success criteria (`npm run build` exits 0). No scope creep.

## Issues Encountered

- `@pinia/testing` package is not installed — the plan's `SettingsCloudTab.test.js` spec used `createTestingPinia` from it. Resolved by using `vi.mock` on the store module (the same pattern already established in `folders.test.js`). No package install required.
- `npm run test` script did not exist in `package.json` — the plan required running tests via `npm run test`. Added `"test": "vitest run"` to the scripts block.

## Known Stubs

None. All 4 provider rows are wired to the live `useCloudConnectionsStore` — `fetchConnections()` is called in `onMounted`. The "Not connected" state is the correct zero-state display (per UI-SPEC: "all 4 providers always shown").

## Threat Surface Scan

No new network endpoints introduced. Client-side changes only.

| Flag | File | Description |
|------|------|-------------|
| T-05-07-02 mitigated | `SettingsView.vue` | `?cloud_error=` decoded via `decodeURIComponent` and displayed via `{{ oauthError }}` template binding — Vue auto-escaping prevents HTML injection |
| T-05-07-03 accepted | `CloudCredentialModal.vue` | Password lives in `ref('')` only during modal interaction; `close()` is called on `@connected` which unmounts the form; `watch(props.show)` resets all refs to empty on reopen |

## Next Phase Readiness

- All frontend cloud storage management UI is complete and building.
- 61 Vitest tests pass (4 new store tests + 2 new component tests + 55 pre-existing).
- Plan 05-08 can proceed: AppSidebar cloud tree nodes (`CloudProviderTreeItem`, `CloudFolderTreeItem`) depend on `useCloudConnectionsStore` (now available).

## Self-Check: PASSED

Files verified present:
- `frontend/src/stores/cloudConnections.js`: FOUND (1045 chars)
- `frontend/src/stores/__tests__/cloudConnections.test.js`: FOUND (2129 chars)
- `frontend/src/api/client.js`: FOUND (with listCloudConnections, disconnectCloud, connectWebDav, updateDefaultStorage)
- `frontend/src/views/SettingsView.vue`: FOUND (with activeTab, oauthSuccessProvider, oauthError, SettingsPreferencesTab, SettingsCloudTab)
- `frontend/src/components/settings/SettingsPreferencesTab.vue`: FOUND
- `frontend/src/components/settings/SettingsAiTab.vue`: FOUND
- `frontend/src/components/settings/SettingsCloudTab.vue`: FOUND (with google_drive, onedrive, nextcloud, webdav, CloudCredentialModal, useCloudConnectionsStore)
- `frontend/src/components/settings/__tests__/SettingsCloudTab.test.js`: FOUND
- `frontend/src/components/cloud/CloudCredentialModal.vue`: FOUND (with authMethod)

Commits verified:
- `612d542`: feat(05-07): cloud connections Pinia store + API client functions — FOUND
- `63a6829`: feat(05-07): 3-tab SettingsView, SettingsCloudTab, CloudCredentialModal — FOUND

Test verification: `npm run test` → 61 passed, 0 failed
Build verification: `npm run build` → exit 0, 185 kB bundle

---
*Phase: 05-cloud-storage-backends*
*Completed: 2026-05-29*
