feat: add "Choose from Server" attachment picker to claim/preauth forms and chatbot

Native file inputs can't be pointed at a default server folder for security
reasons, so instead the Browse buttons on the Claim/PreAuth/Select Procedures
forms and the AI chatbot's attach button now also offer picking from
previously saved Cloud Storage attachments (e.g. Copy Agent screenshots) via
a new server-side picker modal, scoped per-patient on the claim forms and
unscoped (recent across all patients) in the chatbot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 00:36:43 -04:00
co-authored by Claude Sonnet 5
parent 8b245d00c6
commit 34312b62bc
6 changed files with 332 additions and 11 deletions
@@ -9,6 +9,7 @@ import { Upload, X, FilePlus } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { cn } from "@/lib/utils";
import { ServerAttachmentsPickerModal } from "@/components/file-upload/server-attachments-picker-modal";
export type MultipleFileUploadZoneHandle = {
getFiles: () => File[];
@@ -25,6 +26,9 @@ interface FileUploadZoneProps {
maxFileSizeMB?: number;
//OPTIONAL: per-mime (or wildcard) map in MB: { "application/pdf": 10, "image/*": 2 }
maxFileSizeByType?: Record<string, number>;
// OPTIONAL: when set, shows a "Choose from Server" option listing this patient's
// previously saved Cloud Storage attachments (e.g. Copy Agent screenshots)
patientId?: number;
}
export const MultipleFileUploadZone = forwardRef<
@@ -39,12 +43,14 @@ export const MultipleFileUploadZone = forwardRef<
maxFiles = 10,
maxFileSizeMB = 10, // default fallback per-file size (MB)
maxFileSizeByType, // optional per-type overrides, e.g. { "application/pdf": 10, "image/*": 2 }
patientId,
},
ref
) => {
const { toast } = useToast();
const [isDragging, setIsDragging] = useState(false);
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
const [isServerPickerOpen, setIsServerPickerOpen] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const parsedAccept = acceptedFileTypes
@@ -222,7 +228,7 @@ export const MultipleFileUploadZone = forwardRef<
[onFilesChange]
);
const handleFiles = (files: FileList | null) => {
const handleFiles = (files: FileList | File[] | null) => {
if (!files) return;
const newFiles = Array.from(files).filter(validateFile);
@@ -439,19 +445,42 @@ export const MultipleFileUploadZone = forwardRef<
Or click to browse files
</p>
</div>
<Button
type="button"
variant="default"
onClick={(e) => {
e.stopPropagation();
handleBrowseClick();
}}
>
Browse files
</Button>
<div className="flex gap-2">
<Button
type="button"
variant="default"
onClick={(e) => {
e.stopPropagation();
handleBrowseClick();
}}
>
Choose from This Computer
</Button>
{patientId ? (
<Button
type="button"
variant="outline"
onClick={(e) => {
e.stopPropagation();
setIsServerPickerOpen(true);
}}
>
Choose from Server
</Button>
) : null}
</div>
</div>
)}
</div>
{patientId ? (
<ServerAttachmentsPickerModal
open={isServerPickerOpen}
onClose={() => setIsServerPickerOpen(false)}
patientId={patientId}
onFilesSelected={(files) => handleFiles(files)}
/>
) : null}
</div>
);
}