docs(05-07): complete cloud storage frontend UI plan — SUMMARY and STATE

- useCloudConnectionsStore, 3-tab SettingsView, SettingsCloudTab, CloudCredentialModal
- 61 Vitest tests passing, Vite build exits 0
- Fixed pre-existing build failure (top-level await) via build.target=esnext
This commit is contained in:
curo1305
2026-05-29 08:18:48 +02:00
parent 63a68296a5
commit ec0c69fb4e
2 changed files with 185 additions and 7 deletions
@@ -0,0 +1,177 @@
---
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*