From 875434239e1e656c60540e996d6bba00953267b0 Mon Sep 17 00:00:00 2001 From: swcho Date: Mon, 1 Sep 2025 10:21:05 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20-=20=EB=8F=84=EC=8B=9C=EC=B9=A8?= =?UTF-8?q?=EC=88=98=EC=98=88=EB=B0=A9=EC=82=AC=EC=97=85=20=EC=97=91?= =?UTF-8?q?=EC=85=80=20=EB=B3=B4=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20-=20=EB=85=B8=ED=9B=84=20=ED=95=98=EC=88=98?= =?UTF-8?q?=EA=B4=80=EB=A1=9C=20=EC=A0=95=EB=B9=84=EC=82=AC=EC=97=85=20?= =?UTF-8?q?=EC=97=91=EC=85=80=20=EB=B3=B4=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(=EC=97=91=EC=85=80=20=EC=96=91=EC=8B=9D?= =?UTF-8?q?=EC=9D=B4=20=ED=99=95=EC=A0=95=20=EC=95=88=EB=8F=BC=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=ED=95=84=EC=9A=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oper/floodPre/headMakeFloodPre.tsx | 76 + .../oper/floodPre/inputTableBody.tsx | 200 ++ .../oper/floodPre/tableModal.tsx | 31 +- .../(with-base-layout)/oper/oldPp/headList.ts | 1711 +++++++++++++++++ .../(with-base-layout)/oper/oldPp/page.tsx | 22 +- .../oper/oldPp/tableModal.tsx | 465 +++++ 6 files changed, 2494 insertions(+), 11 deletions(-) create mode 100644 apps/sn/app/(with-base-layout)/oper/floodPre/headMakeFloodPre.tsx create mode 100644 apps/sn/app/(with-base-layout)/oper/floodPre/inputTableBody.tsx create mode 100644 apps/sn/app/(with-base-layout)/oper/oldPp/headList.ts create mode 100644 apps/sn/app/(with-base-layout)/oper/oldPp/tableModal.tsx diff --git a/apps/sn/app/(with-base-layout)/oper/floodPre/headMakeFloodPre.tsx b/apps/sn/app/(with-base-layout)/oper/floodPre/headMakeFloodPre.tsx new file mode 100644 index 000000000..f16178003 --- /dev/null +++ b/apps/sn/app/(with-base-layout)/oper/floodPre/headMakeFloodPre.tsx @@ -0,0 +1,76 @@ +import { TableHead, TableRow } from "@common/components/ui"; + + +export type TableUpperProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; + edit?: boolean; + visible?: boolean; + rowSpan?: number; + colSpan?: number; + type?: string; + selectData?: string; // type 이 seelct 일경우 selectarray 정하기 위해 + row?: number; + mergeCell?: boolean // 셀을 합칠경우 + maxLength?: number + readonly?: boolean +}; +type chHeadType = { + headNm: string; + text: string; + length: number; + rows?: number; +}; + +export const HeadMakeFloodPre = ( + upHeadList: TableUpperProps[], + makeHeadNm: chHeadType[] +) => { + return ( + <> + + {upHeadList + .filter((data) => data.visible != false) + .map((head: TableUpperProps) => + !head.upSequnce ? ( + + {head.title} + + ) : head.upSequnce === 1 ? ( + ch.headNm === head.upName)?.length + } + > + {makeHeadNm?.find((ch) => ch.headNm === head.upName)?.text} + + ) : null + )} + + + + {upHeadList + .filter((data) => data.visible != false) + .map((head: TableUpperProps) => + makeHeadNm?.find((ch) => ch.headNm === head.upName) ? ( + + {head.title} + + ) : null + )} + + + ); +}; \ No newline at end of file diff --git a/apps/sn/app/(with-base-layout)/oper/floodPre/inputTableBody.tsx b/apps/sn/app/(with-base-layout)/oper/floodPre/inputTableBody.tsx new file mode 100644 index 000000000..0058b47d1 --- /dev/null +++ b/apps/sn/app/(with-base-layout)/oper/floodPre/inputTableBody.tsx @@ -0,0 +1,200 @@ +import type { TableUpperProps } from "./headMakeFloodPre"; +import { Input, TableCell, TableRow } from "@common/components/ui"; + + +// 숫자입력시 , 삽입 +const formatCommaNumber = ( + value: number | string | undefined | null +): string => { + if (value === undefined || value === null || value === "") return ""; + const num = Number(value); + // 숫자로 변환할 수 없는 값이 들어올 경우 빈 문자열을 반환하여 예상치 못한 "NaN" 출력을 방지합니다. + if (isNaN(num)) { + return ""; + } + return num.toLocaleString(); +}; + +// onchange 텍스트를 숫자로 and ,제거 +const handleCommaNumberChange = ( + e: React.ChangeEvent, + onChange: (value: number | "") => void +) => { + const raw = e.target.value.replace(/,/g, ""); + if (!/^\d*$/.test(raw)) return; + onChange(raw === "" ? "" : Number(raw)); +}; + +const handleChange = ( + rowIndex: number, + field: any, + value: string, + setInputData: (data: any) => void +) => { + if (Number(value) < 0 || !Number(value)) { + value = "0"; + } + setInputData((prevData: any) => + prevData?.map((row: any, index: number) => { + if (index === rowIndex) { + return { + ...row, + [field]: Number(value.replace(/,/g, "")), + }; + } + return row; + }) + ); +}; + +export const InputTableBody = ( + chgRes: any[] | [], + headList: TableUpperProps[], + setInputData: (data: any[]) => void, + isEditable: boolean = true +) => { + // mergeCell 처리용 + const getRowSpan = (data: any[], key: string, rowIndex: number) => { + if (rowIndex > 0 && data[rowIndex - 1][key] && String(data[rowIndex][key]).trim() === "") { + return 0; // 이전과 같으면 셀을 그리지 않음 + } + let span = 1; + for (let i = rowIndex + 1; i < data.length; i++) { + if (data[i][key] === data[rowIndex][key]) span++; + else break; + } + return span; + }; + return ( + <> + {chgRes.map((data, dataIndex) => { + // 소계/총합계 행 판별 + const isSubTotal = data["용수명"] === "소계"; + const isTotal = data["구분"] === 0; + + const bgClass = isTotal + ? "bg-[#d5e1eb] font-bold" + : isSubTotal + ? "bg-[#e6eef5] font-semibold" + : ""; + + return ( + + {headList.map((head, headIndex) => { + // console.log('head, head.id', head, head.id); + const isTextColumn = head.id === "__EMPTY_3"; + const textAlignClass = isTextColumn + ? "text-center" + : "text-right"; + + // // 총합계 행이면 "구분"에서 colSpan=2로 병합 + // if ( + // (data["구분"] === 0 || data["용수명"] === 0) && + // head.id === "구분" + // ) { + // return ( + // + // 총합계 + // + // ); + // } + // // 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음 + // if ( + // (data["구분"] === 0 || data["용수명"] === 0) && + // head.id === "용수명" + // ) { + // return null; + // } + + // // 총합계 행의 나머지 셀에도 색상 적용 + // if (data["구분"] === 0 || data["용수명"] === 0) { + // return ( + // + // {Number(data[head.id]) + // ? formatCommaNumber(data[head.id as keyof any]) + // : (data[head.id as keyof any] ?? "")} + // + // ); + // } + // mergeCell 처리 + if (head.mergeCell === true) { + const rowSpan = getRowSpan(chgRes, head.id, dataIndex); + if (rowSpan === 0) return null; + return ( + + {Number(data[head.id]) + ? formatCommaNumber(data[head.id as keyof any]) + : (data[head.id as keyof any] ?? "")} + + ); + } + // 기본 셀 + // return head.edit === true && data.edit !== false ? ( + return isEditable && head.edit === true && data.edit !== false ? ( + + + handleCommaNumberChange(e, (val) => + handleChange( + dataIndex, + head.id, + String(val), + setInputData + ) + ) + } + value={formatCommaNumber(data[head.id as keyof any]) ?? ""} + maxLength={12} + /> + + ) : ( + +
+ {Number(data[head.id]) + ? formatCommaNumber(data[head.id as keyof any]) + : (data[head.id as keyof any] ?? "")} +
+
+ ); + })} +
+ ); + })} + {chgRes.length === 0 && ( + + + 조회된 결과가 없습니다. + + + )} + + ); +}; diff --git a/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx b/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx index 80cd94a0e..1b38d3290 100644 --- a/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx +++ b/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx @@ -2,7 +2,10 @@ import { useState, useEffect } from 'react' import LoadingOverlay from '@/components/ui/LoadingBar' import { UITab, UiTable } from '@common/business_components/ui' +import { Table, TableBody, TableHeader } from "@common/components/ui"; import * as XLSX from 'xlsx'; +import { HeadMakeFloodPre } from './headMakeFloodPre'; +import { InputTableBody } from './inputTableBody'; const TABS = [ { val: "management", text: "관리SHEET" }, @@ -19,7 +22,7 @@ const TableModal: React.FC = ({ fileNm, atflId }) => { const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "management"); const [managementTableHeader, setManagementTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더 const [managementTableData, setManagementTableData] = useState([{}]) //테이블 데이터 - const [resultTableHeader, setResultTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더 + const [resultTableHeader, setResultTableHeader] = useState<{ id: string; title: string, mergeCell?: boolean }[]>([]) //테이블 헤더 const [resultTableData, setResultTableData] = useState([{}]) //테이블 데이터 const [isLoading, setIsLoading] = useState(false) //로딩 상태 @@ -53,13 +56,12 @@ const TableModal: React.FC = ({ fileNm, atflId }) => { // 관리 SHEET const managementHeader = jsonDataBySheet['관리SHEET']?.[7] || []; - // 1번째~47번째, 74번째~80번째 데이터만 필터링 const filteredManagementHeader = Object.entries(managementHeader) .filter(([key, value]) => { const match = key.match(/^__EMPTY_(\d+)$/); if (match) { const index = parseInt(match[1] ?? '', 10); - return (index > 0 && index < 47) || (index > 73); + return (0 < index && index < 47) || (73 < index); } return false; }) @@ -81,17 +83,20 @@ const TableModal: React.FC = ({ fileNm, atflId }) => { // 운영관리현황 점검결과 const resultHeader = jsonDataBySheet['운영관리현황 점검결과']?.[2] || []; - const dynamicInspectionHeader = Object.entries(resultHeader) + const dynamicResultHeader = Object.entries(resultHeader) .map(([key, value]) => ({ id: key, - title: String(value).replace(/\r\n/g, '\n') + title: String(value).replace(/\r\n/g, '\n'), + mergeCell: false, + ...(key === '__EMPTY_3' && { mergeCell: true }) // __EMPTY_3인 경우 mergeCell: true 추가 })); - dynamicInspectionHeader.splice(1, 0, { + dynamicResultHeader.splice(1, 0, { id: "__EMPTY_2", - title: '' + title: '', + mergeCell: false }); - setResultTableHeader(dynamicInspectionHeader); - console.log('resultTableHeader', dynamicInspectionHeader); + setResultTableHeader(dynamicResultHeader); + console.log('resultTableHeader', dynamicResultHeader); const resultData = Array.isArray(jsonDataBySheet['운영관리현황 점검결과']) ? jsonDataBySheet['운영관리현황 점검결과'].slice(3) @@ -238,6 +243,14 @@ const TableModal: React.FC = ({ fileNm, atflId }) => { cellClick={undefined} onPageChange={() => { }} /> + {/* + + {HeadMakeFloodPre(resultTableHeader, [])} + + + {InputTableBody(resultTableData ?? [], resultTableHeader, setResultTableData, false)} + +
*/} )} diff --git a/apps/sn/app/(with-base-layout)/oper/oldPp/headList.ts b/apps/sn/app/(with-base-layout)/oper/oldPp/headList.ts new file mode 100644 index 000000000..516302720 --- /dev/null +++ b/apps/sn/app/(with-base-layout)/oper/oldPp/headList.ts @@ -0,0 +1,1711 @@ +import { z } from "zod"; + +export const oldPpHeadListType = z.array( + z.object({ + id: z.string(), + title: z.string(), + visible: z.boolean(), + }) +); + +type CommonTableProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; + row?: number; + rowSpan?: number; + colSpan?: number; + visible?: boolean; +}; + +export const oldPpTable2HeadList: z.infer = [ + { + id: "rowNumber", + title: "일련번호", + visible: true, + }, + { + id: "__EMPTY", + title: "연번", + visible: true, + }, + { + id: "__EMPTY_1", + title: "차수", + visible: true, + }, + { + id: "__EMPTY_2", + title: "정밀조사 사업코드", + visible: true, + }, + { + id: "__EMPTY_3", + title: "시도", + visible: true, + }, + { + id: "__EMPTY_4", + title: "시군구", + visible: true, + }, + { + id: "__EMPTY_5", + title: "사업명", + visible: true, + }, + { + id: "__EMPTY_6", + title: "착수", + visible: true, + }, + { + id: "__EMPTY_7", + title: "준공", + visible: true, + }, + { + id: "__EMPTY_8", + title: "내역사업명", + visible: true, + }, + { + id: "__EMPTY_9", + title: "물량\r\n(결산자료)", + visible: true, + }, + { + id: "__EMPTY_0", + title: "실제조사물량\r\n(보고서,실적보고)", + visible: true, + }, + { + id: "__EMPTY_10", + title: "계", + visible: true, + }, + { + id: "__EMPTY_11", + title: "국고", + visible: true, + }, + { + id: "__EMPTY_12", + title: "지방비", + visible: true, + }, + { + id: "__EMPTY_13", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_14", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_15", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_16", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_17", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_18", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_19", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_20", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_21", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_22", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_23", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_24", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_25", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_26", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_27", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_28", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_29", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_30", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_31", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_32", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_33", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_34", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_35", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_36", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_37", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_38", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_39", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_40", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_41", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_42", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_43", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_44", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_45", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_46", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_47", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_48", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_49", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_50", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_51", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_52", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_53", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_54", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_55", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_56", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_57", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_58", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_59", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_60", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_61", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_62", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_63", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_64", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_65", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_66", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_67", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_68", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_69", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_70", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_71", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_72", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_73", + title: " 총사업비 ", + visible: true, + }, + { + id: "__EMPTY_74", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_75", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_76", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_77", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_78", + title: " 지방비 ", + visible: true, + }, +]; + +export const oldPpTable2NodeList: CommonTableProps[] = [ + { + id: "rowNumber", + title: "일련번호", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY", + title: "연번", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_1", + title: "차수", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_2", + title: "정밀조사 사업코드", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_3", + title: "시도", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_4", + title: "시군구", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_5", + title: "사업명", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_6", + title: "착수", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_7", + title: "준공", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_8", + title: "내역사업명", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_9", + title: "물량\r\n(결산자료)", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_0", + title: "실제조사물량\r\n(보고서,실적보고)", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_10", + title: "총계", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "2__EMPTY_10", + title: "계", + upName: "__EMPTY_10", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_11", + title: "국고", + upName: "__EMPTY_10", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_12", + title: "지방비", + upName: "__EMPTY_10", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_13", + title: " 2015년 ", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_13", + title: " 총사업비 ", + upName: "__EMPTY_13", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_14", + title: " 국고 ", + upName: "__EMPTY_13", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_15", + title: " 지방비 ", + upName: "__EMPTY_13", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_16", + title: " 계 ", + upName: "__EMPTY_13", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_17", + title: " 국고 ", + upName: "__EMPTY_13", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_18", + title: " 지방비 ", + upName: "__EMPTY_13", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + + { + id: "__EMPTY_19", + title: " 2016년 ", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_19", + title: " 총사업비 ", + upName: "__EMPTY_19", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_20", + title: " 국고 ", + upName: "__EMPTY_19", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_21", + title: " 지방비 ", + upName: "__EMPTY_19", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_22", + title: " 계 ", + upName: "__EMPTY_19", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_23", + title: " 국고 ", + upName: "__EMPTY_19", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_24", + title: " 지방비 ", + upName: "__EMPTY_19", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_25", + title: "2017년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_25", + title: " 총사업비 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_26", + title: " 국고 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_27", + title: " 지방비 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_28", + title: " 계 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_29", + title: " 국고 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_30", + title: " 지방비 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_31", + title: "2018년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_31", + title: " 총사업비 ", + upName: "__EMPTY_31", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_32", + title: " 국고 ", + upName: "__EMPTY_25", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_33", + title: " 지방비 ", + upName: "__EMPTY_31", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_34", + title: " 계 ", + upName: "__EMPTY_31", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_35", + title: " 국고 ", + upName: "__EMPTY_31", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_36", + title: " 지방비 ", + upName: "__EMPTY_31", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_37", + title: "2019년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_37", + title: " 총사업비 ", + upName: "__EMPTY_43", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_38", + title: " 국고 ", + upName: "__EMPTY_43", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_39", + title: " 지방비 ", + upName: "__EMPTY_43", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_40", + title: " 계 ", + upName: "__EMPTY_43", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_41", + title: " 국고 ", + upName: "__EMPTY_43", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_42", + title: " 지방비 ", + upName: "__EMPTY_43", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_43", + title: "2020년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_43", + title: " 총사업비 ", + upName: "__EMPTY_49", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_44", + title: " 국고 ", + upName: "__EMPTY_49", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_45", + title: " 지방비 ", + upName: "__EMPTY_49", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_46", + title: " 계 ", + upName: "__EMPTY_49", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_47", + title: " 국고 ", + upName: "__EMPTY_49", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_48", + title: " 지방비 ", + upName: "__EMPTY_49", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_49", + title: "2021년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_49", + title: " 총사업비 ", + upName: "__EMPTY_55", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_50", + title: " 국고 ", + upName: "__EMPTY_55", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_51", + title: " 지방비 ", + upName: "__EMPTY_55", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_52", + title: " 계 ", + upName: "__EMPTY_55", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_53", + title: " 국고 ", + upName: "__EMPTY_55", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_54", + title: " 지방비 ", + upName: "__EMPTY_55", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_55", + title: "2022년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_55", + title: " 총사업비 ", + upName: "__EMPTY_61", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_56", + title: " 국고 ", + upName: "__EMPTY_61", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_57", + title: " 지방비 ", + upName: "__EMPTY_61", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_58", + title: " 계 ", + upName: "__EMPTY_61", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_59", + title: " 국고 ", + upName: "__EMPTY_61", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_60", + title: " 지방비 ", + upName: "__EMPTY_61", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_61", + title: "2023년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_61", + title: " 총사업비 ", + upName: "__EMPTY_67", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_62", + title: " 국고 ", + upName: "__EMPTY_67", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_63", + title: " 지방비 ", + upName: "__EMPTY_67", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_64", + title: " 계 ", + upName: "__EMPTY_67", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_65", + title: " 국고 ", + upName: "__EMPTY_67", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_66", + title: " 지방비 ", + upName: "__EMPTY_67", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_67", + title: "2024년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_67", + title: " 총사업비 ", + upName: "__EMPTY_73", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_68", + title: " 국고 ", + upName: "__EMPTY_73", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_69", + title: " 지방비 ", + upName: "__EMPTY_73", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_70", + title: " 계 ", + upName: "__EMPTY_73", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_71", + title: " 국고 ", + upName: "__EMPTY_73", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_72", + title: " 지방비 ", + upName: "__EMPTY_73", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_73", + title: "2025년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "2__EMPTY_73", + title: " 총사업비 ", + upName: "__EMPTY_37", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_74", + title: " 국고 ", + upName: "__EMPTY_37", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_75", + title: " 지방비 ", + upName: "__EMPTY_37", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_76", + title: " 계 ", + upName: "__EMPTY_37", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_77", + title: " 국고 ", + upName: "__EMPTY_37", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_78", + title: " 지방비 ", + upName: "__EMPTY_37", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, +]; + +export const oldPpTable6HeadList: z.infer = [ + { + id: "rowNumber", + title: "일련번호", + visible: true, + }, + { + id: "__EMPTY", + title: "차수", + visible: true, + }, + { + id: "__EMPTY_1", + title: "기존관리", + visible: true, + }, + { + id: "__EMPTY_2", + title: "연번", + visible: true, + }, + { + id: "__EMPTY_3", + title: "정비사업 사업코드", + visible: true, + }, + { + id: "__EMPTY_4", + title: "착공년도", + visible: true, + }, + { + id: "__EMPTY_5", + title: "준공년도", + visible: true, + }, + { + id: "__EMPTY_6", + title: "유역청", + visible: true, + }, + { + id: "__EMPTY_7", + title: "시도", + visible: true, + }, + { + id: "__EMPTY_8", + title: "시군구", + visible: true, + }, + { + id: "__EMPTY_9", + title: "사업명", + visible: true, + }, + { + id: "__EMPTY_10", + title: "내역사업명", + visible: true, + }, + { + id: "__EMPTY_11", + title: "계획물량\r\n(결산자료)", + visible: true, + }, + { + id: "__EMPTY_12", + title: " 계 ", + visible: true, + }, + { + id: "__EMPTY_13", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_14", + title: " 지방비 ", + visible: true, + }, + { + id: "__EMPTY_19", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_25", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_31", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_37", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_43", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_49", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_55", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_61", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_67", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_73", + title: " 국고 ", + visible: true, + }, + { + id: "__EMPTY_75", + title: " 총국고 ", + visible: true, + }, + { + id: "__EMPTY_76", + title: " 검토자 ", + visible: true, + }, +]; + +export const oldPpTable6NodeList: CommonTableProps[] = [ + { + id: "rowNumber", + title: "일련번호", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY", + title: "차수", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_1", + title: "기존관리", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_2", + title: "연번", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_3", + title: "정비사업 사업코드", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_4", + title: "착공년도", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_5", + title: "준공년도", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_6", + title: "유역청", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_7", + title: "시도", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_8", + title: "시군구", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_9", + title: "사업명", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_10", + title: "내역사업명", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "2__EMPTY_11", + title: "물량", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_11", + title: "계획물량\r\n(결산자료)", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_12", + title: "총계", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "__EMPTY_12", + title: " 계 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_13", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_14", + title: " 지방비 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_15", + title: "2016년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_19", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_21", + title: "2017년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_25", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_27", + title: "2018년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_31", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_33", + title: "2019년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_37", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_39", + title: "2020년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_43", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_45", + title: "2021년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_49", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_51", + title: "2022년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_55", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_57", + title: "2023년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_61", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_63", + title: "2024년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_67", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "2__EMPTY_69", + title: "2025년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_73", + title: " 국고 ", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "__EMPTY_75", + title: " 총국고 ", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "__EMPTY_76", + title: " 검토자 ", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, +]; \ No newline at end of file diff --git a/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx b/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx index 7165188ca..8fc09b5f4 100644 --- a/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx +++ b/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx @@ -1,7 +1,7 @@ 'use client' import { deleteData, postData, useCookieAuth } from '@/components/lib/api' import { searchData } from '@/components/lib/common/apiHelpers' -import { formatDate, handleDownload, yearOptions } from '@/components/lib/utils' +import { formatDate, handleDownload } from '@/components/lib/utils' import LoadingOverlay from '@/components/ui/LoadingBar' import { SearchDiv, SearchFormLeft, SearchFormRight, UiTable, UiModal } from '@common/business_components/ui' import ListDiv from '@common/business_components/ui/list_div' @@ -14,6 +14,7 @@ import { AUTH_USER_ID } from "@/shared/config"; import { useQuery } from "@tanstack/react-query"; import { ENDPOINT } from "@common/components/config"; import { getUserInfo } from "@common/components/api"; +import TableModal from './tableModal'; const Page = () => { @@ -30,7 +31,8 @@ const Page = () => { const [curPage, setCurPage] = useState(1) const [totalData, setTotalData] = useState(0) //총 데이터 수 const [isLoading, setIsLoading] = useState(true) //로딩 상태 - const [isModalOpen, setIsModalOpen] = useState(false) // 모달 상태 + const [isModalOpen, setIsModalOpen] = useState(false) // 등록 모달 상태 + const [isTableModalOpen, setIsTableModalOpen] = useState(false) // 테이블 모달 상태 const [resetSelection, setResetSelection] = useState(false); // 선택 초기화 상태 const [isResetPage, setIsResetPage] = useState(false); @@ -223,6 +225,7 @@ const Page = () => { setFileNm(fileName); setAtflId(id); setDcrpPplMtncBizSn(tableData[index]?.dcrpPplMtncBizSn); + setIsTableModalOpen(true); } }} onPageChange={setCurPage} @@ -245,6 +248,21 @@ const Page = () => { /> } /> + {/* 엑셀 데이터 테이블 모달 */} + } + useDirectRender={true} + /> ) } diff --git a/apps/sn/app/(with-base-layout)/oper/oldPp/tableModal.tsx b/apps/sn/app/(with-base-layout)/oper/oldPp/tableModal.tsx new file mode 100644 index 000000000..169ae0895 --- /dev/null +++ b/apps/sn/app/(with-base-layout)/oper/oldPp/tableModal.tsx @@ -0,0 +1,465 @@ +'use client' +import { useState, useEffect } from 'react' +import LoadingOverlay from '@/components/ui/LoadingBar' +import { UITab, UiTable } from '@common/business_components/ui' +import * as XLSX from 'xlsx'; +import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan'; +import { oldPpTable2HeadList, oldPpTable2NodeList, oldPpTable6HeadList, oldPpTable6NodeList } from './headList'; + +const TABS = [ + { val: "1", text: "row data" }, + { val: "2", text: "1.정밀조사" }, + { val: "3", text: "2.정밀조사_정비사업연계" }, + { val: "4", text: " 3.정비사업" }, + { val: "5", text: "4.정비사업요약" }, + { val: "6", text: "4.정비사업최종" }, +]; + +interface TableModalProps { + fileNm: string; + atflId: string; +} + +const TableModal: React.FC = ({ fileNm, atflId }) => { + + const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "1"); + const [tableHeader1, setTableHeader1] = useState<{ id: string; title: string }[]>([]) + const [tableHeader2, setTableHeader2] = useState<{ id: string; title: string }[]>([]) + const [tableHeader4, setTableHeader4] = useState<{ id: string; title: string }[]>([]) + const [tableHeader5, setTableHeader5] = useState<{ id: string; title: string }[]>([]) + const [tableData1, setTableData1] = useState([{}]) + const [tableData2, setTableData2] = useState([{}]) + const [tableData3, setTableData3] = useState([{}]) + const [tableData4, setTableData4] = useState([{}]) + const [tableData5, setTableData5] = useState([{}]) + const [tableData6, setTableData6] = useState([{}]) + const [isLoading, setIsLoading] = useState(false) + + useEffect(() => { + isChckFile(); + }, []); + + // 파일 다운로드 + const isChckFile = async () => { + setIsLoading(true); + try { + if (fileNm === '' || fileNm === undefined) { + alert('파일을 선택해주세요'); + setIsLoading(false); + return false; + } + + const response = await fetch(`/attach-service/api/v1/download/${atflId}`); + if (!response.ok) { + throw new Error('파일을 가져오는 중 오류가 발생했습니다.'); + } + + const blob = await response.blob(); + + const file = new File([blob], fileNm, { + type: blob.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + }); + + const targetSheets = ['row data', '1.정밀조사', '2.정밀조사_정비사업연계', ' 3.정비사업', '4.정비사업요약', '4.정비사업최종']; + const jsonDataBySheet = await filePickedSelectedSheets(file, 0, targetSheets); + + const header1 = jsonDataBySheet['row data']?.[1] || []; + const header2 = jsonDataBySheet['1.정밀조사']?.[1] || []; + const header4 = jsonDataBySheet[' 3.정비사업']?.[1] || []; + const header5 = jsonDataBySheet['4.정비사업요약']?.[2] || []; + + // row data + const allEntries1 = Object.entries(header1); + const filteredHeader1 = [ + allEntries1[0], + allEntries1[1], + allEntries1[2], + allEntries1[3], + allEntries1[5], + allEntries1[6], + allEntries1[7], + allEntries1[8], + allEntries1[9], + allEntries1[10], + allEntries1[11], + allEntries1[12], + allEntries1[13], + allEntries1[14], + allEntries1[15] + ].filter((e): e is [string, any] => Boolean(e)) + .map(([key, value]) => ({ + id: key, + title: String(value).replace(/\r\n/g, '\n') + })); + filteredHeader1.splice(0, 0, { + id: "rowNumber", + title: '일련번호' + }); + setTableHeader1(filteredHeader1); + + const data1 = Array.isArray(jsonDataBySheet['row data']) + ? jsonDataBySheet['row data'].slice(2) + : []; + const filteredData1 = filterEmptyRows(data1).map((item: any, index: number) => ({ + ...item, + rowNumber: index + 1, + __EMPTY_1: item.__EMPTY_1 || '-' + })); + setTableData1(filteredData1); + + // 1.정밀조사 + const allEntries2 = Object.entries(header2); + let filteredHeader2 = [ + allEntries2[0], + allEntries2[1], + allEntries2[2], + allEntries2[3], + allEntries2[5], + allEntries2[6], + allEntries2[7], + allEntries2[8], + allEntries2[9], + allEntries2[10], + allEntries2[11], + allEntries2[12], + allEntries2[13], + allEntries2[14], + allEntries2[15] + ].filter((e): e is [string, any] => Boolean(e)) + .map(([key, value]) => ({ + id: key, + title: String(value).replace(/\r\n/g, '\n') + })); + filteredHeader2.splice(0, 0, { + id: "rowNumber", + title: '일련번호' + }); + setTableHeader2(filteredHeader2); + + const data2 = Array.isArray(jsonDataBySheet['1.정밀조사']) + ? jsonDataBySheet['1.정밀조사'].slice(2) + : []; + const filteredData2 = filterEmptyRows(data2).map((item: any, index: number) => ({ + ...item, + rowNumber: index + 1 + })); + setTableData2(filteredData2); + + // 2.정밀조사_정비사업연계 (헤더는 import) + const data3 = Array.isArray(jsonDataBySheet['2.정밀조사_정비사업연계']) + ? jsonDataBySheet['2.정밀조사_정비사업연계'].slice(5) + : []; + const filteredData3 = filterEmptyRows(data3).map((item: any, index: number) => { + const newItem = { ...item }; + // 매핑 에러로 인해 특정 키를 "__EMPTY_0"으로 변경 + const targetKey = "*1차 출처: 공단자료\r\n*2차 출처: 정산서, 정밀조사보고서 등.\r\n (파랑) 2024년말 기준 실적 제출 자료. 준공 후 재확인 필요\r\n (노랑셀) 준공(또는 정산) 후에 재확인 필요"; + if (newItem[targetKey] !== undefined) { + newItem["__EMPTY_0"] = newItem[targetKey]; + delete newItem[targetKey]; + } + return { + ...newItem, + __EMPTY_4: newItem?.__EMPTY_4 || '-', + rowNumber: index + 1 + }; + }); + setTableData3(filteredData3); + + // 3.정비사업 + const allEntries4 = Object.entries(header4); + const filteredHeader4 = allEntries4.filter((e): e is [string, any] => Boolean(e)) + .map(([key, value]) => ({ + id: key, + title: String(value).replace(/\r\n/g, '\n') + })); + + filteredHeader4.splice(0, 0, { + id: "rowNumber", + title: '일련번호' + }); + setTableHeader4(filteredHeader4); + + const data4 = Array.isArray(jsonDataBySheet[' 3.정비사업']) + ? jsonDataBySheet[' 3.정비사업'].slice(2) + : []; + const filteredData4 = filterEmptyRows(data4).map((item: any, index: number) => ({ + ...item, + rowNumber: index + 1, + __EMPTY_2: item?.__EMPTY_2 || '-', + __EMPTY_11: item?.__EMPTY_11 || '-', + })); + setTableData4(filteredData4); + + // 4.정비사업요약 + const allEntries5 = Object.entries(header5); + const excludeKeys5 = [ + ": \"정비사업\"sheet에서 사업코드 ctrl+c, Ctrl+v 한 다음 중복된 데이터 제거실시", + "__EMPTY", + "__EMPTY_8", + "__EMPTY_15" + ]; + const filteredHeader5 = allEntries5 + .filter(([key]) => !excludeKeys5.includes(key)) + .filter((e): e is [string, any] => Boolean(e)) + .map(([key, value]) => ({ + id: key, + title: String(value).replace(/\r\n/g, '\n'), + })); + filteredHeader5.splice(0, 0, { + id: "rowNumber", + title: '일련번호' + }); + setTableHeader5(filteredHeader5); + + const data5 = Array.isArray(jsonDataBySheet['4.정비사업요약']) + ? jsonDataBySheet['4.정비사업요약'].slice(3) + : []; + const filteredData5 = + filterEmptyRows(data5).map((item: any, index: number) => ({ + ...item, + rowNumber: index + 1, + })); + setTableData5(filteredData5); + + // 4.정비사업최종 (헤더는 import) + const data6 = Array.isArray(jsonDataBySheet['4.정비사업최종']) + ? jsonDataBySheet['4.정비사업최종'].slice(6) + : []; + const filteredData6 = + filterEmptyRows(data6).map((item: any, index: number) => ({ + ...item, + rowNumber: index + 1, + })); + setTableData6(filteredData6); + } catch (error) { + console.error('파일 처리 중 오류 발생:', error); + alert('파일 처리 중 오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } +} + +function filterEmptyRows(data: any[]) { + return data + .filter(row => { + return Object.entries(row).some(([key, value]) => { + const match = key.match(/^__EMPTY_(\d+)$/); + if (!match || parseInt(match[1] ?? '', 10) < 2) return false; + + return value !== undefined && + value !== null && + String(value).trim() !== '' && + String(value).trim() !== '-' && + String(value).trim() !== ''; + }); + }) + .map((row) => { + const processedRow = { ...row }; + const keys = Object.keys(processedRow); + + keys.forEach((key, keyIndex) => { + // 마지막 속성(사업 근거)이 아닌 경우에만 빈 값을 '-'으로 변경 + if (keyIndex !== keys.length - 1) { + const value = processedRow[key]; + if (value === undefined || + value === null || + value === '' || + String(value).trim() === '') { + processedRow[key] = '-'; + } + } + }); + + return processedRow; + }); +}; + +// 특정 시트만 선택적으로 JSON으로 변환하는 함수 +function filePickedSelectedSheets(file: File, headerNum: number = 0, sheetNames: string[]): Promise> { + const reader = new FileReader(); + + return new Promise((resolve, reject) => { + reader.onload = function (e) { + const data = e.target?.result; + if (!data) { + console.error("Failed to read file: e.target.result is null"); + reject(new Error("Failed to read file")); + return; + } + + // Excel 파일 읽기 + const wb = XLSX.read(data, { type: 'binary', cellDates: true }); + const result: Record = {}; + + // 지정된 시트 이름만 찾아서 처리 + sheetNames.forEach(targetSheetName => { + // 대소문자 구분 없이 시트 이름 찾기 + const sheetName = wb.SheetNames.find(name => + name.toLowerCase() === targetSheetName.toLowerCase() + ); + + if (sheetName) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + const jsonData = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw: false }); + result[targetSheetName] = jsonData; + } + } else { + console.warn(`Sheet "${targetSheetName}" not found in workbook`); + result[targetSheetName] = []; + } + }); + + resolve(result); + }; + + reader.onerror = function () { + console.error("Error reading file"); + reject(new Error("Error reading file")); + }; + + // 파일 읽기 시작 + reader.readAsBinaryString(file); + }); +}; + + +return ( + <> + {/* 탭 영역 */} + ({ + value: tab.val, + label: tab.text + }))} + onValueChange={(value: string) => setActiveTab(value)} + /> + {/* row data 탭 */} + {activeTab === '1' && ( +
+
+ +
+ { }} + /> +
+
+
+ )} + {/* 1.정밀조사 탭 */} + {activeTab === '2' && ( +
+
+ +
+ { }} + /> +
+
+
+ )} + {/* 2.정밀조사_정비사업연계 탭 */} + {activeTab === '3' && ( +
+
+ +
+ { }} + > + + +
+
+
+ )} + {/* 3.정비사업 탭 */} + {activeTab === '4' && ( +
+
+ +
+ { }} + /> +
+
+
+ )} + {/* 4.정비사업요약 탭 */} + {activeTab === '5' && ( +
+
+ +
+ { }} + /> +
+
+
+ )} + {/* 4.정비사업최종 탭 */} + {activeTab === '6' && ( +
+
+ +
+ { }} + > + + +
+
+
+ )} + +) +}; + +export default TableModal; \ No newline at end of file