From f5be0a0de3b14de0566426af8a1ab850f3d21e6f Mon Sep 17 00:00:00 2001 From: nobswebdev Date: Sun, 25 Oct 2026 22:00:16 +0100 Subject: [PATCH] Show copyable share links after batch upload completes. Add share results panel gated by showShareLinks, build URLs from queue entry secrets, and align file name handling with the metadata inspector. --- frontend/src/components.d.ts | 4 + .../src/components/upload/ShareLinkField.vue | 77 ++++++++++++++++++ .../components/upload/UploadShareResults.vue | 78 +++++++++++++++++++ frontend/src/views/UploadView.vue | 17 +++- 4 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/upload/ShareLinkField.vue create mode 100644 frontend/src/components/upload/UploadShareResults.vue diff --git a/frontend/src/components.d.ts b/frontend/src/components.d.ts index 1d73c9e..1e40178 100644 --- a/frontend/src/components.d.ts +++ b/frontend/src/components.d.ts @@ -11,12 +11,16 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + InlineTabList: typeof import('./components/InlineTabList.vue')['default'] MetadataInspector: typeof import('./components/upload/MetadataInspector.vue')['default'] + MetadataOutputPanel: typeof import('./components/upload/MetadataOutputPanel.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + ShareLinkField: typeof import('./components/upload/ShareLinkField.vue')['default'] UploadConfigureModal: typeof import('./components/upload/UploadConfigureModal.vue')['default'] UploadDropzone: typeof import('./components/upload/UploadDropzone.vue')['default'] UploadQueueList: typeof import('./components/upload/UploadQueueList.vue')['default'] UploadSettingsPanel: typeof import('./components/upload/UploadSettingsPanel.vue')['default'] + UploadShareResults: typeof import('./components/upload/UploadShareResults.vue')['default'] } } diff --git a/frontend/src/components/upload/ShareLinkField.vue b/frontend/src/components/upload/ShareLinkField.vue new file mode 100644 index 0000000..ec3902c --- /dev/null +++ b/frontend/src/components/upload/ShareLinkField.vue @@ -0,0 +1,77 @@ + + + diff --git a/frontend/src/components/upload/UploadShareResults.vue b/frontend/src/components/upload/UploadShareResults.vue new file mode 100644 index 0000000..e18e4a1 --- /dev/null +++ b/frontend/src/components/upload/UploadShareResults.vue @@ -0,0 +1,78 @@ + + + diff --git a/frontend/src/views/UploadView.vue b/frontend/src/views/UploadView.vue index f61696e..d6b7910 100644 --- a/frontend/src/views/UploadView.vue +++ b/frontend/src/views/UploadView.vue @@ -31,12 +31,18 @@

{{ selectionError }}

-

+

{{ confirmedBatchSummary }}

+ + ({ }); const configureModalOpen = ref(false); const uploadConfirmed = ref(false); +const showShareLinks = ref(false); const confirmedBatchSummary = ref(null); onBeforeMount(async () => { @@ -114,6 +121,8 @@ const queueRows = computed(() => const selectedPreview = computed(() => metadataStore.getPreview(selectedFile.value)); +const selectedUpload = computed(() => uploadStore.getUpload(selectedFile.value)); + const metadataErrorCount = computed( () => queuedFiles.value.filter(file => metadataStore.getPreview(file).status === 'error').length ); @@ -125,7 +134,7 @@ const onFilesPicked = async (picked: File[]): Promise => { return; } - resetUploadSelection(); + resetUploadSession(); if (picked.length === 0) { return; @@ -168,7 +177,7 @@ const onFilesPicked = async (picked: File[]): Promise => { } }; -const resetUploadSelection = (): void => { +const resetUploadSession = (): void => { selectionError.value = null; clearAllPreviews(); queuedFiles.value = []; @@ -177,6 +186,7 @@ const resetUploadSelection = (): void => { dropzoneRef.value?.clearInput(); configureModalOpen.value = false; uploadConfirmed.value = false; + showShareLinks.value = false; confirmedBatchSummary.value = null; }; @@ -195,6 +205,7 @@ const onBatchUpload = async (uploadDespiteMetadataFailure?: boolean): Promise