From ba15811f913b797e242432884f14a110be2b8cbb Mon Sep 17 00:00:00 2001 From: curo1305 Date: Tue, 23 Jun 2026 19:52:23 +0200 Subject: [PATCH] feat(14-08): add Analysis Settings section to SettingsCloudTab - Add cache limit number input with tier cap validation and usage display - Add segmented control for simplified vs detailed analysis progress (D-06) - Add segmented control for pause-batch vs continue-item failure behavior (D-11) - Load cache settings on mount via loadCacheSettings; surface API errors - Keep existing connection health/reconnect/disconnect UX intact - All 471 frontend tests pass; no regressions --- .../components/settings/SettingsCloudTab.vue | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/frontend/src/components/settings/SettingsCloudTab.vue b/frontend/src/components/settings/SettingsCloudTab.vue index e2ffce7..cf632de 100644 --- a/frontend/src/components/settings/SettingsCloudTab.vue +++ b/frontend/src/components/settings/SettingsCloudTab.vue @@ -321,6 +321,145 @@ @close="closeModal" @connected="handleConnected" /> + + +
+

Analysis Settings

+ + +
+ {{ store.cacheSettingsError }} +
+ +
+ + +
+ +
+ + + Max: {{ cacheLimitMaxMB }} MB + + + {{ cacheLimitError }} + +
+

+ Currently using {{ cacheUsageMB }} MB of {{ cacheLimitMB }} MB +

+
+ + +
+ + Analysis progress detail + +
+ + +
+
+ + +
+ + On analysis failure + +
+ + +
+
+ +
+
@@ -334,6 +473,31 @@ import CloudCredentialModal from '../cloud/CloudCredentialModal.vue' const store = useCloudConnectionsStore() +// ── Phase 14: Cache settings computed helpers ───────────────────────────────── + +/** Default tier cap: 2 GB in MB if server hasn't responded yet. */ +const DEFAULT_TIER_CAP_BYTES = 2 * 1024 * 1024 * 1024 + +/** Tier cap in MB (from server or fallback). */ +const cacheLimitMaxMB = computed(() => { + const cap = store.tierCapBytes ?? DEFAULT_TIER_CAP_BYTES + return Math.floor(cap / (1024 * 1024)) +}) + +/** Current cache limit in MB. */ +const cacheLimitMB = computed(() => { + return Math.round(store.cacheLimit / (1024 * 1024)) +}) + +/** Current cache usage in MB (from server). */ +const cacheUsageMB = computed(() => { + if (!store.cacheUsage) return 0 + return Math.round(store.cacheUsage.total_bytes / (1024 * 1024)) +}) + +/** Local validation error for cache limit input. */ +const cacheLimitError = ref('') + const PROVIDERS = [ { key: 'google_drive', label: 'Google Drive', iconColor: 'text-blue-500' }, { key: 'onedrive', label: 'OneDrive', iconColor: 'text-sky-500' }, @@ -358,6 +522,10 @@ const testingId = ref(null) onMounted(() => { store.fetchConnections() + // Phase 14: hydrate server-authoritative cache settings on mount + if (typeof store.loadCacheSettings === 'function') { + store.loadCacheSettings() + } }) /** All connections for a given provider key (may be multiple). */ @@ -530,4 +698,69 @@ async function submitRename(id) { renameError.value = e.message || 'Failed to rename connection' } } + +// ── Phase 14: Cache settings handlers ──────────────────────────────────────── + +/** + * Handle cache limit input change — validate against tier cap, then persist. + */ +async function handleCacheLimitChange(event) { + cacheLimitError.value = '' + const rawValue = parseInt(event.target.value, 10) + if (isNaN(rawValue) || rawValue < 1) { + cacheLimitError.value = 'Enter a value of at least 1 MB' + return + } + if (rawValue > cacheLimitMaxMB.value) { + cacheLimitError.value = `Maximum cache limit is ${cacheLimitMaxMB.value} MB` + return + } + const bytes = rawValue * 1024 * 1024 + try { + if (typeof store.updateCacheSettings === 'function') { + await store.updateCacheSettings({ cloud_cache_limit_bytes: bytes }) + } + } catch { + // store.cacheSettingsError set by the action; input reverts on next render + } +} + +/** + * Toggle simplified vs detailed analysis progress mode. + */ +async function handleProgressDetail(enabled) { + if (typeof store.setDetailedAnalysisProgress === 'function') { + store.setDetailedAnalysisProgress(enabled) + } + try { + if (typeof store.updateCacheSettings === 'function') { + await store.updateCacheSettings({ analysis_progress_detail: enabled }) + } + } catch { + // Revert local state on API failure + if (typeof store.setDetailedAnalysisProgress === 'function') { + store.setDetailedAnalysisProgress(!enabled) + } + } +} + +/** + * Change failure behavior for analysis jobs. + */ +async function handleFailureBehavior(behavior) { + const previous = store.failureBehavior + if (typeof store.setFailureBehavior === 'function') { + store.setFailureBehavior(behavior) + } + try { + if (typeof store.updateCacheSettings === 'function') { + await store.updateCacheSettings({ analysis_failure_behavior: behavior }) + } + } catch { + // Revert on failure + if (typeof store.setFailureBehavior === 'function') { + store.setFailureBehavior(previous) + } + } +}