From 3e47b8275454452324e3b6a7607f2db361110b0e Mon Sep 17 00:00:00 2001
From: rlaks5757 <rlaks5757@gmail.com>
Date: Mon, 31 Mar 2025 17:23:48 +0900
Subject: Report -> Vendor Document 문구 수정, Upload Dialog Design Layout 변경
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../form-data-report-temp-upload-dialog.tsx        | 453 ++++++++++++---------
 1 file changed, 255 insertions(+), 198 deletions(-)

(limited to 'components/form-data/form-data-report-temp-upload-dialog.tsx')

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>
+  );
+};
-- 
cgit v1.2.3

