Merge commit 'e3ff0d5e9633446e53b17f9c14840837eb52a805' into iipbs/wr/excel

This commit is contained in:
truthwing
2025-04-29 10:29:45 +09:00
14 changed files with 291 additions and 232 deletions
@@ -3,7 +3,7 @@
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type { TableTripleProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { tableData } from "@/components/lib/func/commom";
import { excelDownload, tableData } from "@/components/lib/func/commom";
import HeadChModal from "@/components/ui/modal/headChModal/headChModal";
import {
ContentsDiv,
@@ -20,7 +20,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { headMakeTriple } from "./TableHead";
export default function RainSttn() {
const { code, sttnCommon, excelRainSttn,excelExportAction, rainSttnSearch } =
const { code, sttnCommon, excelExportAction, rainSttnSearch } =
useWrStore((state) => state);
const { rainRes } = sttnCommon;
@@ -66,21 +66,14 @@ export default function RainSttn() {
useEffect(() => {
if(sttnCommon.excelList.length>0){
if (sttnCommon.excelList.length === 0) return;
fetch('/sample_rainy.xlsx', {})
.then(async (response) => {
if (!response.ok) {
throw Error(response.statusText)
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer()
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
const ret =await filePickedBlob(blobRet, sttnCommon.excelList )
console.log('ret', ret);
})
excelDownload(sttnCommon.excelList , '빗물재이용시설 현황','sample_rainSttn_test.xlsx')
}
}, [sttnCommon.excelList]);
const excelClick = useCallback((returnType:ReturnType) => {
let con
if (returnType.sigunValue != '0') {
con = returnType.sigunValue
@@ -114,7 +107,6 @@ export default function RainSttn() {
}
leftChildren={undefined}
queryClick={function (returnType: ReturnType): void {
// page: string, total: string, con: string
// '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}'
let con
if (returnType.sigunValue != '0') {
@@ -7,6 +7,7 @@ import {
type rainChkZodType,
} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { excelDownload } from "@/components/lib/func/commom";
import ChkgdExcel from "@/components/ui/modal/chkgdExcel/chkgdExcel";
import ChkgdModal from "@/components/ui/modal/chkgdModal/chkgdModal";
import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable";
@@ -53,19 +54,7 @@ export default function Page() {
useEffect(() => {
console.log("rainChkgdn.excelList", rainChkgdn.excelList);
if (rainChkgdn.excelList.length === 0) return;
fetch('/sample_rainy.xlsx', {})
.then(async (response) => {
if (!response.ok) {
throw Error(response.statusText)
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer()
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
const ret =await filePickedBlob(blobRet, rainChkgdn.excelList )
console.log('ret', ret);
})
excelDownload(rainChkgdn.excelList);
}, [rainChkgdn.excelList]);
const excelClick= useCallback(()=>{
@@ -288,7 +288,7 @@ export type rainChkgdnState = {
splHeadList: TableUpperProps[];
rswtDetailHeadList: TableUpperProps[];
isInit: boolean;
excelList:ExcelExportType[] | JSON[]
excelList:JSON[];
};
};
@@ -101,6 +101,10 @@ export type TableUpperProps = {
type?: string;
};
export type TableTripleProps = {
id: string;
title: string;
@@ -24,20 +24,13 @@ export type ExcelSttnRainType={
},
"저류조용량": string,
},
"3.시설운영및 이용정보"?:{
"운영관리코드": string,
"연간빗물운영량": string
},
"3.인센티브 정보":{
"설치비지원":{
"설치지원비용여부": string,
"설치지원비용": string,
},
"요금감면"?:{
"감면여부": string,
"감면근거": string,
"감면금액": string,
},
"녹색건축물인증여부":{
"녹색건축물인증여부": string,
"녹색건축물인증년도": string,
@@ -47,7 +40,7 @@ export type ExcelSttnRainType={
}
}
const excelSttnParsing = (data: customRainBdst[]): ExcelSttnRainType[] => {
export const excelSttnParsing = (data: customRainBdst[]): ExcelSttnRainType[] => {
const excelExportData: ExcelSttnRainType[] = []
data.forEach((item) => {
excelExportData.push(
@@ -89,8 +82,85 @@ const excelSttnParsing = (data: customRainBdst[]): ExcelSttnRainType[] => {
}
})
});
console.log(excelExportData)
// excelItem = {} as ExcelSttnRainType
// excelItem["1.건축물정보"]["행정구역"]["시도"] = item.sidoNm
// excelItem["1.건축물정보"]["행정구역"]["시군구"] = item.sggNm
return excelExportData;
}
}
export type ExcelSttnRainTypeFlat = {
행정구역: string;
건축물명: string;
주소: string;
건축물용도코드: string;
최초건축허가일: string;
건축면적: string;
법적의무대상: string;
설치완료일시: string;
설치비용: string;
집수면: string;
여과등처리시설: string;
저류조용량: string;
운영관리코드?: string;
연간빗물운영량?: string;
설치지원비용여부: string;
설치지원비용: string;
녹색건축물인증여부: string;
};
export const excelSttnParsingJson = (data: customRainBdst[]): ExcelSttnRainTypeFlat[] => {
const excelExportData: ExcelSttnRainTypeFlat[] = [];
data.forEach((item) => {
excelExportData.push({
// 1.건축물정보
"행정구역": JSON.stringify({
시도: String(item.ctpvCd),
시군구: String(item.sggCd)
}),
"건축물명": item.bdstNm ?? "",
"주소": item.daddr ?? "",
"건축물용도코드": String(item.bdstInfoPk ?? ""),
"최초건축허가일": String(item.frstArchPrmsnDt ?? ""),
"건축면적": String(item.archArea ?? ""),
"법적의무대상": String(item.reuseBuilding?.[0]?.lawTrgtYn ?? ""),
"설치완료일시": String(item.reuseBuilding?.[0]?.instlCmptnDt ?? ""),
// 2.빗물이용시설정보
"설치비용": String(item.reuseBuilding?.[0]?.instlCst ?? ""),
"집수면": JSON.stringify({
집수지점종류: item.reuseBuilding?.[0]?.rainyObj?.pntfColKnd ?? "",
집수지점면적: String(item.reuseBuilding?.[0]?.rainyObj?.pntfColArea ?? "")
}),
"여과등처리시설": JSON.stringify({
여과처리시설유무: String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctYn ?? ""),
여과처리시설용량: String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctCpct ?? ""),
여과처리시설처리공정: String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctPrs ?? "")
}),
"저류조용량": String(item.reuseBuilding?.[0]?.rainyObj?.dtfcltCpct ?? ""),
// 3.인센티브 정보
"설치지원비용여부": String(item.reuseBuilding?.[0]?.instlCstSprtYn ?? ""),
"설치지원비용": String(item.reuseBuilding?.[0]?.instlCstSprtAmt ?? ""),
"녹색건축물인증여부": JSON.stringify({
녹색건축물인증여부: String(item.reuseBuilding?.[0]?.grntchBdstCertYn ?? ""),
녹색건축물인증년도: String(item.reuseBuilding?.[0]?.grntchBdstCertYr ?? ""),
녹색건축물인증번호: String(item.reuseBuilding?.[0]?.grntchBdstCertNo ?? ""),
녹색건축물인증등급: String(item.reuseBuilding?.[0]?.grntchBdstCertGrd ?? "")
}),
});
});
return excelExportData;
};
@@ -4,7 +4,7 @@ import {
type TableTripleProps,
type TableUpperProps,
} from "../../fcltReg/rainReg";
import type { ExcelSttnRainType } from "./excel";
import { excelSttnParsing, excelSttnParsingJson, type ExcelSttnRainType, type FlatExcelSttnRainType } from "./excel";
import { fetchType, getFetch } from "@/components/lib/api";
const pbnSttHeadNm: chHeadType[] = [
@@ -316,7 +316,6 @@ export type sttnState = {
type sttnActions = {
rainSttnSearch: (page: string, total: string, con: string) => void;
chSttnHead: (checkList: string[], type: string) => void;
excelRainSttn:(data:JSON[] ) => void
};
export type sttnType = sttnState & sttnActions;
@@ -342,12 +341,14 @@ export const sttnActionsExport: (set: any) => sttnActions = (set: any) => {
`/reuseFc/rainyInfoList/${page}/${total}/${con}`
);
if(Number(total) > 9000){
//const rainExcel= excelSttnParsing(rainRes)
const rainExcel= excelSttnParsing(rainRes)
set((state: sttnState) => {
return {
...state,
sttnCommon: {
...state.sttnCommon,
excelList: rainRes,
excelList: rainExcel,
},
};
});
@@ -367,21 +368,6 @@ export const sttnActionsExport: (set: any) => sttnActions = (set: any) => {
console.log("error", erTxt);
}
},
excelRainSttn: async (data: JSON[]) => {
//TODO: 엑셀 데이터 array 생성해서 return 필요. ExcelSttnRainType[]으로 생성해서 던져주면 됨.
set((state: sttnState) => {
return {
...state,
sttnCommon: {
...state.sttnCommon,
rainRes: rainRes,
isInit: false,
},
};
});
},
chSttnHead: async (checkList: string[], type: string) => {
const headList =
type === "pbn"
@@ -423,3 +409,5 @@ export const sttnActionsExport: (set: any) => sttnActions = (set: any) => {
},
};
};
+144
View File
@@ -0,0 +1,144 @@
import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { Checkbox, Input, TableCell, TableRow } from "@common/components/ui";
export const rightTableData = (rainChk: rainChkZodType[]) => {
// 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기
const maketotal = rainChk.map((item: any) => {
const selectTotal = item.guideDetails.reduce((acc: any, cur: any) => {
const code = cur.utztnUsgCd;
const fk = cur.chkgdnFk;
const key = `${fk}_${code}`;
acc[key] = acc[key] || {
totalSplyUswtrUtztnQnt: 0,
totalRuseFcltUtztnQnt: 0,
};
acc[key].totalSplyUswtrUtztnQnt += cur.splyUswtrUtztnQnt;
acc[key].totalRuseFcltUtztnQnt += cur.ruseFcltUtztnQnt;
return acc;
}, {});
return selectTotal; // 모든 키: `${chkgdnFk}_${utztnUsgCd}` 형태
});
// 2. 누계 정보들을 하나의 객체로 합치기 (평탄화)
const flatTotalMap = Object.assign({}, ...maketotal);
// 3. rainyDetails에 누계 정보 주입
const updatedRainyDetails: any[] = [];
rainChk.forEach((rd: any) => {
const details = rd.rainyDetails.forEach((rainyItem: any) => {
const key = `${rainyItem.chkgdnFk}_${rainyItem.utztnUsgCd}`;
const totals = flatTotalMap[key] || {
totalSplyUswtrUtztnQnt: 0,
totalRuseFcltUtztnQnt: 0,
};
updatedRainyDetails.push({
...rd,
...rd.building,
...rainyItem,
...totals,
rowspan: rd.rainyDetails?.length,
});
});
});
return updatedRainyDetails;
};
export const chRowspanTable = (
makeTableData: any[],
headList: TableUpperProps[],
handleChange: (
rowIndex: number,
field: keyof (typeof makeTableData)[0],
value: string | number
) => void,
toggleSelection: (pk: number, checked: boolean) => void,
selectedIds: number[],
handleCellClick: (index: number, headId: string) => void
) => {
// rowspan된 셀 렌더링 여부 추적용
const renderedMap = new Map<string, Set<number>>();
return (
<>
{makeTableData.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{headList.map((head) => {
const key = head.id;
const isMerged = head.rowSpan;
const value = row[key];
// 병합 대상이면 중복 렌더링 여부 판단
if (isMerged) {
const renderedSet = renderedMap.get(key) ?? new Set<number>();
if (renderedSet.has(rowIndex)) return null; // 이미 렌더링된 셀은 skip
const rowspan = row.rowspan ?? 1;
for (let i = 0; i < rowspan; i++) {
renderedSet.add(rowIndex + i);
}
renderedMap.set(key, renderedSet);
return (
<TableCell
key={key}
rowSpan={rowspan}
className="align-middle text-center whitespace-nowrap"
>
{key === "order" ? (
<Checkbox
checked={selectedIds.includes(row.chkgdnPk)}
onCheckedChange={(checked) =>
toggleSelection(row.chkgdnPk, checked === true)
}
></Checkbox>
) : (
value
)}
</TableCell>
);
}
// 기본 셀
return (
<TableCell
key={key}
className="text-center whitespace-nowrap"
onClick={() => handleCellClick(rowIndex, head.id)}
>
{key === "totalSplyUswtrUtztnQnt" ||
key === "totalRuseFcltUtztnQnt" ||
key === "utztnUsgCd" ? (
`${value}`
) : (
<Input
className={`${head.type === "number" ? "text-right" : "text-center"} text-3xl`}
type={head.type}
onChange={(e) => {
handleChange(rowIndex, head.id, e.target.value);
}}
value={value}
/>
)}
</TableCell>
);
})}
</TableRow>
))}
</>
);
};
@@ -1,5 +1,5 @@
import type { comCodeState, } from "@/app/store/common/code";
import type { SelectDataType } from "@common/business_components/ui";
import { filePickedBlob, type SelectDataType } from "@common/business_components/ui";
@@ -47,4 +47,20 @@ export const getCity = (val: string, sggCd: SelectDataType[]) => {
});
}
export const excelDownload =(excelList: JSON[], fileNm:string ,sampleFile:string ) =>{
console.log("excelList", excelList);
if (excelList.length === 0) return;
fetch(`/${sampleFile}`, {})
.then(async (response) => {
if (!response.ok) {
throw Error(response.statusText)
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer()
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
const ret =await filePickedBlob(blobRet, excelList,fileNm )
})
}
@@ -1,8 +1,7 @@
"use client";
import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/page";
import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead";
import { useWrStore } from "@/app/store";
import type {
rainChkZodType,
splRuseWaterType,
} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
@@ -30,7 +29,6 @@ export function GuideDetailsTable({
const { code, rainChkgdn } = useWrStore((state) => state);
const [guidData, setGuidData] = useState<splRuseWaterType[]>();
const nodeList = headMakeprfmnc(headList, rainChkgdn?.rainHeadNm);
const handleChange = (
rowIndex: number,
@@ -89,7 +87,7 @@ export function GuideDetailsTable({
<>
<div className={`card !m-[1.2rem] !p-0r `}>
<Table variant="secondary">
<TableHeader className="font-bold">{nodeList}</TableHeader>
<TableHeader className="font-bold">{headMakeprfmnc(headList, rainChkgdn?.rainHeadNm)}</TableHeader>
<TableBody >
{tableBody(guidData ?? [])}
</TableBody>
@@ -1,167 +1,20 @@
"use client";
import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/page";
import { useWrStore } from "@/app/store";
import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { tableData } from "@/components/lib/func/commom";
import { UiModal } from "@common/business_components/ui";
import {
Checkbox,
Input,
Table,
TableBody,
TableCell,
TableHeader,
TableRow,
} from "@common/components/ui";
import { useEffect, useState } from "react";
import ChkgdModal from "../../../modal/chkgdModal/chkgdModal";
export const totalData = (
makeTableData: any[],
headList: TableUpperProps[],
handleChange: (
rowIndex: number,
field: keyof (typeof makeTableData)[0],
value: string | number
) => void,
toggleSelection: (pk: number, checked: boolean) => void,
selectedIds: number[],
handleCellClick: (index: number, headId: string) => void
) => {
const mergeKeys = [
"order",
"cityText",
"sggText",
"bdstNm",
"operMngCd",
"flowCheck",
"yearFee",
"rainDtnbaOpct",
"chckYmd",
];
// rowspan된 셀 렌더링 여부 추적용
const renderedMap = new Map<string, Set<number>>();
return (
<>
{makeTableData.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{headList.map((head) => {
const key = head.id;
const isMerged = head.rowSpan;
const value = row[key];
// 병합 대상이면 중복 렌더링 여부 판단
if (isMerged) {
const renderedSet = renderedMap.get(key) ?? new Set<number>();
if (renderedSet.has(rowIndex)) return null; // 이미 렌더링된 셀은 skip
const rowspan = row.rowspan ?? 1;
for (let i = 0; i < rowspan; i++) {
renderedSet.add(rowIndex + i);
}
renderedMap.set(key, renderedSet);
return (
<TableCell
key={key}
rowSpan={rowspan}
className="align-middle text-center whitespace-nowrap"
>
{key === "order" ? (
<Checkbox
checked={selectedIds.includes(row.chkgdnPk)}
onCheckedChange={(checked) =>
toggleSelection(row.chkgdnPk, checked === true)
}
></Checkbox>
) : (
value
)}
</TableCell>
);
}
// 기본 셀
return (
<TableCell
key={key}
className="text-center whitespace-nowrap"
onClick={() => handleCellClick(rowIndex, head.id)}
>
{key === "totalSplyUswtrUtztnQnt" ||
key === "totalRuseFcltUtztnQnt" ||
key === "utztnUsgCd" ? (
`${value}`
) : (
<Input
className={`${head.type === "number" ? "text-right" : "text-center"} text-3xl`}
type={head.type}
onChange={(e) => {
handleChange(rowIndex, head.id, e.target.value);
}}
value={value}
/>
)}
</TableCell>
);
})}
</TableRow>
))}
</>
);
};
export const rightTableData = (rainChk: rainChkZodType[]) => {
// 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기
const maketotal = rainChk.map((item: any) => {
const selectTotal = item.guideDetails.reduce((acc: any, cur: any) => {
const code = cur.utztnUsgCd;
const fk = cur.chkgdnFk;
const key = `${fk}_${code}`;
acc[key] = acc[key] || {
totalSplyUswtrUtztnQnt: 0,
totalRuseFcltUtztnQnt: 0,
};
acc[key].totalSplyUswtrUtztnQnt += cur.splyUswtrUtztnQnt;
acc[key].totalRuseFcltUtztnQnt += cur.ruseFcltUtztnQnt;
return acc;
}, {});
return selectTotal; // 모든 키: `${chkgdnFk}_${utztnUsgCd}` 형태
});
// 2. 누계 정보들을 하나의 객체로 합치기 (평탄화)
const flatTotalMap = Object.assign({}, ...maketotal);
// 3. rainyDetails에 누계 정보 주입
const updatedRainyDetails: any[] = [];
rainChk.forEach((rd: any) => {
const details = rd.rainyDetails.forEach((rainyItem: any) => {
const key = `${rainyItem.chkgdnFk}_${rainyItem.utztnUsgCd}`;
const totals = flatTotalMap[key] || {
totalSplyUswtrUtztnQnt: 0,
totalRuseFcltUtztnQnt: 0,
};
updatedRainyDetails.push({
...rd,
...rd.building,
...rainyItem,
...totals,
rowspan: rd.rainyDetails?.length,
});
});
});
return updatedRainyDetails;
};
import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead";
import {
chRowspanTable,
rightTableData,
} from "@/components/lib/func/TableData";
export default function RainChkgdTable({
detailUp,
}: {
@@ -172,14 +25,14 @@ export default function RainChkgdTable({
const { rainChk } = rainChkgdn;
const [selectedIds, setSelectedIds] = useState<number[]>([]);
const [upRaintDetail, setUpRaintDetail] = useState<rainChkZodType[]>([]);
const [isModal, setIsModal] = useState({open:false, data:{}});
const [isModal, setIsModal] = useState({ open: false, data: {} });
const handleCellClick = (index: number, headId: string) => {
if (
headId === "totalSplyUswtrUtztnQnt" ||
headId === "totalRuseFcltUtztnQnt"
) {
setIsModal({open:true, data: makeTableData[index]});
setIsModal({ open: true, data: makeTableData[index] });
}
};
@@ -200,10 +53,7 @@ export default function RainChkgdTable({
);
};
const nodeList = headMakeprfmnc(
rainChkgdn?.rainChkgdnHeadList,
rainChkgdn?.rainHeadNm
);
useEffect(() => {
const rainChg = rainChk.map((item: any) => {
@@ -219,8 +69,6 @@ export default function RainChkgdTable({
setMakeTableData(rightTableData(rainChg));
}, [rainChk, code]);
useEffect(() => {
const selectedData = makeTableData.filter((item) =>
selectedIds.includes(item.chkgdnPk)
@@ -271,10 +119,15 @@ export default function RainChkgdTable({
return (
<>
<Table variant="secondary">
<TableHeader className="font-bold">{nodeList}</TableHeader>
<TableHeader className="font-bold">
{headMakeprfmnc(
rainChkgdn?.rainChkgdnHeadList,
rainChkgdn?.rainHeadNm
)}
</TableHeader>
<TableBody>
{totalData(
{chRowspanTable(
makeTableData,
rainChkgdn.rainChkgdnHeadList,
handleChange,
@@ -288,10 +141,10 @@ export default function RainChkgdTable({
<UiModal
open={isModal.open}
onOpenChange={() => {
setIsModal({ open:!open , data:{} });
setIsModal({ open: !open, data: {} });
}}
bodyClass={"w-[80vw] max-w-none"}
body={<ChkgdModal type={"RNB"} obj={isModal.data} />}
body={<ChkgdModal type={"RNB"} obj={isModal.data} />}
></UiModal>
</>
);
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -50,7 +50,10 @@ export const filePicked=(oFile: File, headerNum:number=0):Promise<JSON | undefin
reader.readAsBinaryString(oFile);
});
}
export const filePickedBlob=(oFile: ArrayBuffer, data:JSON[], headerNum:number=0):Promise<JSON | undefined> =>{
export const filePickedBlob=(oFile: ArrayBuffer, data:JSON[],fileNm:string, headerNum:number=0):Promise<JSON | undefined> =>{
// Create A File Reader HTML5
return new Promise<JSON | undefined>((resolve) => {
// Ready The Event For When A File Gets Selected
@@ -62,14 +65,16 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:JSON[], headerNum:number=0
if(index==0){
const sheet = wb.Sheets[sheetName];
if (sheet) {
XLSX.utils.sheet_add_json(sheet,data);
//오리진 확인 test 필요
// XLSX.utils.sheet_add_json(sheet,data ,{origin: 3} );
XLSX.utils.sheet_add_json(sheet,data );
jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum }) as unknown as JSON;
XLSX.writeFile(wb, "test.xlsx");
XLSX.writeFile(wb, `${fileNm}.xlsx`);
} else {
console.error(`Sheet "${sheetName}" is undefined.`);
}
}
});
resolve(jsonXls);
});
}
});
}