From 633c7c52c58e685bed08f4539b0a3b0ae2827c12 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Fri, 3 Jul 2026 06:22:32 +0000 Subject: [PATCH] feat: add viewing existingFiles to smartfileinput --- .../src/components/SmartFileInput/index.tsx | 68 ++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/packages/ui-common/src/components/SmartFileInput/index.tsx b/packages/ui-common/src/components/SmartFileInput/index.tsx index 978fb5498..d39e431c7 100644 --- a/packages/ui-common/src/components/SmartFileInput/index.tsx +++ b/packages/ui-common/src/components/SmartFileInput/index.tsx @@ -27,6 +27,16 @@ export interface SmartFileInputProps { * no in-memory File is currently selected for them. */ uploadedKeys?: string[]; + /** + * Metadata for already-uploaded files, keyed by fileKey. When a field has + * entries here, they're listed with view/download links (instead of the + * generic placeholder text) and the field counts as uploaded even if it's + * not also listed in `uploadedKeys`. + */ + existingFiles?: Record< + string, + { name: string; url: string; size?: number }[] + >; /** Disabled state for the entire file input group. */ disabled?: boolean; /** Display variant style. Default is "default" (large dropzone). Minimal renders a compact upload button. */ @@ -76,6 +86,7 @@ export function SmartFileInput({ onChange, errors, uploadedKeys, + existingFiles, disabled = false, variant = "default", className, @@ -270,9 +281,11 @@ export function SmartFileInput({ const fieldError = errors?.[field.fileKey] || localErrors[field.fileKey]; const isDragOver = dragActive[field.fileKey]; + const existingForField = existingFiles?.[field.fileKey] ?? []; // Already uploaded server-side and nothing newly picked to replace it. const isUploaded = - (uploadedKeys?.includes(field.fileKey) ?? false) && + ((uploadedKeys?.includes(field.fileKey) ?? false) || + existingForField.length > 0) && currentFiles.length === 0; // Format accepted files for the HTML input element @@ -417,6 +430,21 @@ export function SmartFileInput({ {field.allowedExtensions.join(", ").toUpperCase() || "All"} + {existingForField.length > 0 && ( +
+ {existingForField.map((f, idx) => ( + + {f.name} + + ))} +
+ )} ) : isUploaded ? ( // Uploaded state: a solid success panel that still doubles as a @@ -431,7 +459,7 @@ export function SmartFileInput({ ? "border-2 border-dashed border-primary bg-primary/5 dark:bg-primary/10" : "border-emerald-300/70 bg-emerald-50/60 dark:border-emerald-500/30 dark:bg-emerald-500/10", disabled && - "opacity-50 pointer-events-none cursor-not-allowed", + "opacity-50 pointer-events-none cursor-not-allowed", )} > {isDragOver ? "Drop to replace" : "Document uploaded"}

-

- {isDragOver - ? "Release to replace the document on file." - : "Saved to your application. Drag a new file here or click to replace it."} -

+ {existingForField.length > 0 ? ( +
+ {existingForField.map((f, idx) => ( + e.stopPropagation()} + className="relative z-10 text-xs text-primary hover:underline truncate max-w-xs" + > + {f.name} + {typeof f.size === "number" + ? ` (${formatBytes(f.size)})` + : ""} + + ))} +
+ ) : ( +

+ {isDragOver + ? "Release to replace the document on file." + : "Saved to your application. Drag a new file here or click to replace it."} +

+ )} @@ -480,9 +528,9 @@ export function SmartFileInput({ ? "border-primary bg-primary/5 dark:bg-primary/10" : "border-border hover:border-primary/50 hover:bg-muted/10", fieldError && - "border-destructive hover:border-destructive/80", + "border-destructive hover:border-destructive/80", disabled && - "opacity-50 pointer-events-none cursor-not-allowed", + "opacity-50 pointer-events-none cursor-not-allowed", )} >