summaryrefslogtreecommitdiff
path: root/components/form-data/form-data-report-temp-upload-dialog.tsx
diff options
context:
space:
mode:
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.tsx453
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>
+ );
+};