diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-28 06:12:09 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-28 06:12:09 +0000 |
| commit | f2c29e0023200b58d5e5c7a4c677a3c473c9bb8e (patch) | |
| tree | f6fcfb6e5dcddceab7529d7ca5fb0a4b44bd8798 /components/form-data/form-data-report-temp-upload-dialog.tsx | |
| parent | f3e640b666c57f133c229d2742fb214c586d21e4 (diff) | |
| parent | dbfc752cbbae6bded6040f4727ec7564d2a3b759 (diff) | |
기만프로 작업내역 머지
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 | 352 |
1 files changed, 308 insertions, 44 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 b646c3e6..69df704e 100644 --- a/components/form-data/form-data-report-temp-upload-dialog.tsx +++ b/components/form-data/form-data-report-temp-upload-dialog.tsx @@ -8,8 +8,11 @@ import React, { useEffect, } from "react"; import { useToast } from "@/hooks/use-toast"; +import { toast as toastMessage } from "sonner"; import prettyBytes from "pretty-bytes"; -import { X, Loader2 } from "lucide-react"; +import { X, Loader2, Download, Delete, Trash2 } from "lucide-react"; +import ExcelJS from "exceljs"; +import { saveAs } from "file-saver"; import { Badge } from "@/components/ui/badge"; import { Dialog, @@ -40,10 +43,28 @@ import { FileListItem, FileListName, } from "@/components/ui/file-list"; -import { getReportTempList, uploadReportTemp } from "@/lib/forms/services"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { + getReportTempList, + uploadReportTemp, + getReportTempFileData, + deleteReportTempFile, +} from "@/lib/forms/services"; import { VendorDataReportTemps } from "@/db/schema/vendorData"; +import { DataTableColumnJSON } from "./form-data-table-columns"; interface FormDataReportTempUploadDialogProps { + columnsJSON: DataTableColumnJSON[]; open: boolean; setOpen: Dispatch<SetStateAction<boolean>>; packageId: number; @@ -57,7 +78,15 @@ const MAX_FILE_SIZE = 3000000; export const FormDataReportTempUploadDialog: FC< FormDataReportTempUploadDialogProps -> = ({ open, setOpen, packageId, formId, uploaderType }) => { +> = ({ + columnsJSON, + open, + setOpen, + packageId, + formId, + formCode, + uploaderType, +}) => { const { toast } = useToast(); const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [isUploading, setIsUploading] = useState(false); @@ -131,55 +160,177 @@ export const FormDataReportTempUploadDialog: FC< } }; + const downloadTempFile = async () => { + try { + const { fileName, fileType, base64 } = await getReportTempFileData(); + + saveAs(`data:${fileType};base64,${base64}`, fileName); + + toastMessage.success("Report Sample File 다운로드 완료!"); + } catch (err) { + console.log(err); + toast({ + title: "Error", + description: "Sample File을 찾을 수가 없습니다.", + variant: "destructive", + }); + } + }; + + const downloadReportVarList = async () => { + try { + // Create a new workbook + const workbook = new ExcelJS.Workbook(); + + // 데이터 시트 생성 + const worksheet = workbook.addWorksheet("Data"); + + // 유효성 검사용 숨김 시트 생성 + const validationSheet = workbook.addWorksheet("ValidationData"); + validationSheet.state = "hidden"; // 시트 숨김 처리 + + // 1. 데이터 시트에 헤더 추가 + const headers = ["Table Column Label", "Report Variable"]; + worksheet.addRow(headers); + + // 헤더 스타일 적용 + const headerRow = worksheet.getRow(1); + headerRow.font = { bold: true }; + headerRow.alignment = { horizontal: "center" }; + headerRow.eachCell((cell) => { + cell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "FFCCCCCC" }, + }; + }); + + // 2. 데이터 행 추가 + columnsJSON.forEach((row) => { + const { displayLabel, label } = row; + + const labelConvert = label.replaceAll(" ", "_"); + + worksheet.addRow([displayLabel, labelConvert]); + }); + + // 3. 컬럼 너비 자동 조정 + headers.forEach((col, idx) => { + const column = worksheet.getColumn(idx + 1); + + // 최적 너비 계산 + let maxLength = col.length; + columnsJSON.forEach((row) => { + const valueKey = idx === 0 ? "displayLabel" : "label"; + + const value = row[valueKey]; + if (value !== undefined && value !== null) { + const valueLength = String(value).length; + if (valueLength > maxLength) { + maxLength = valueLength; + } + } + }); + + // 너비 설정 (최소 10, 최대 50) + column.width = Math.min(Math.max(maxLength + 2, 10), 50); + }); + + const buffer = await workbook.xlsx.writeBuffer(); + saveAs(new Blob([buffer]), `${formCode}_report_varible_list.xlsx`); + toastMessage.success("Report Varible List File 다운로드 완료!"); + } catch (err) { + console.log(err); + toast({ + title: "Error", + description: "Variable List 파일을 찾을 수가 없습니다.", + variant: "destructive", + }); + } + }; + return ( <Dialog open={open} onOpenChange={setOpen}> <DialogContent className="w-[600px]" style={{ maxWidth: "none" }}> <DialogHeader> <DialogTitle>Report Template Upload</DialogTitle> <DialogDescription> - 사용하시고자 하는 Report Template를 업로드 하여주시기 바랍니다. + 사용하시고자 하는 Report Template(.docx)를 업로드 하여주시기 + 바랍니다. </DialogDescription> </DialogHeader> - {/* {prevReportTemp.length > 0 && ( - <> - <Label>Prev Report Template</Label> - <ScrollArea className="max-h-[100px]"> - {prevReportTemp.map((c, i) => { - return <div key={i}>{i}</div>; - })} - </ScrollArea> - </> - )} */} - - <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> + <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> - </div> - </DropzoneZone> - <Label className="text-xs text-muted-foreground"> - 여러 파일을 선택할 수 있습니다. - </Label> - </> - )} - </Dropzone> + </DropzoneZone> + <Label className="text-xs text-muted-foreground"> + 여러 파일을 선택할 수 있습니다. + </Label> + </> + )} + </Dropzone> + </div> {selectedFiles.length > 0 && ( <div className="grid gap-2"> @@ -223,7 +374,7 @@ const UploadFileItem: FC<UploadFileItemProps> = ({ isUploading, }) => { return ( - <FileList className="max-h-[200px] gap-3"> + <FileList className="max-h-[100px] gap-3"> {selectedFiles.map((file, index) => ( <FileListItem key={index} className="p-3"> <FileListHeader> @@ -303,3 +454,116 @@ const updateReportTempList: UpdateReportTempList = async ( const tempList = await getReportTempList(packageId, formId); setPrevReportTemp(tempList); }; + +interface UploadedTempFiles { + prevReportTemp: VendorDataReportTemps[]; + updateReportTempList: () => void; +} + +const UploadedTempFiles: FC<UploadedTempFiles> = ({ + prevReportTemp, + updateReportTempList, +}) => { + const { toast } = useToast(); + + const downloadTempFile = async (fileName: string, filePath: string) => { + try { + const getTempFile = await fetch(filePath); + + if (getTempFile.ok) { + const blob = await getTempFile.blob(); + + saveAs(blob, fileName); + + toastMessage.success("Report 다운로드 완료!"); + } else { + const err = await getTempFile.json(); + console.error("에러:", err); + throw new Error(err.message); + } + + toastMessage.success("Template File 다운로드 완료!"); + } catch (err) { + console.error(err) + toast({ + title: "Error", + description: "Template File 다운로드 중 오류가 발생했습니다.", + variant: "destructive", + }); + } + }; + + const deleteTempFile = async (id: number) => { + try { + const { result, error } = await deleteReportTempFile(id); + + if (result) { + updateReportTempList(); + toastMessage.success("Template File 삭제 완료!"); + } else { + throw new Error(error); + } + } catch (err) { + toast({ + title: "Error", + description: "Template File 삭제 중 오류가 발생했습니다.", + variant: "destructive", + }); + } + }; + + return ( + <ScrollArea> + <FileList className="max-h-[100px] gap-3"> + {prevReportTemp.map((c) => { + const { fileName, filePath, id } = c; + + return ( + <AlertDialog key={id}> + <FileListItem className="p-3"> + <FileListHeader> + <FileListIcon /> + <FileListInfo> + <FileListName>{fileName}</FileListName> + </FileListInfo> + <FileListAction + onClick={() => { + downloadTempFile(fileName, filePath); + }} + > + <Download className="h-4 w-4" /> + <span className="sr-only">Download</span> + </FileListAction> + <AlertDialogTrigger asChild> + <FileListAction> + <Trash2 className="h-4 w-4" /> + <span className="sr-only">Delete</span> + </FileListAction> + </AlertDialogTrigger> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle> + Report Templete File({fileName})을 삭제하시겠습니까? + </AlertDialogTitle> + <AlertDialogDescription /> + </AlertDialogHeader> + <AlertDialogFooter> + <AlertDialogCancel>취소</AlertDialogCancel> + <AlertDialogAction + onClick={() => { + deleteTempFile(id); + }} + > + 삭제 + </AlertDialogAction> + </AlertDialogFooter> + </AlertDialogContent> + </FileListHeader> + </FileListItem> + </AlertDialog> + ); + })} + </FileList> + </ScrollArea> + ); +}; |
