summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrlaks5757 <rlaks5757@gmail.com>2025-03-28 11:27:25 +0900
committerrlaks5757 <rlaks5757@gmail.com>2025-03-28 11:30:42 +0900
commitc4c3f12b4a6d1a3c09b797e1a160747cac8761e9 (patch)
tree8b376b5579ce7b5bc055d90d644a37295eb4c626
parent773918229ccb14c0d00798fbbf2b2be0130a8251 (diff)
template file download 개발 완료
-rw-r--r--components/form-data/form-data-report-batch-dialog.tsx13
-rw-r--r--components/form-data/form-data-report-dialog.tsx139
-rw-r--r--components/form-data/form-data-report-temp-upload-dialog.tsx340
-rw-r--r--components/form-data/form-data-table.tsx47
-rw-r--r--db/schema/vendorData.ts19
-rw-r--r--lib/forms/services.ts53
-rw-r--r--public/vendorFormReportSample/sample_template_file.docxbin0 -> 24773 bytes
7 files changed, 399 insertions, 212 deletions
diff --git a/components/form-data/form-data-report-batch-dialog.tsx b/components/form-data/form-data-report-batch-dialog.tsx
index 614f890e..6c690363 100644
--- a/components/form-data/form-data-report-batch-dialog.tsx
+++ b/components/form-data/form-data-report-batch-dialog.tsx
@@ -8,8 +8,10 @@ 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 { saveAs } from 'file-saver';
import { Badge } from "@/components/ui/badge";
import {
Dialog,
@@ -159,15 +161,14 @@ export const FormDataReportBatchDialog: FC<FormDataReportBatchDialogProps> = ({
if (reqeustCreateReport.ok) {
const blob = await reqeustCreateReport.blob();
- const url = window.URL.createObjectURL(blob);
- const a = document.createElement("a");
- a.href = url;
- a.download = `${formCode}.pdf`;
- a.click();
- window.URL.revokeObjectURL(url);
+
+ saveAs(blob, `${formCode}.pdf`);
+
+ toastMessage.success("Report 다운로드 완료!")
} else {
const err = await reqeustCreateReport.json();
console.error("에러:", err);
+ throw new Error(err.message)
}
} catch (err) {
console.error(err);
diff --git a/components/form-data/form-data-report-dialog.tsx b/components/form-data/form-data-report-dialog.tsx
index deb0873b..e28b4345 100644
--- a/components/form-data/form-data-report-dialog.tsx
+++ b/components/form-data/form-data-report-dialog.tsx
@@ -8,11 +8,10 @@ import React, {
useEffect,
useRef,
} from "react";
-import { WebViewerInstance, Core } from "@pdftron/webviewer";
-import { useToast } from "@/hooks/use-toast";
-import prettyBytes from "pretty-bytes";
-import { X, Loader2 } from "lucide-react";
-import { Badge } from "@/components/ui/badge";
+import { WebViewerInstance } from "@pdftron/webviewer";
+import { Loader2 } from "lucide-react";
+import { saveAs } from "file-saver";
+import { toast } from "sonner";
import {
Dialog,
DialogContent,
@@ -25,11 +24,11 @@ import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
- SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
+
import { Button } from "@/components/ui/button";
import { getReportTempList } from "@/lib/forms/services";
import { DataTableColumnJSON } from "./form-data-table-columns";
@@ -58,7 +57,9 @@ export const FormDataReportDialog: FC<FormDataReportDialogProps> = ({
setReportData,
packageId,
formId,
+ formCode,
}) => {
+
const [tempList, setTempList] = useState<tempFile[]>([]);
const [selectTemp, setSelectTemp] = useState<string>("");
const [instance, setInstance] = useState<null | WebViewerInstance>(null);
@@ -92,46 +93,9 @@ export const FormDataReportDialog: FC<FormDataReportDialogProps> = ({
// },
});
- const blob = new Blob([fileData], {
- type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
- });
+ saveAs(new Blob([fileData]), fileName);
- const link = document.createElement("a");
- link.href = URL.createObjectURL(blob);
- link.download = fileName;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
-
- // const allTabs = UI.TabManager.getAllTabs() as {
- // id: number;
- // src: Core.Document;
- // }[];
-
- // for (const tab of allTabs) {
- // // await UI.TabManager.setActiveTab(tab.id);
- // await activateTabAndWaitForLoad(instance, tab.id);
- // const tabDoc = tab.src;
- // const fileName = tabDoc.getFilename();
-
- // const fileData = await tabDoc.getFileData({
- // includeAnnotations: true,
- // });
-
- // console.log({ fileData });
-
- // const blob = new Blob([fileData], {
- // type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
- // });
-
- // // 다운로드
- // // const link = document.createElement("a");
- // // link.href = URL.createObjectURL(blob);
- // // link.download = fileName;
- // // document.body.appendChild(link);
- // // link.click();
- // // document.body.removeChild(link);
- // }
+ toast.success("Report 다운로드 완료!");
}
};
@@ -175,6 +139,7 @@ export const FormDataReportDialog: FC<FormDataReportDialogProps> = ({
instance={instance}
setInstance={setInstance}
setFileLoading={setFileLoading}
+ formCode={formCode}
/>
</div>
@@ -195,6 +160,7 @@ interface ReportWebViewerProps {
instance: null | WebViewerInstance;
setInstance: Dispatch<SetStateAction<WebViewerInstance | null>>;
setFileLoading: Dispatch<SetStateAction<boolean>>;
+ formCode: string;
}
const ReportWebViewer: FC<ReportWebViewerProps> = ({
@@ -204,6 +170,7 @@ const ReportWebViewer: FC<ReportWebViewerProps> = ({
instance,
setInstance,
setFileLoading,
+ formCode,
}) => {
const [viwerLoading, setViewerLoading] = useState<boolean>(true);
const viewer = useRef<HTMLDivElement>(null);
@@ -234,12 +201,6 @@ const ReportWebViewer: FC<ReportWebViewerProps> = ({
viewer.current as HTMLDivElement
).then(async (instance: WebViewerInstance) => {
setInstance(instance);
- // //Tab 메뉴 사용 필요시 활성화
- // instance.UI.enableFeatures([instance.UI.Feature.MultiTab]);
- // instance.UI.disableElements([
- // "addTabButton",
- // "multiTabsEmptyPage",
- // ]);
setViewerLoading(false);
});
});
@@ -262,9 +223,10 @@ const ReportWebViewer: FC<ReportWebViewerProps> = ({
instance,
reportDatas,
reportTempPath,
- setFileLoading
+ setFileLoading,
+ formCode
);
- }, [reportTempPath, reportDatas, instance, columnsJSON]);
+ }, [reportTempPath, reportDatas, instance, columnsJSON, formCode]);
return (
<div ref={viewer} className="h-[100%]">
@@ -319,7 +281,8 @@ type ImportReportData = (
instance: null | WebViewerInstance,
reportDatas: ReportData[],
reportTempPath: string,
- setFileLoading: Dispatch<SetStateAction<boolean>>
+ setFileLoading: Dispatch<SetStateAction<boolean>>,
+ formCode: string
) => void;
const importReportData: ImportReportData = async (
@@ -327,7 +290,8 @@ const importReportData: ImportReportData = async (
instance,
reportDatas,
reportTempPath,
- setFileLoading
+ setFileLoading,
+ formCode
) => {
setFileLoading(true);
try {
@@ -352,12 +316,13 @@ const importReportData: ImportReportData = async (
});
const doc = await createDocument(reportFileBlob, {
+ filename: `${formCode}_report.docx`,
extension: "docx",
});
await doc.applyTemplateValues(reportValueMapping);
- documentViewer.loadDocument(doc, {
+ documentViewer.loadDocument(doc, {
extension: "docx",
enableOfficeEditing: true,
officeOptions: {
@@ -373,68 +338,6 @@ const importReportData: ImportReportData = async (
}
};
-const importReportDataTab: ImportReportData = async (
- columnJSON,
- instance,
- reportDatas,
- reportTempPath,
- setFileLoading
-) => {
- setFileLoading(true);
- try {
- if (instance && reportDatas.length > 0 && reportTempPath.length > 0) {
- const { UI, Core } = instance;
- const { createDocument } = Core;
-
- const getFileData = await fetch(reportTempPath);
- const reportFileBlob = await getFileData.blob();
-
- const prevTab = UI.TabManager.getAllTabs();
-
- (prevTab as object[] as { id: number }[]).forEach((c) => {
- const { id } = c;
- UI.TabManager.deleteTab(id);
- });
-
- const fileOptions = reportDatas.map((c) => {
- const { tagNumber } = c;
-
- const options = {
- filename: `${tagNumber}_report.docx`,
- };
-
- return { options, reportData: c };
- });
-
- const tabIds = [];
-
- for (const fileOption of fileOptions) {
- let doc = await createDocument(reportFileBlob, {
- ...fileOption.options,
- extension: "docx",
- });
-
- await doc.applyTemplateValues(
- stringifyAllValues(fileOption.reportData)
- );
-
- const tab = await UI.TabManager.addTab(doc, {
- ...fileOption.options,
- });
-
- tabIds.push(tab); // 탭 ID 저장
- }
-
- if (tabIds.length > 0) {
- await UI.TabManager.setActiveTab(tabIds[0]);
- }
- }
- } catch (err) {
- } finally {
- setFileLoading(false);
- }
-};
-
type UpdateReportTempList = (
packageId: number,
formId: number,
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 413c1e51..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, Download } 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,29 +160,106 @@ 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(docx File)를 업로드 하여주시기
+ 사용하시고자 하는 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>
- </>
- )} */}
- <div
- // className="flex flex-col gap-2"
- >
+ <div>
<Label>Sample Template Download</Label>
<FileList className="max-h-[200px] gap-3">
@@ -163,10 +269,19 @@ export const FormDataReportTempUploadDialog: FC<
<FileListInfo>
<FileListName>sample_template_file.docx</FileListName>
</FileListInfo>
- <FileListAction
- // onClick={() => removeFile(index)}
- // disabled={isUploading}
- >
+ <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>
@@ -174,36 +289,48 @@ export const FormDataReportTempUploadDialog: FC<
</FileListItem>
</FileList>
</div>
+ <div>
+ <Label>Uploaded Template Files</Label>
+ <UploadedTempFiles
+ prevReportTemp={prevReportTemp}
+ updateReportTempList={() =>
+ updateReportTempList(packageId, formId, setPrevReportTemp)
+ }
+ />
+ </div>
- <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>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">
@@ -247,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>
@@ -327,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>
+ );
+};
diff --git a/components/form-data/form-data-table.tsx b/components/form-data/form-data-table.tsx
index 50c4f267..823416c1 100644
--- a/components/form-data/form-data-table.tsx
+++ b/components/form-data/form-data-table.tsx
@@ -11,19 +11,22 @@ import {
DataTableColumnJSON,
ColumnType,
} from "./form-data-table-columns";
-
import type { DataTableAdvancedFilterField } from "@/types/table";
import { Button } from "../ui/button";
import { Download, Loader, Save, Upload } from "lucide-react";
import { toast } from "sonner";
import { syncMissingTags, updateFormDataInDB } from "@/lib/forms/services";
import { UpdateTagSheet } from "./update-form-sheet";
-
import ExcelJS from "exceljs";
import { saveAs } from "file-saver";
import { FormDataReportTempUploadDialog } from "./form-data-report-temp-upload-dialog";
import { FormDataReportDialog } from "./form-data-report-dialog";
import { FormDataReportBatchDialog } from "./form-data-report-batch-dialog";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
interface GenericData {
[key: string]: any;
@@ -526,20 +529,29 @@ export default function DynamicTable({
{/* 버튼 그룹 */}
<div className="flex items-center gap-2">
{/* 태그 불러오기 버튼 */}
- <Button
- variant="default"
- size="sm"
- onClick={() => setBatchDownDialog(true)}
- >
- Report Download
- </Button>
- <Button
- variant="default"
- size="sm"
- onClick={() => setTempUpDialog(true)}
- >
- Template Upload
- </Button>
+ <Popover>
+ <PopoverTrigger asChild>
+ <Button variant="default" size="sm">
+ Report
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent className="flex flex-row gap-2">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setTempUpDialog(true)}
+ >
+ Template Upload
+ </Button>
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setBatchDownDialog(true)}
+ >
+ Report Download
+ </Button>
+ </PopoverContent>
+ </Popover>
<Button
variant="default"
size="sm"
@@ -611,6 +623,7 @@ export default function DynamicTable({
/>
{tempUpDialog && (
<FormDataReportTempUploadDialog
+ columnsJSON={columnsJSON}
open={tempUpDialog}
setOpen={setTempUpDialog}
packageId={contractItemId}
@@ -645,5 +658,3 @@ export default function DynamicTable({
</>
);
}
-
-
diff --git a/db/schema/vendorData.ts b/db/schema/vendorData.ts
index 2739e8eb..01a10b7e 100644
--- a/db/schema/vendorData.ts
+++ b/db/schema/vendorData.ts
@@ -238,19 +238,11 @@ import {
createdAt: timestamp("created_at", { withTimezone: true }),
updatedAt: timestamp("updated_at", { withTimezone: true }),
-<<<<<<< HEAD
});
export type ViewTagSubfields = typeof viewTagSubfields.$inferSelect;
export const vendorDataReportTemps = pgTable("vendor_data_report_temps", {
-=======
-})
-
-export type ViewTagSubfields = typeof viewTagSubfields.$inferSelect
-
-export const vendorDataReportTemps = pgTable("vendor_data_report_temps", {
->>>>>>> dev
id: serial("id").primaryKey(),
contractItemId: integer("contract_item_id")
.notNull()
@@ -261,7 +253,6 @@ export const vendorDataReportTemps = pgTable("vendor_data_report_temps", {
fileName: varchar("file_name", { length: 255 }).notNull(),
filePath: varchar("file_path", { length: 1024 }).notNull(),
createdAt: timestamp("created_at", { withTimezone: true })
-<<<<<<< HEAD
.defaultNow()
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true })
@@ -271,13 +262,3 @@ export const vendorDataReportTemps = pgTable("vendor_data_report_temps", {
export type VendorDataReportTemps = typeof vendorDataReportTemps.$inferSelect;
-=======
- .defaultNow()
- .notNull(),
- updatedAt: timestamp("updated_at", { withTimezone: true })
- .defaultNow()
- .notNull(),
- });
-
- export type VendorDataReportTemps = typeof vendorDataReportTemps.$inferSelect;
->>>>>>> dev
diff --git a/lib/forms/services.ts b/lib/forms/services.ts
index a1bbf003..22f10466 100644
--- a/lib/forms/services.ts
+++ b/lib/forms/services.ts
@@ -807,7 +807,7 @@ export async function uploadReportTemp(
}
}
-export const getOrigin = async ():Promise<string> => {
+export const getOrigin = async (): Promise<string> => {
const headersList = await headers();
const host = headersList.get("host");
const proto = headersList.get("x-forwarded-proto") || "http"; // 기본값은 http
@@ -815,3 +815,54 @@ export const getOrigin = async ():Promise<string> => {
return origin;
};
+
+export const getReportTempFileData = async (): Promise<{
+ fileName: string;
+ fileType: string;
+ base64: string;
+}> => {
+ const fileName = "sample_template_file.docx";
+
+ const tempFile = await fs.readFile(
+ `public/vendorFormReportSample/${fileName}`
+ );
+
+ return {
+ fileName,
+ fileType:
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ base64: tempFile.toString("base64"),
+ };
+};
+
+type deleteReportTempFile = (id: number) => Promise<{
+ result: boolean;
+ error?: any;
+}>;
+
+export const deleteReportTempFile: deleteReportTempFile = async (id) => {
+ try {
+ return db.transaction(async (tx) => {
+ const [targetTempFile] = await tx
+ .select()
+ .from(vendorDataReportTemps)
+ .where(eq(vendorDataReportTemps.id, id));
+
+ if (!targetTempFile) {
+ throw new Error("해당 Template File을 찾을 수 없습니다.");
+ }
+
+ await tx
+ .delete(vendorDataReportTemps)
+ .where(eq(vendorDataReportTemps.id, id));
+
+ const { filePath } = targetTempFile;
+
+ await fs.unlink("public" + filePath);
+
+ return { result: true };
+ });
+ } catch (err) {
+ return { result: false, error: (err as Error).message };
+ }
+};
diff --git a/public/vendorFormReportSample/sample_template_file.docx b/public/vendorFormReportSample/sample_template_file.docx
new file mode 100644
index 00000000..0e338eb8
--- /dev/null
+++ b/public/vendorFormReportSample/sample_template_file.docx
Binary files differ