Files
DentalManagement2025/apps/Frontend/src/components/claims/claim-document-upload-modal.tsx

149 lines
4.6 KiB
TypeScript

import React, { useCallback, useRef, useState } from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { RefreshCw, FilePlus } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import {
MultipleFileUploadZone,
MultipleFileUploadZoneHandle,
} from "../file-upload/multiple-file-upload-zone";
export default function ClaimDocumentsUploadMultiple() {
const { toast } = useToast();
// Internal configuration
const MAX_FILES = 10;
const ACCEPTED_FILE_TYPES =
"application/pdf,image/jpeg,image/jpg,image/png,image/webp";
const TITLE = "Upload Claim Document(s)";
const DESCRIPTION =
"You can upload up to 10 files. Allowed types: PDF, JPG, PNG, WEBP.";
// Zone ref + minimal UI state (parent does not own files)
const uploadZoneRef = useRef<MultipleFileUploadZoneHandle | null>(null);
const [filesForUI, setFilesForUI] = useState<File[]>([]);
const [isUploading, setIsUploading] = useState(false); // forwarded to upload zone
const [isExtracting, setIsExtracting] = useState(false);
// Called by MultipleFileUploadZone when its internal list changes (UI-only)
const handleZoneFilesChange = useCallback((files: File[]) => {
setFilesForUI(files);
}, []);
// Dummy save (simulate async). Replace with real API call when needed.
const handleSave = useCallback(async (files: File[]) => {
// simulate network / processing time
await new Promise((res) => setTimeout(res, 800));
console.log(
"handleSave called for files:",
files.map((f) => f.name)
);
}, []);
// Extract handler — reads files from the zone via ref and calls handleSave
const handleExtract = useCallback(async () => {
const files = uploadZoneRef.current?.getFiles() ?? [];
if (files.length === 0) {
toast({
title: "No files",
description: "Please upload at least one file before extracting.",
variant: "destructive",
});
return;
}
if (isExtracting) return;
setIsExtracting(true);
try {
await handleSave(files);
toast({
title: "Extraction started",
description: `Processing ${files.length} file(s).`,
variant: "default",
});
// we intentionally leave files intact in the zone after extraction
} catch (err) {
toast({
title: "Extraction failed",
description:
"There was an error starting extraction. Please try again.",
variant: "destructive",
});
// eslint-disable-next-line no-console
console.error("extract error", err);
} finally {
setIsExtracting(false);
}
}, [handleSave, isExtracting, toast]);
return (
<div className="space-y-8 py-8">
<Card>
<CardHeader>
<CardTitle>{TITLE}</CardTitle>
<CardDescription>{DESCRIPTION}</CardDescription>
</CardHeader>
<CardContent>
{/* File Upload Section */}
<div className="bg-gray-100 p-4 rounded-md space-y-4">
<MultipleFileUploadZone
ref={uploadZoneRef}
onFilesChange={handleZoneFilesChange}
isUploading={isUploading}
acceptedFileTypes={ACCEPTED_FILE_TYPES}
maxFiles={MAX_FILES}
/>
{/* Show list of files received from the upload zone */}
{filesForUI.length > 0 && (
<div>
<p className="text-sm text-gray-600 mb-2">
Uploaded ({filesForUI.length}/{MAX_FILES})
</p>
<ul className="text-sm text-gray-700 list-disc ml-6 max-h-40 overflow-auto">
{filesForUI.map((file, index) => (
<li key={index} className="truncate" title={file.name}>
{file.name}
</li>
))}
</ul>
</div>
)}
</div>
{/* Action Button */}
<div className="mt-4">
<Button
className="w-full h-12 gap-2"
disabled={filesForUI.length === 0 || isExtracting}
onClick={handleExtract}
>
{isExtracting ? (
<>
<RefreshCw className="h-4 w-4 animate-spin" />
Processing...
</>
) : (
<>
<FilePlus className="h-4 w-4" />
Extract Claim Data
</>
)}
</Button>
</div>
</CardContent>
</Card>
</div>
);
}