From 9053a931ce4d075eddd8063de73ea95c3a346937 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Tue, 15 Jul 2025 17:42:25 +0200 Subject: [PATCH] feat: multi s3 arg input (#6187) * feat: multi s3 arg input * nits --- frontend/src/lib/components/ArgInput.svelte | 96 ++------ .../common/fileUpload/FileUpload.svelte | 207 +++++++++++------- .../common/fileUpload/S3ArgInput.svelte | 142 ++++++++++++ .../triggers/http/RouteEditorInner.svelte | 1 - 4 files changed, 285 insertions(+), 161 deletions(-) create mode 100644 frontend/src/lib/components/common/fileUpload/S3ArgInput.svelte diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index c84c04aeed..b7c274a0b4 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -9,7 +9,7 @@ emptyString, getSchemaFromProperties } from '$lib/utils' - import { DollarSign, Pipette, Plus, X, Check, Loader2 } from 'lucide-svelte' + import { DollarSign, Plus, X, Check, Loader2 } from 'lucide-svelte' import { createEventDispatcher, onDestroy, onMount, tick, untrack } from 'svelte' import { fade } from 'svelte/transition' import { Button, SecondsInput } from './common' @@ -25,7 +25,6 @@ import ArgEnum from './ArgEnum.svelte' import DateTimeInput from './DateTimeInput.svelte' import DateInput from './DateInput.svelte' - import S3FilePicker from './S3FilePicker.svelte' import CurrencyInput from './apps/components/inputs/currency/CurrencyInput.svelte' import FileUpload from './common/fileUpload/FileUpload.svelte' import autosize from '$lib/autosize' @@ -42,6 +41,7 @@ import type { ComponentCustomCSS } from './apps/types' import MultiSelect from './select/MultiSelect.svelte' import { safeSelectItems } from './select/utils.svelte' + import S3ArgInput from './common/fileUpload/S3ArgInput.svelte' interface Props { label?: string @@ -227,8 +227,6 @@ let ignoreValueUndefined = $state(false) let error: string = $state('') - let s3FilePicker: S3FilePicker | undefined = $state() - let s3FileUploadRawMode: boolean = $state(false) let isListJson = $state(false) let hasIsListJsonChanged = $state(false) @@ -499,16 +497,6 @@ }) - { - rawValue = JSON.stringify(value, null, 2) - editor?.setCode(rawValue) - }} - readOnlyMode={false} -/> -
  Not set {/if}
+ {:else if (inputCat == 'resource-object' && format && format.split('-').length > 1 && format + .replace('resource-', '') + .replace('_', '') + .toLowerCase() == 's3object') || (inputCat == 'list' && itemsType?.resourceType === 's3_object')} + dispatch('focus')} + onBlur={() => dispatch('blur')} + bind:editor + {appPath} + {computeS3ForceViewerPolicies} + /> {:else if inputCat == 'list' && !isListJson}
@@ -851,70 +855,6 @@ }} {showSchemaExplorer} /> - {:else if inputCat == 'resource-object' && format && format.split('-').length > 1 && format - .replace('resource-', '') - .replace('_', '') - .toLowerCase() == 's3object'} -
- - {#if s3FileUploadRawMode} - {#await import('$lib/components/JsonEditor.svelte')} - - {:then Module} - { - dispatch('focus') - }} - on:blur={(e) => { - dispatch('blur') - }} - code={JSON.stringify(value ?? defaultValue ?? { s3: '' }, null, 2)} - on:changeValue={(e) => { - setNewValueFromCode(e.detail) - }} - /> - {/await} - {:else} - { - value = { - s3: evt.detail?.path ?? '', - filename: evt.detail?.filename ?? '' - } - }} - on:deletion={(evt) => { - value = { - s3: '' - } - }} - defaultValue={defaultValue?.s3} - initialValue={value} - /> - {/if} - -
{:else if inputCat == 'object' || inputCat == 'resource-object' || isListJson} {#if oneOf && oneOf.length >= 2}
diff --git a/frontend/src/lib/components/common/fileUpload/FileUpload.svelte b/frontend/src/lib/components/common/fileUpload/FileUpload.svelte index 44f2a7d8d6..a12fb6b421 100644 --- a/frontend/src/lib/components/common/fileUpload/FileUpload.svelte +++ b/frontend/src/lib/components/common/fileUpload/FileUpload.svelte @@ -7,7 +7,7 @@ import { workspaceStore } from '$lib/stores' import { AppService, HelpersService } from '$lib/gen' import { writable, type Writable } from 'svelte/store' - import { Ban, CheckCheck, FileWarning, Files, RefreshCcw, Trash } from 'lucide-svelte' + import { Ban, CheckCheck, FileWarning, Files, RefreshCcw, Trash, XIcon } from 'lucide-svelte' import { twMerge } from 'tailwind-merge' import { createEventDispatcher, onDestroy } from 'svelte' import { emptyString } from '$lib/utils' @@ -94,16 +94,15 @@ ) init() + function transform(initialValue: { s3: string; filename?: string }) { + return { + name: initialValue?.filename ?? initialValue?.s3 ?? 'Unknown file', + path: initialValue?.s3, + fromBucket: true + } + } function init() { if (initialS3) { - function transform(initialValue: { s3: string; filename: string }) { - return { - name: initialValue.filename, - size: 0, - progress: 100, - path: initialValue?.s3 - } - } for (const s3 of initialS3) { if (!$fileUploads.find((fileUpload) => fileUpload.path === s3)) { let initialFileUploads = initialValue @@ -117,15 +116,24 @@ } } + export function addUpload(upload: { s3: string; filename?: string }) { + $fileUploads = [...$fileUploads, transform(upload)] + } + + export function setUpload(upload: { s3: string; filename?: string }) { + $fileUploads = [transform(upload)] + } + type FileUploadData = { name: string - size: number - progress: number + size?: number + progress?: number cancelled?: boolean errorMessage?: string path?: string file?: File deleteToken?: string + fromBucket?: boolean } async function handleChange(files: File[] | undefined) { @@ -394,8 +402,28 @@ }) +{#snippet fileInput()} + { + forceDisplayUploads = false + handleChange(detail) + }} + class={twMerge('w-full h-full', customClass, 'wm-file-input')} + style={customStyle} + defaultFile={defaultValue} + > + {containerText}{#if disabled}
(Disabled){/if} +
+{/snippet} +
- {#if $fileUploads.length > 0 && !forceDisplayUploads} + {#if $fileUploads.length > 0}
{#each $fileUploads as fileUpload} @@ -468,7 +496,7 @@ Remove from list {/if} - {#if fileUpload.progress < 100 && !fileUpload.cancelled && !fileUpload.errorMessage} + {#if fileUpload.progress !== undefined && fileUpload.progress < 100 && !fileUpload.cancelled && !fileUpload.errorMessage} + {:else if fileUpload.fromBucket} + {/if}
- - {#if fileUpload.errorMessage} - {fileUpload.errorMessage} - {:else if fileUpload.cancelled} - Upload cancelled - {/if} - + {#if !fileUpload.fromBucket} + + {#if fileUpload.errorMessage} + {fileUpload.errorMessage} + {:else if fileUpload.cancelled} + Upload cancelled + {/if} + + {/if} {#if !(fileUpload.cancelled || fileUpload.errorMessage !== undefined)} - {fileUpload.progress === 100 ? 'Upload finished' : `Uploading`} to path: {fileUpload.path ?? - 'N/A'} + {#if fileUpload.fromBucket} + {fileUpload.path ?? 'N/A'} + {:else} + {fileUpload.progress === 100 ? 'Upload finished' : `Uploading`} to path: {fileUpload.path ?? + 'N/A'} + {/if} {/if}
{/each}
- {#if allowMultiple} -
- {#if !$fileUploads.every((fileUpload) => fileUpload.progress === 100 || fileUpload.cancelled)} - - {/if} +
+ {#if !forceDisplayUploads && (allowMultiple || folderOnly) && !$fileUploads.every((fileUpload) => fileUpload.progress === 100 || fileUpload.cancelled || fileUpload.fromBucket)} -
- {/if} + {/if} + {#if allowMultiple} + {#if forceDisplayUploads} + {@render fileInput()} + {:else} + + {/if} + {/if} +
{:else} - { - forceDisplayUploads = false - handleChange(detail) - }} - class={twMerge('w-full h-full', customClass, 'wm-file-input')} - style={customStyle} - defaultFile={defaultValue} - > - {containerText}{#if disabled}
(Disabled){/if} -
+ {@render fileInput()} {/if} {#if initialS3 && initialS3.length > 0 && $fileUploads.length == 0}
- File currently selected: {initialS3?.join(', ')} + File{initialS3.length > 1 ? 's' : ''} currently selected: {initialS3?.join(', ')}
{/if} diff --git a/frontend/src/lib/components/common/fileUpload/S3ArgInput.svelte b/frontend/src/lib/components/common/fileUpload/S3ArgInput.svelte new file mode 100644 index 0000000000..0f072e99ef --- /dev/null +++ b/frontend/src/lib/components/common/fileUpload/S3ArgInput.svelte @@ -0,0 +1,142 @@ + + + { + if (multiple) { + if (Array.isArray(value)) { + value.push(ev.detail) + } else { + value = [ev.detail] + } + fileUpload?.addUpload(ev.detail) + } else { + value = ev.detail + fileUpload?.setUpload(value) + } + editor?.setCode(JSON.stringify(value)) + }} + readOnlyMode={false} +/> + +
+ + {#if s3FileUploadRawMode} + {#await import('$lib/components/JsonEditor.svelte')} + + {:then Module} + { + onFocus() + }} + on:blur={(e) => { + onBlur() + }} + code={JSON.stringify(value ?? defaultValue ?? (multiple ? [] : { s3: '' }), null, 2)} + on:changeValue={(e) => { + setNewValueFromCode(e.detail) + }} + /> + {/await} + {:else} + { + const s3Object = { + s3: evt.detail?.path ?? '', + filename: evt.detail?.filename ?? '' + } + if (multiple) { + if (Array.isArray(value)) { + value.push(s3Object) + } else { + value = [s3Object] + } + } else { + value = s3Object + } + }} + on:deletion={(evt) => { + if (multiple) { + if (Array.isArray(value)) { + value = value.filter((v) => v.s3 !== evt.detail?.path) + } + } else { + value = { + s3: '' + } + } + }} + defaultValue={defaultValue?.s3} + initialValue={value} + /> + {/if} + +
diff --git a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte index b4c3005322..fad315e775 100644 --- a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte +++ b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte @@ -502,7 +502,6 @@ disabled={!can_write} /> {/if} - {s3FileUploadRawMode} {#if s3FileUploadRawMode} {#if can_write}