diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json index 5120980c2..1f02a708e 100644 --- a/packages/common/business_components/package.json +++ b/packages/common/business_components/package.json @@ -42,6 +42,7 @@ "recharts": "^2.15.1", "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", + "xlsx": "^0.18.5", "zod": "^3.24.2" }, "devDependencies": { diff --git a/packages/common/business_components/src/ui/excel_download/index.tsx b/packages/common/business_components/src/ui/excel_download/index.tsx new file mode 100644 index 000000000..d1370b95c --- /dev/null +++ b/packages/common/business_components/src/ui/excel_download/index.tsx @@ -0,0 +1,16 @@ +import * as XLSX from 'xlsx'; + +type ExcelDownloadProps = { + data : JSON | JSON[]; + fileName : string; + sheetNameArray : string[]; +} + +export const downloadExcel = ({ data, fileName = new Date().getTime().toString()+'.xlsx', sheetNameArray = ["Sheet1"] }: ExcelDownloadProps) => { + const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); + //let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }); + //XLSX.write(workbook, { bookType: "xlsx", type: "binary" }); + XLSX.writeFile(workbook, fileName+'.xlsx'); + }; \ No newline at end of file diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index c3022740d..e591be5b5 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -1,19 +1,21 @@ export * from "./chart"; export * from "./date_picker"; +export * from "./dialog_ui"; +export * from "./excel_download"; export * from "./file-upload"; export * from "./form"; export * from "./form_ui"; +export * from "./list_div"; +export * from "./main_content_div"; export * from "./scroll"; +export * from "./search_base_form"; export * from "./search_form"; +export * from "./search_form_left"; +export * from "./search_form_right"; export * from "./search_input"; export * from "./search_input_select"; export * from "./select"; -export * from "./table"; -export * from "./main_content_div"; -export * from "./title"; -export * from "./search_form_left"; -export * from "./search_form_right"; -export * from "./table_div"; -export * from "./list_div"; export * from "./tab"; -export * from "./dialog_ui" \ No newline at end of file +export * from "./table"; +export * from "./table_div"; +export * from "./title"; diff --git a/packages/common/business_components/src/ui/search_base_form/index.tsx b/packages/common/business_components/src/ui/search_base_form/index.tsx new file mode 100644 index 000000000..90ad0b05e --- /dev/null +++ b/packages/common/business_components/src/ui/search_base_form/index.tsx @@ -0,0 +1,140 @@ +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +import { SearchFormLeft, type EnvPartSelectDataType } from "../search_form_left"; +import { SearchFormRight } from "../search_form_right"; +import type { SelectDataType } from "../search_input_select"; +type SearchBaseType = { + leftChildren:React.ReactNode; + rightChildren:React.ReactNode; + selectSidoData?: SelectDataType[] | []; + selectSigunData?: SelectDataType[] | []; + selectRegionData?: SelectDataType[] | []; + selectUnitWaterShedData?: SelectDataType[] | []; + selectMiddleRegionData?: SelectDataType[] | []; + selectEnvPartData?: EnvPartSelectDataType[] | []; + queryClick:(returnType:ReturnType) => void; + excelClick:(returnType:ReturnType) => void; + initClick:() => void; + btnClass:string; + ref:React.RefObject; +} +export type ReturnType = { + partValue: string; + sidoValue: string; + sigunValue: string; + regionValue: string; + unitWaterShedValue: string; + middleRegionValue: string; + envPartValue: string; +} + + +function SearchBaseForm ({ + leftChildren, + rightChildren, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + btnClass, + queryClick, + excelClick, + initClick, + ref + }: SearchBaseType) { + const [partValue, setPartValue] = useState("A"); + const [sidoValue, setSidoValue] = useState("0"); + const [sigunValue, setSigunValue] = useState("0"); + const [regionValue, setRegionValue] = useState("0"); + const [untitWaterShedValue, setUntitWaterShedValue] = useState("0"); + const [middleRegionValue, setMiddleRegionValue] = useState("0"); + const [envPartValue, setEnvPartValue] = useState("0"); + const [resetTrigger, setResetTrigger] = useState(false); + const queryVal = () => { + console.log("조회버튼 클릭"); + queryClick({ + partValue: partValue, + sidoValue: sidoValue, + sigunValue: sigunValue, + regionValue: regionValue, + unitWaterShedValue: untitWaterShedValue, + middleRegionValue: middleRegionValue, + envPartValue: envPartValue, + }); + } + const initVal = useCallback(() => { + setResetTrigger(true); + initClick(); + }, [setResetTrigger, initClick]); + useEffect(() => { + if (resetTrigger) { + setResetTrigger(false); + } + }, [resetTrigger]); + const excelDownload = () => { + excelClick({ + partValue: partValue, + sidoValue: sidoValue, + sigunValue: sigunValue, + regionValue: regionValue, + unitWaterShedValue: untitWaterShedValue, + middleRegionValue: middleRegionValue, + envPartValue: envPartValue, + }) + }; + useEffect(() => { + if (ref && ref.current) { + const customRef = ref.current; + customRef.partValue = partValue; + customRef.sidoValue = sidoValue; + customRef.sigunValue = sigunValue; + customRef.regionValue = regionValue; + customRef.unitWaterShedValue = untitWaterShedValue; + customRef.middleRegionValue = middleRegionValue; + customRef.envPartValue = envPartValue; + } + }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]); + return ( +
+ + {leftChildren} + + + +
+ + +
+ {rightChildren} +
+
+ ) + } + + export { SearchBaseForm }; + \ No newline at end of file diff --git a/packages/common/business_components/src/ui/search_form_left/index.tsx b/packages/common/business_components/src/ui/search_form_left/index.tsx index 448f2df04..ec9e814f5 100644 --- a/packages/common/business_components/src/ui/search_form_left/index.tsx +++ b/packages/common/business_components/src/ui/search_form_left/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import type { SelectDataType } from '../search_input_select'; import { SelectBox } from '../select'; @@ -38,40 +38,44 @@ type SearchFormLeftProps = { setRtnEnvPartValue?: (value: string) => void; setRtnEnvPartSidoValue?: (value: string) => void; setRtnEnvPartSigunValue?: (value: string) => void; + resetTrigger?: boolean; } -type EnvPartSelectDataType = SelectDataType & { +export type EnvPartSelectDataType = SelectDataType & { adstDliStrCd: string, } const selectPartData:SelectDataType[] = [ - { text: "행정별", val: '0' }, - { text: "유역별", val: '01' }, - { text: "환경청별", val: '02' }, + { text: "행정별", val: 'A' }, + { text: "유역별", val: 'R' }, + { text: "환경청별", val: 'G' }, ]; -function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSigunValue, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue }: SearchFormLeftProps) { - const [partValue, setPartValue] = useState('0'); - const [showPart, setShowPart] = useState(''); +function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSigunValue, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false }: SearchFormLeftProps) { + /******************선택된 데이터******************/ + const [partValue, setPartValue] = useState('A'); + //행정별 시도 시군구 const [sidoValue, setSidoValue] = useState('0'); const [sigunValue, setSigunValue] = useState('0'); + //유역별 const [regionValue, setRegionValue] = useState('0'); const [unitWaterShedValue, setUnitWaterShedValue] = useState('0'); const [middleRegionValue, setMiddleRegionValue] = useState('0'); + //환경청별 시도 시군구 const [envPartValue, setEnvPartValue] = useState('0'); - const [envPartSidoValue, setEnvPartSidoValue] = useState('0'); - const [envPartSigunValue, setEnvPartSigunValue] = useState('0'); + // 환경청 + const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState([]); + /****************** 셀렉트 리스트 데이터. ******************/ + // 조회된 조건에 맞는 시도 시군 유역 const [filteredSigunData, setFilteredSigunData] = useState([{ text: "전체", val: '0' }]); const [filteredUnitWaterShedData, setFilteredUnitWaterShedData] = useState([{ text: "전체", val: '0' }]); const [filteredMiddleRegionData, setFilteredMiddleRegionData] = useState([{ text: "전체", val: '0' }]); - + // 조회된 조건에 맞는 환경청 시도 시군 const [filteredEnvPartData, setFilteredEnvPartData] = useState([{ text: "전체", val: '0', adstDliStrCd:'' }]); const [filteredEnvPartSidoData, setFilteredEnvPartSidoData] = useState([{ text: "전체", val: '0', adstDliStrCd:'' }]); const [filteredEnvPartSigunData, setFilteredEnvPartSigunData] = useState([{ text: "전체", val: '0' }]); - const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState([]); - // 시군구 세팅 const initializeSigunData = (sidoCd: string) => { if (sidoCd === '0') { @@ -153,12 +157,10 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel setRtnPartValue(val); } // 구분 - if(val == '01') { - setShowPart('01'); + if(val == 'R') { initializeMiddleRegionData('0'); initializeUnitWaterShedData('0'); - } else if(val == '02') { - setShowPart('02'); + } else if(val == 'G') { setFilteredEnvPartData([ { text: "전체", val: '0', adstDliStrCd:'' }, ...selectEnvPartData.filter((item: EnvPartSelectDataType) => item.val !== null && item.adstDliStrCd !== '') @@ -166,7 +168,6 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel setEnvPartValue('0'); initializeCityDataByEnvPartSido('0'); } else { - setShowPart('0'); initializeSigunData('0'); onSelectValue(selectSigunData[0]?.val || '0', 'sigun'); } @@ -224,7 +225,7 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel onSelectValue(filteredEnvPartSidoData[0]?.val || '0', 'envPartSido'); break; case 'envPartSido' : - setEnvPartSidoValue(val); + setSidoValue(val); if (setRtnEnvPartSidoValue) { setRtnEnvPartSidoValue(val); } @@ -232,29 +233,68 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel onSelectValue(filteredEnvPartSigunData[0]?.val || '0', 'envPartSigun'); break; case 'envPartSigun' : - setEnvPartSigunValue(val); + setSigunValue(val); if (setRtnEnvPartSigunValue) { setRtnEnvPartSigunValue(val); } break; } } - + + // 리셋 함수 + const resetAllValues = () => { + setPartValue('A'); + setSidoValue('0'); + setSigunValue('0'); + setRegionValue('0'); + setUnitWaterShedValue('0'); + setMiddleRegionValue('0'); + setEnvPartValue('0'); + + setFilteredSigunData([{ text: "전체", val: '0' }]); + setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]); + setFilteredMiddleRegionData([{ text: "전체", val: '0' }]); + setFilteredEnvPartData([{ text: "전체", val: '0', adstDliStrCd: '' }]); + setFilteredEnvPartSidoData([{ text: "전체", val: '0', adstDliStrCd: '' }]); + setFilteredEnvPartSigunData([{ text: "전체", val: '0' }]); + + if (setRtnPartValue) setRtnPartValue('A'); + if (setRtnSidoValue) setRtnSidoValue('0'); + if (setRtnSigunValue) setRtnSigunValue('0'); + if (setRtnRegionValue) setRtnRegionValue('0'); + if (setRtnUnitWaterShedValue) setRtnUnitWaterShedValue('0'); + if (setRtnMiddleRegionValue) setRtnMiddleRegionValue('0'); + if (setRtnEnvPartValue) setRtnEnvPartValue('0'); + if (setRtnEnvPartSidoValue) setRtnEnvPartSidoValue('0'); + if (setRtnEnvPartSigunValue) setRtnEnvPartSigunValue('0'); + }; + + // 리셋 트리거 변경 감지 + useEffect(() => { + if (resetTrigger) { + resetAllValues(); + // 리셋 후 트리거를 false로 설정 + if (setRtnPartValue) { + setRtnPartValue('0'); + } + } + }, [resetTrigger]); + return ( isShowPart ? (
onSelectValue(val.toString(), 'part')} value={partValue} selectClass={undefined}/> - {showPart === '01' ? ( + {partValue === 'R' ? ( <> onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={undefined}/> onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={undefined}/> onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={undefined}/> - ) : showPart === '02' ? ( + ) : partValue === 'G' ? ( <> onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={undefined}/> - onSelectValue(val.toString(), 'envPartSido')} value={envPartSidoValue} selectClass={undefined}/> - onSelectValue(val.toString(), 'envPartSigun')} value={envPartSigunValue} selectClass={undefined}/> + onSelectValue(val.toString(), 'envPartSido')} value={sidoValue} selectClass={undefined}/> + onSelectValue(val.toString(), 'envPartSigun')} value={sigunValue} selectClass={undefined}/> ) : ( <> diff --git a/packages/common/business_components/src/ui/search_form_left/searchFormLeft.md b/packages/common/business_components/src/ui/search_form_left/searchFormLeft.md index 18b95b13f..04531c27f 100644 --- a/packages/common/business_components/src/ui/search_form_left/searchFormLeft.md +++ b/packages/common/business_components/src/ui/search_form_left/searchFormLeft.md @@ -3,45 +3,41 @@ #### 1. 컴포넌트 호출방법 ```js import { SearchFormLeft } from "@common/business_components"; + ..... - { - setPartValue(value); - }} - setRtnSidoValue={(value) => { - setSidoValue(value); - }} - setRtnSigunValue={(value) => { - setSigunValue(value); - }} - setRtnRegionValue={(value) => { - setRegionValue(value); - }} - setRtnUnitWaterShedValue={(value) => { - setUntitWaterShedValue(value); - }} - setRtnMiddleRegionValue={(value) => { - setMiddleRegionValue(value); - }} - setRtnEnvPartValue={(value) => { - setEnvPartValue(value); - }} - setRtnEnvPartSidoValue={(value) => { - setEnvPartSidoValue(value); - }} - setRtnEnvPartSigunValue={(value) => { - setEnvPartSigunValue(value); - }} + +// state 지정 +const [partValue, setPartValue] = useState("0"); +const [sidoValue, setSidoValue] = useState("0"); +const [sigunValue, setSigunValue] = useState("0"); +const [regionValue, setRegionValue] = useState("0"); +const [untitWaterShedValue, setUntitWaterShedValue] = useState("0"); +const [middleRegionValue, setMiddleRegionValue] = useState("0"); +const [envPartValue, setEnvPartValue] = useState("0"); +const [envPartSidoValue, setEnvPartSidoValue] = useState("0"); +const [envPartSigunValue, setEnvPartSigunValue] = useState("0"); +const [resetTrigger, setResetTrigger] = useState(false); +..... + + + ```   ㆍisShowPart : 검색단에서 구분(행정별,유역별,환경청별) 세팅 여부(보일 경우 : Y, 아닐경우 : N 혹은 미첨부) @@ -73,7 +69,11 @@ import { SearchFormLeft } from "@common/business_components";   ㆍsetRtnEnvPartSidoValue : 선택한 환경청의 시도 set함수(조회시 페이지에서 사용) -  ㆍsetRtnEnvPartSidoValue : 선택한 환경청 시도의 시군구 set함수(조회시 페이지에서 사용) +  ㆍsetRtnEnvPartSigunValue : 선택한 환경청 시도의 시군구 set함수(조회시 페이지에서 사용) + +  ㆍresetTrigger : 초기화 trigger 변수 + + #### 2. 세팅방법 ```js @@ -318,8 +318,58 @@ const [middleRegionValue, setMiddleRegionValue] = useState("0"); const [envPartValue, setEnvPartValue] = useState("0"); const [envPartSidoValue, setEnvPartSidoValue] = useState("0"); const [envPartSigunValue, setEnvPartSigunValue] = useState("0"); +const [resetTrigger, setResetTrigger] = useState(false); - + +const resetVal = () => { + console.log('=== 초기화 ==='); + console.log(selectSidoData); + + // SelectBox 초기화 - 각 selectArray의 첫 번째 옵션 값 사용 + const initialPartValue = selectPartData[0]?.val || "0"; + const initialSidoValue = selectSidoData[0]?.val || "0"; + const initialSigunValue = selectSigunData[0]?.val || "0"; + const initialRegionValue = selectRegionData[0]?.val || "0"; + const initialUnitWaterShedValue = selectUnitWaterShedData[0]?.val || "0"; + const initialMiddleRegionValue = selectMiddleRegionData[0]?.val || "0"; + const initialEnvPartValue = selectEnvPartData[0]?.val || "0"; + const initialEnvPartSidoValue = selectEnvPartData[0]?.val || "0"; + const initialEnvPartSigunValue = selectEnvPartData[0]?.val || "0"; + + // 상태값 설정 + setPartValue(initialPartValue); + setSidoValue(initialSidoValue); + setSigunValue(initialSigunValue); + setRegionValue(initialRegionValue); + setUntitWaterShedValue(initialUnitWaterShedValue); + setMiddleRegionValue(initialMiddleRegionValue); + setEnvPartValue(initialEnvPartValue); + setEnvPartSidoValue(initialEnvPartSidoValue); + setEnvPartSigunValue(initialEnvPartSigunValue); + + // 리셋 후 트리거를 false로 설정 + setTimeout(() => { + setResetTrigger(false); + }, 0); + } + +// 조회 함수 +const chkVal = useCallback(() => { + setResetPage(true); + // 조회 funciton + searchFcltyQltyList(1); +}, [searchFcltyQltyList]); + +// 그리드 page 넘어갈 때 필요한 함수 +const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchFcltyQltyList(page); +}, [searchFcltyQltyList]); + +// 테이블 호출 컴포넌트 + index !== undefined && cellClick(index)} headName={"시설별 수질현황"} tableData={mainList} headlist={upHeadList2} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange}> + + ``` diff --git a/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx b/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx new file mode 100644 index 000000000..cedc0bd7f --- /dev/null +++ b/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx @@ -0,0 +1,52 @@ +import { TableHead, TableRow } from "@common/components/ui"; + +interface TableUpperProps { + id: string; + title: string; + upName?: string; + upSequnce?: number; + row? : number; + rowSpan?: number; + colSpan?: number; +} + +const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => { + const maxRow = upHeadList.reduce((max, head) => Math.max(max, head.row || 0), 0); + return ( + <> + {Array.from({ length: maxRow + 1 }, (_, rowIndex) => ( + + {upHeadList.map((head: TableUpperProps) => { + if (head.row === rowIndex) { + return ( + + {head.title} + + ); + }else{ + return ( + + {head.title} + + ); + } + })} + + ))} + + ); +} + +export { HeadMakeColSpan }; diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index f6da92d7f..60c457544 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -1,18 +1,19 @@ -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; +import { Table, TableBody, TableCell, TableHeader, TableRow } from "@common/components"; import { useEffect, useState } from "react"; +import { HeadMakeColSpan } from "./HeadMakeColSpan"; import UiPageNations from "./uiPageNations"; type UiTableProps = { headName: React.ReactNode | undefined; - tableData: { [key: string]: string | number | boolean | object}[]; + tableData: { [key: string]:any}[]; headlist: UiHeadProps[]; pageSize: number; total: number; - children: React.ReactNode | undefined; cellClick : ((index:number|undefined, headId:string)=>void )| undefined; onPageChange: (page: number) => void; className?: string; resetPageNo?: boolean; + children?: React.ReactNode | undefined; } type UiHeadProps = { @@ -21,7 +22,7 @@ type UiHeadProps = { align?: string; } -export function UiTable({ tableData, headlist, pageSize, total, children, headName, cellClick, onPageChange, className = '', resetPageNo = false }: UiTableProps) { +export function UiTable({ tableData, headlist, pageSize, total, headName, children, cellClick, onPageChange, className = '', resetPageNo = false }: UiTableProps) { const [cur, setCur] = useState(1); const [idx, setIdx] = useState(-1); @@ -43,7 +44,7 @@ export function UiTable({ tableData, headlist, pageSize, total, children, headNa setIdx(index); } }; - + return ( <> {headName ?
@@ -51,31 +52,32 @@ export function UiTable({ tableData, headlist, pageSize, total, children, headNa
: <>}
+ {children?children: - {children ? <>{children} : - {headlist?.map((head) => ( - {head.title} - ))} - } - - - {tableData?.map((el: { [key: string]: string | number | boolean | object}, index) => ( - - {headlist?.map((head) => ( - handleCellClick(index, head.id)} - key={head.id + index} - > - {String(el[head.id] ?? '')} - - ))} - - ))} - + {children ? children : } + + } + + + {tableData.length > 0 ? tableData?.map((el: { [key: string]: any}, index) => ( + + {headlist?.map((head) => ( + head.id && head.id.length > 1 && ( + handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + {typeof el[head.id] === 'number' ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") : String(el[head.id] ?? '')} + + ) + ))} + + )) : <>} +
{total <= 0 ? <> : }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82f59a6cd..62c43bac1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -266,6 +266,9 @@ importers: tailwind-merge: specifier: ^3.0.1 version: 3.0.2 + xlsx: + specifier: ^0.18.5 + version: 0.18.5 zod: specifier: ^3.24.2 version: 3.24.2 @@ -325,7 +328,7 @@ importers: specifier: ^15.14.0 version: 15.15.0 postcss: - specifier: ^8.5.1 + specifier: ^8.5.3 version: 8.5.3 prettier: specifier: ^3.4.2 @@ -3853,6 +3856,11 @@ packages: hasBin: true dev: true + /adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + dev: false + /ajv-formats@2.1.1(ajv@8.17.1): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -4236,6 +4244,14 @@ packages: /caniuse-lite@1.0.30001701: resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} + /cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + dev: false + /chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -4325,6 +4341,11 @@ packages: engines: {node: '>=6'} dev: false + /codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + dev: false + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -4402,6 +4423,12 @@ packages: typescript: 5.6.3 dev: true + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: false + /cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -5516,6 +5543,11 @@ packages: mime-types: 2.1.35 dev: false + /frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + dev: false + /fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true @@ -7571,6 +7603,13 @@ packages: whatwg-url: 7.1.0 dev: true + /ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + dependencies: + frac: 1.1.2 + dev: false + /stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} dev: true @@ -8535,11 +8574,21 @@ packages: dependencies: isexe: 2.0.0 + /wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + dev: false + /word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} dev: true + /word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + dev: false + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -8569,6 +8618,20 @@ packages: optional: true dev: true + /xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + dev: false + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'}