diff options
| author | rlaks5757 <rlaks5757@gmail.com> | 2025-03-31 17:23:48 +0900 |
|---|---|---|
| committer | rlaks5757 <rlaks5757@gmail.com> | 2025-03-31 17:23:48 +0900 |
| commit | 3e47b8275454452324e3b6a7607f2db361110b0e (patch) | |
| tree | b6a3be3c1d4c8dd8d0dda9bc33f5974c32261a71 /components/form-data/form-data-report-temp-upload-dialog.tsx | |
| parent | 311ba2c57fe75ef378bf272c4ccf9fdee7843973 (diff) | |
Report -> Vendor Document 문구 수정, Upload Dialog Design Layout 변경
Diffstat (limited to 'components/form-data/form-data-report-temp-upload-dialog.tsx')
| -rw-r--r-- | components/form-data/form-data-report-temp-upload-dialog.tsx | 453 |
1 files changed, 255 insertions, 198 deletions
diff --git a/components/form-data/form-data-report-temp-upload-dialog.tsx b/components/form-data/form-data-report-temp-upload-dialog.tsx index 69df704e..32273415 100644 --- a/components/form-data/form-data-report-temp-upload-dialog.tsx +++ b/components/form-data/form-data-report-temp-upload-dialog.tsx @@ -10,7 +10,7 @@ import React, { import { useToast } from "@/hooks/use-toast"; import { toast as toastMessage } from "sonner"; import prettyBytes from "pretty-bytes"; -import { X, Loader2, Download, Delete, Trash2 } from "lucide-react"; +import { X, Loader2, Download, Delete, Trash2, BookDown } from "lucide-react"; import ExcelJS from "exceljs"; import { saveAs } from "file-saver"; import { Badge } from "@/components/ui/badge"; @@ -54,6 +54,13 @@ import { AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { getReportTempList, uploadReportTemp, @@ -88,77 +95,7 @@ export const FormDataReportTempUploadDialog: FC< uploaderType, }) => { const { toast } = useToast(); - const [selectedFiles, setSelectedFiles] = useState<File[]>([]); - const [isUploading, setIsUploading] = useState(false); - const [uploadProgress, setUploadProgress] = useState(0); - const [prevReportTemp, setPrevReportTemp] = useState<VendorDataReportTemps[]>( - [] - ); - - useEffect(() => { - updateReportTempList(packageId, formId, setPrevReportTemp); - }, [packageId, formId]); - - // 드롭존 - 파일 드랍 처리 - const handleDropAccepted = (acceptedFiles: File[]) => { - const newFiles = [...selectedFiles, ...acceptedFiles]; - setSelectedFiles(newFiles); - }; - - // 드롭존 - 파일 거부(에러) 처리 - const handleDropRejected = (fileRejections: any[]) => { - fileRejections.forEach((rejection) => { - toast({ - variant: "destructive", - title: "File Error", - description: `${rejection.file.name}: ${ - rejection.errors[0]?.message || "Upload failed" - }`, - }); - }); - }; - - // 파일 제거 핸들러 - const removeFile = (index: number) => { - const updatedFiles = [...selectedFiles]; - updatedFiles.splice(index, 1); - setSelectedFiles(updatedFiles); - }; - - const submitData = async () => { - setIsUploading(true); - setUploadProgress(0); - try { - const totalFiles = selectedFiles.length; - let successCount = 0; - - for (let i = 0; i < totalFiles; i++) { - const file = selectedFiles[i]; - - const formData = new FormData(); - formData.append("file", file); - formData.append("customFileName", file.name); - formData.append("uploaderType", uploaderType); - - await uploadReportTemp(packageId, formId, formData); - - successCount++; - setUploadProgress(Math.round((successCount / totalFiles) * 100)); - } - } catch (err) { - console.error(err); - toast({ - title: "Error", - description: "파일 업로드 중 오류가 발생했습니다.", - variant: "destructive", - }); - } finally { - setIsUploading(false); - setUploadProgress(0); - updateReportTempList(packageId, formId, setPrevReportTemp); - setOpen(false); - } - }; + const [tabValue, setTabValue] = useState<"upload" | "uploaded">("upload"); const downloadTempFile = async () => { try { @@ -253,115 +190,214 @@ export const FormDataReportTempUploadDialog: FC< <Dialog open={open} onOpenChange={setOpen}> <DialogContent className="w-[600px]" style={{ maxWidth: "none" }}> <DialogHeader> - <DialogTitle>Report Template Upload</DialogTitle> + <DialogTitle>Vendor Document Template</DialogTitle> <DialogDescription> - 사용하시고자 하는 Report Template(.docx)를 업로드 하여주시기 - 바랍니다. + {/* 사용하시고자 하는 Vendor Document Template(.docx)를 업로드 + 하여주시기 바랍니다. */} </DialogDescription> </DialogHeader> - <div> - <Label>Sample Template Download</Label> - - <FileList className="max-h-[200px] gap-3"> - <FileListItem className="p-3"> - <FileListHeader> - <FileListIcon /> - <FileListInfo> - <FileListName>sample_template_file.docx</FileListName> - </FileListInfo> - <FileListAction onClick={downloadTempFile}> - <Download className="h-4 w-4" /> - <span className="sr-only">Download</span> - </FileListAction> - </FileListHeader> - </FileListItem> - <FileListItem className="p-3"> - <FileListHeader> - <FileListIcon /> - <FileListInfo> - <FileListName>report_variable_list.xlsx</FileListName> - </FileListInfo> - <FileListAction onClick={downloadReportVarList}> - <Download className="h-4 w-4" /> - <span className="sr-only">Download</span> - </FileListAction> - </FileListHeader> - </FileListItem> - </FileList> - </div> - <div> - <Label>Uploaded Template Files</Label> - <UploadedTempFiles - prevReportTemp={prevReportTemp} - updateReportTempList={() => - updateReportTempList(packageId, formId, setPrevReportTemp) - } - /> - </div> - - <div> - <Label>Report Template File Upload(.docx)</Label> - <Dropzone - maxSize={MAX_FILE_SIZE} - multiple={true} - accept={{ accept: [".docx"] }} - onDropAccepted={handleDropAccepted} - onDropRejected={handleDropRejected} - disabled={isUploading} - > - {({ maxSize }) => ( - <> - <DropzoneZone className="flex justify-center"> - <DropzoneInput /> - <div className="flex items-center gap-6"> - <DropzoneUploadIcon /> - <div className="grid gap-0.5"> - <DropzoneTitle>파일을 여기에 드롭하세요</DropzoneTitle> - <DropzoneDescription> - 또는 클릭하여 파일을 선택하세요. 최대 크기:{" "} - {maxSize ? prettyBytes(maxSize) : "무제한"} - </DropzoneDescription> - </div> - </div> - </DropzoneZone> - <Label className="text-xs text-muted-foreground"> - 여러 파일을 선택할 수 있습니다. - </Label> - </> - )} - </Dropzone> - </div> - - {selectedFiles.length > 0 && ( - <div className="grid gap-2"> - <div className="flex items-center justify-between"> - <h6 className="text-sm font-semibold"> - 선택된 파일 ({selectedFiles.length}) - </h6> - <Badge variant="secondary">{selectedFiles.length}개 파일</Badge> + <Tabs value={tabValue}> + <div className="flex justify-between items-center"> + <TabsList> + <TabsTrigger value="upload" onClick={() => setTabValue("upload")}> + Upload Template File + </TabsTrigger> + <TabsTrigger + value="uploaded" + onClick={() => setTabValue("uploaded")} + > + Uploaded Template File List + </TabsTrigger> + </TabsList> + <div className="flex flex-row gap-2"> + <TooltipProvider> + <Tooltip> + <TooltipTrigger asChild> + <Button + variant="ghost" + className="relative p-2" + aria-label="Template Sample Download" + onClick={downloadTempFile} + > + <Download /> + </Button> + </TooltipTrigger> + <TooltipContent> + <Label>Template Sample Download</Label> + </TooltipContent> + </Tooltip> + <Tooltip> + <TooltipTrigger asChild> + <Button + variant="ghost" + className="relative p-2" + aria-label="Variable List Download" + onClick={downloadReportVarList} + > + <BookDown /> + </Button> + </TooltipTrigger> + <TooltipContent> + <Label>Variable List Download</Label> + </TooltipContent> + </Tooltip> + </TooltipProvider> </div> - <ScrollArea> - <UploadFileItem - selectedFiles={selectedFiles} - removeFile={removeFile} - isUploading={isUploading} - /> - </ScrollArea> </div> - )} - - {isUploading && <UploadProgressBox uploadProgress={uploadProgress} />} - <DialogFooter> - <Button onClick={() => setOpen(false)}>취소</Button> - <Button disabled={selectedFiles.length === 0} onClick={submitData}> - 업로드 - </Button> - </DialogFooter> + <TabsContent value="upload"> + <TempUploadTab + packageId={packageId} + formId={formId} + uploaderType={uploaderType} + /> + </TabsContent> + <TabsContent value="uploaded"> + <TempUploadedTab packageId={packageId} formId={formId} /> + </TabsContent> + </Tabs> </DialogContent> </Dialog> ); }; +interface TempUploadTabProps { + packageId: number; + formId: number; + uploaderType: string; +} + +const TempUploadTab: FC<TempUploadTabProps> = ({ + packageId, + formId, + uploaderType, +}) => { + const { toast } = useToast(); + const [selectedFiles, setSelectedFiles] = useState<File[]>([]); + const [isUploading, setIsUploading] = useState(false); + const [uploadProgress, setUploadProgress] = useState(0); + + // 드롭존 - 파일 드랍 처리 + const handleDropAccepted = (acceptedFiles: File[]) => { + const newFiles = [...selectedFiles, ...acceptedFiles]; + setSelectedFiles(newFiles); + }; + + // 드롭존 - 파일 거부(에러) 처리 + const handleDropRejected = (fileRejections: any[]) => { + fileRejections.forEach((rejection) => { + toast({ + variant: "destructive", + title: "File Error", + description: `${rejection.file.name}: ${ + rejection.errors[0]?.message || "Upload failed" + }`, + }); + }); + }; + + // 파일 제거 핸들러 + const removeFile = (index: number) => { + const updatedFiles = [...selectedFiles]; + updatedFiles.splice(index, 1); + setSelectedFiles(updatedFiles); + }; + + const submitData = async () => { + setIsUploading(true); + setUploadProgress(0); + try { + const totalFiles = selectedFiles.length; + let successCount = 0; + + for (let i = 0; i < totalFiles; i++) { + const file = selectedFiles[i]; + + const formData = new FormData(); + formData.append("file", file); + formData.append("customFileName", file.name); + formData.append("uploaderType", uploaderType); + + await uploadReportTemp(packageId, formId, formData); + + successCount++; + setUploadProgress(Math.round((successCount / totalFiles) * 100)); + } + } catch (err) { + console.error(err); + toast({ + title: "Error", + description: "파일 업로드 중 오류가 발생했습니다.", + variant: "destructive", + }); + } finally { + setIsUploading(false); + setUploadProgress(0); + } + }; + + return ( + <> + <div> + <Label>Vendor Document Template File Upload(.docx)</Label> + <Dropzone + maxSize={MAX_FILE_SIZE} + multiple={true} + accept={{ accept: [".docx"] }} + onDropAccepted={handleDropAccepted} + onDropRejected={handleDropRejected} + disabled={isUploading} + > + {({ maxSize }) => ( + <> + <DropzoneZone className="flex justify-center"> + <DropzoneInput /> + <div className="flex items-center gap-6"> + <DropzoneUploadIcon /> + <div className="grid gap-0.5"> + <DropzoneTitle>파일을 여기에 드롭하세요</DropzoneTitle> + <DropzoneDescription> + 또는 클릭하여 파일을 선택하세요. 최대 크기:{" "} + {maxSize ? prettyBytes(maxSize) : "무제한"} + </DropzoneDescription> + </div> + </div> + </DropzoneZone> + <Label className="text-xs text-muted-foreground"> + 여러 파일을 선택할 수 있습니다. + </Label> + </> + )} + </Dropzone> + </div> + + {selectedFiles.length > 0 && ( + <div className="grid gap-2"> + <div className="flex items-center justify-between"> + <h6 className="text-sm font-semibold"> + 선택된 파일 ({selectedFiles.length}) + </h6> + <Badge variant="secondary">{selectedFiles.length}개 파일</Badge> + </div> + <ScrollArea> + <UploadFileItem + selectedFiles={selectedFiles} + removeFile={removeFile} + isUploading={isUploading} + /> + </ScrollArea> + </div> + )} + + {isUploading && <UploadProgressBox uploadProgress={uploadProgress} />} + <DialogFooter> + <Button disabled={selectedFiles.length === 0} onClick={submitData}> + 업로드 + </Button> + </DialogFooter> + </> + ); +}; + interface UploadFileItemProps { selectedFiles: File[]; removeFile: (index: number) => void; @@ -374,7 +410,7 @@ const UploadFileItem: FC<UploadFileItemProps> = ({ isUploading, }) => { return ( - <FileList className="max-h-[100px] gap-3"> + <FileList className="max-h-[150px] gap-3"> {selectedFiles.map((file, index) => ( <FileListItem key={index} className="p-3"> <FileListHeader> @@ -418,33 +454,11 @@ const UploadProgressBox: FC<{ uploadProgress: number }> = ({ ); }; -const generateFileName = ( - packageId: number, - formId: number, - originalFileName: string, - index: number, - totalFiles: number -) => { - // Get the file extension - const extension = originalFileName.split(".").pop() || ""; - - // Base name without extension - const baseName = `${packageId}_${formId}`; - - // For multiple files, add a suffix - if (totalFiles > 1) { - return `${baseName}_${index + 1}.${extension}`; - } - - // For a single file, no suffix needed - return `${baseName}.${extension}`; -}; - type UpdateReportTempList = ( packageId: number, formId: number, setPrevReportTemp: Dispatch<SetStateAction<VendorDataReportTemps[]>> -) => void; +) => Promise<void>; const updateReportTempList: UpdateReportTempList = async ( packageId, @@ -458,11 +472,13 @@ const updateReportTempList: UpdateReportTempList = async ( interface UploadedTempFiles { prevReportTemp: VendorDataReportTemps[]; updateReportTempList: () => void; + isLoading: boolean; } const UploadedTempFiles: FC<UploadedTempFiles> = ({ prevReportTemp, updateReportTempList, + isLoading, }) => { const { toast } = useToast(); @@ -484,7 +500,7 @@ const UploadedTempFiles: FC<UploadedTempFiles> = ({ toastMessage.success("Template File 다운로드 완료!"); } catch (err) { - console.error(err) + console.error(err); toast({ title: "Error", description: "Template File 다운로드 중 오류가 발생했습니다.", @@ -512,9 +528,17 @@ const UploadedTempFiles: FC<UploadedTempFiles> = ({ } }; + if (isLoading) { + return ( + <div className="min-h-[157px]"> + <Label>로딩 중...</Label> + </div> + ); + } + return ( - <ScrollArea> - <FileList className="max-h-[100px] gap-3"> + <ScrollArea className="min-h-[157px] max-h-[337px] overflow-auto"> + <FileList className="gap-3"> {prevReportTemp.map((c) => { const { fileName, filePath, id } = c; @@ -567,3 +591,36 @@ const UploadedTempFiles: FC<UploadedTempFiles> = ({ </ScrollArea> ); }; + +interface TempUploadedTabProps { + packageId: number; + formId: number; +} + +const TempUploadedTab: FC<TempUploadedTabProps> = ({ packageId, formId }) => { + const [prevReportTemp, setPrevReportTemp] = useState<VendorDataReportTemps[]>( + [] + ); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const getTempFiles = async () => { + await updateReportTempList(packageId, formId, setPrevReportTemp); + setIsLoading(false); + }; + + getTempFiles(); + }, [packageId, formId]); + return ( + <div> + <Label>Uploaded Template File List</Label> + <UploadedTempFiles + prevReportTemp={prevReportTemp} + updateReportTempList={() => + updateReportTempList(packageId, formId, setPrevReportTemp) + } + isLoading={isLoading} + /> + </div> + ); +}; |
