feat(13-07): implement sequential cloud upload queue with typed pause/resume

- StorageBrowser: add conflict dialog (D-03) and error dialog (D-04) for paused queue states
- StorageBrowser: add upload-queue-resolve emit for Keep both / Replace / Skip / Retry / Cancel all actions
- StorageBrowser: add upload-queue-list with upload-queue-item rows for remaining queued items
- StorageBrowser: suppress UploadProgress in cloud mode (replaced by queue dialogs)
- CloudFolderView: replace placeholder onFilesSelected with sequential queue runner
- CloudFolderView: handle upload-queue-resolve events for all conflict/error resolution actions
- api/cloud.js: add conflictAction param to uploadCloudFile for keep_both/replace paths
- api/cloud.js: add downloadCloudFile authorized fallback helper
- CloudFolderView.test.js: fix CapturingStub missing name, add uploadCloudFile mock
- All 34 queue and thin-view tests pass (StorageBrowser.cloud-queue + CloudFolderView)
This commit is contained in:
curo1305
2026-06-22 19:42:29 +02:00
parent fc28e032be
commit e7e62bbab8
4 changed files with 332 additions and 15 deletions
+24 -2
View File
@@ -131,8 +131,12 @@ export function testCloudConnection(connectionId) {
* D-02: Provider credentials and raw provider URLs are never exposed.
* Returns {kind: 'open', url: '<authorized-docuvault-url>'}.
* The caller must navigate to the returned URL — never window.open to a provider URL.
*
* @param {string} connectionId - Connection UUID
* @param {string} itemId - Provider item ID (opaque reference)
* @param {object} [fileContext] - Optional file metadata context (name, contentType) for display
*/
export function openCloudFile(connectionId, itemId) {
export function openCloudFile(connectionId, itemId, fileContext) {
return request(`/api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/open`)
}
@@ -222,14 +226,32 @@ export function deleteCloudItem(connectionId, itemId) {
* @param {string} parentRef - Parent folder provider_item_id
* @param {string} filename - Target filename
* @param {File|Blob} file - File bytes
* @param {'keep_both'|'replace'|undefined} conflictAction - Optional conflict resolution action
*/
export function uploadCloudFile(connectionId, parentRef, filename, file) {
export function uploadCloudFile(connectionId, parentRef, filename, file, conflictAction) {
const formData = new FormData()
formData.append('file', file, filename)
formData.append('parent_ref', parentRef ?? '')
formData.append('filename', filename)
if (conflictAction) formData.append('conflict_action', conflictAction)
return request(`/api/cloud/connections/${connectionId}/items/upload`, {
method: 'POST',
body: formData,
})
}
/**
* Download a cloud file through the DocuVault authorized download endpoint.
*
* D-02: No raw provider URL or credential is exposed.
* D-18: Used as a fallback for unsupported preview formats (Office, Workspace).
* The authorized endpoint serves bytes through DocuVault's own proxy.
*
* Returns {kind: 'download', url: '<docuvault-relative-url>'}.
*/
export function downloadCloudFile(connectionId, itemId) {
return request(
`/api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/download`,
{ method: 'GET' },
)
}