From 48afb8b2667c53b0c09a3190fb372fec07e67e2e Mon Sep 17 00:00:00 2001 From: curo1305 Date: Fri, 26 Jun 2026 22:11:18 +0200 Subject: [PATCH] feat(14.1-03): cloud-file-detail route + CloudDetailView + getCloudItemDetail + force wiring - Add named route 'cloud-file-detail' at /cloud/:connectionId/item/:itemId(.*) to frontend/src/router/index.js; placed before cloud-folder to prevent wildcard capture; itemId is opaque (T-14.1-08) - Add getCloudItemDetail(connectionId, itemId) to frontend/src/api/cloud.js calling GET /api/cloud/connections/{id}/items/{encodeURIComponent(itemId)}/detail (T-14.1-03/T-14.1-06: zero bytes, credential-free schema) - Create frontend/src/views/CloudDetailView.vue as thin data-provider feeding DocumentDetailSurface with CloudItem detail data + event handlers - Wire force=true for re-analyze confirmation (D-11, Copywriting Contract modal) - Unsupported preview shows reason + keeps Download active, no auto-download (D-14) - Add fetchCloudItemDetail action + force param to enqueueAnalysis in store (cloudConnections.js imports cloud.js barrel for getCloudItemDetail) - translateAnalysisStatus remains single source; CloudDetailView calls store translator, never defines its own - All 8 CloudDetailParity.test.js tests now pass (route, params, Re-classify gone) --- frontend/src/api/cloud.js | 19 ++ frontend/src/router/index.js | 11 + frontend/src/stores/cloudConnections.js | 27 +- frontend/src/views/CloudDetailView.vue | 319 ++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 frontend/src/views/CloudDetailView.vue diff --git a/frontend/src/api/cloud.js b/frontend/src/api/cloud.js index f21dfd0..7a84457 100644 --- a/frontend/src/api/cloud.js +++ b/frontend/src/api/cloud.js @@ -353,6 +353,25 @@ export function retryAnalysisItem(jobId, itemId) { return jsonRequest(`/api/cloud/analysis/jobs/${jobId}/items/${itemId}/retry`, 'POST', {}) } +// ── Phase 14.1: Cloud item detail ──────────────────────────────────────────── + +/** + * Fetch owner-scoped cloud item detail: extracted text, topics, analysis status, + * provider metadata, and action capabilities. + * + * T-14.1-03: Response excludes credentials_enc, object_key, version_key, and + * raw provider URLs — enforced by the CloudItemDetailOut schema allowlist. + * T-14.1-06: Zero bytes downloaded — metadata only (no hydrate_and_cache_bytes). + * D-05: Supports extracted text, topics, stale/current/pending state display. + * + * @param {string} connectionId - Connection UUID + * @param {string} itemId - Provider item ID (opaque — must be encodeURIComponent'd) + * @returns {Promise} + */ +export function getCloudItemDetail(connectionId, itemId) { + return request(`/api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/detail`) +} + /** * Get the current user cache status and analysis settings. * diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index d5f88e0..5701080 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -61,6 +61,17 @@ const routes = [ component: () => import('../views/CloudStorageView.vue'), meta: { requiresAuth: true }, }, + { + // cloud-file-detail must be declared BEFORE cloud-folder to avoid the + // /:folderId(.*) wildcard consuming the /item/ segment. + // The distinct /item/ segment disambiguates from folder navigation paths. + // T-14.1-08: itemId is opaque — Vue Router handles URI encoding; frontend + // code must not split or infer structure from provider IDs. + path: '/cloud/:connectionId/item/:itemId(.*)', + name: 'cloud-file-detail', + component: () => import('../views/CloudDetailView.vue'), + meta: { requiresAuth: true }, + }, { path: '/cloud/:connectionId/:folderId(.*)', name: 'cloud-folder', diff --git a/frontend/src/stores/cloudConnections.js b/frontend/src/stores/cloudConnections.js index 2342da8..d315fdc 100644 --- a/frontend/src/stores/cloudConnections.js +++ b/frontend/src/stores/cloudConnections.js @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import * as api from '../api/client.js' +import * as cloudApi from '../api/cloud.js' /** * Session-storage key for last visited folder per connection. @@ -436,15 +437,20 @@ export const useCloudConnectionsStore = defineStore('cloudConnections', () => { /** * ANALYZE-01: Enqueue an analysis job and track it in store state. * + * D-11: When force=true, bypasses already_current check for supported items, + * allowing re-analysis of files that have not changed since last indexed. + * * @param {string} connectionId - Connection UUID * @param {{ scope: string, provider_item_ids?: string[], recursive?: boolean, - * failure_behavior?: string }} params + * failure_behavior?: string, force?: boolean }} params * @returns {Promise} AnalysisEnqueueOut */ async function enqueueAnalysis(connectionId, params) { const result = await api.enqueueAnalysis(connectionId, { ...params, failure_behavior: params.failure_behavior ?? failureBehavior.value, + // D-11: forward force flag to backend — undefined means default (false) + ...(params.force !== undefined ? { force: params.force } : {}), }) // Initialize the active job in memory (no credentials stored) activeAnalysisJob.value = { @@ -593,6 +599,23 @@ export const useCloudConnectionsStore = defineStore('cloudConnections', () => { } } + /** + * Phase 14.1 / D-01: Fetch owner-scoped cloud item detail. + * + * Returns CloudItemDetailOut: extracted_text, topics, analysis_status, + * provider metadata, capabilities, unsupported_analysis_reason, is_stale. + * T-14.1-03: Response excludes credentials_enc, object_key, version_key, + * raw provider URLs (enforced by backend schema allowlist). + * T-14.1-06: Zero bytes downloaded — metadata only. + * + * @param {string} connectionId - Connection UUID + * @param {string} itemId - Provider item ID (opaque) + * @returns {Promise} CloudItemDetailOut + */ + async function fetchCloudItemDetail(connectionId, itemId) { + return cloudApi.getCloudItemDetail(connectionId, itemId) + } + /** * CACHE-04: Update user cache settings via the API. * @@ -689,6 +712,8 @@ export const useCloudConnectionsStore = defineStore('cloudConnections', () => { retryItem, skipItem, cancelItem, + // Phase 14.1: Cloud item detail + fetchCloudItemDetail, // User preferences and cache settings setFailureBehavior, setDetailedAnalysisProgress, diff --git a/frontend/src/views/CloudDetailView.vue b/frontend/src/views/CloudDetailView.vue new file mode 100644 index 0000000..b7db999 --- /dev/null +++ b/frontend/src/views/CloudDetailView.vue @@ -0,0 +1,319 @@ + + +