import { numSize } from "@/app/store/common/code/basicCode"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { Checkbox, Input, TableCell, TableRow, } from "@common/components/ui"; import moment from "moment"; import { codeToText, formatCommaNumber, formatYyyymmdd, handleCommaNumberChange } from "./commom"; export const rainChkTableData = (chkData: rainChkZodType[]) => { // 1. chkgdnSn + utztnUsgCd 기준 누계 정보 만들기 const maketotal = chkData.map((item: rainChkZodType) => { const selectTotal = item.guideDetails.reduce((acc: any, cur: any) => { const code = cur.utztnUsgCd; const fk = cur.chkgdnSn; 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; // 모든 키: `${chkgdnSn}_${utztnUsgCd}` 형태 }); // 2. 누계 정보들을 하나의 객체로 합치기 (평탄화) const flatTotalMap = Object.assign({}, ...maketotal); // 3. rainyDetails에 누계 정보 주입 const updatedRainyDetails: any[] = []; chkData.forEach((rd: rainChkZodType) => { rd.rainyDetails.forEach((rainyItem: any) => { const key = `${rainyItem.chkgdnSn}_${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 rswtChkTableData = (chkData: rswtChkZodType[]) => { // 1. chkgdnSn + utztnUsgCd 기준 누계 정보 만들기 const makeRswtData: any[] = []; if (chkData && chkData.length > 0) { chkData?.forEach((root) => { root.rswtrDetails.forEach((detail) => { let flatRswtChk = {}; const findGuide = root.guideDetails.find( (guide) => guide.utztnUsgCd === detail.utztnUsgCd ) const findPoluttion = root.rswtrPollutions.find( (polution) => polution.utztnUsgCd === detail.utztnUsgCd ); flatRswtChk = { ...root, ...detail, ...findGuide, ...findPoluttion, ...flatRswtChk, utztnUsgCdText: codeToText(detail.utztnUsgCd, 'dmdUszCd'), }; makeRswtData.push(flatRswtChk); }); }); } return makeRswtData; }; 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, disable?: boolean ) => { let order = 0; // rowspan된 셀 렌더링 여부 추적용 const renderedMap = new Map>(); return ( <> {/* {makeTableData.map((row, rowIndex) => ( */} {makeTableData && makeTableData.length > 0 ? ( makeTableData.map((row, rowIndex) => ( {headList.map((head) => { const key = head.id; const isMerged = head.mergeCell; const value = row[key]; // 병합 대상이면 중복 렌더링 여부 판단 if (isMerged) { const renderedSet = renderedMap.get(key) ?? new Set(); 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 ( {key === "checkBox" ? ( toggleSelection(row.chkgdnSn, checked === true) } disabled={disable} > ) : key === "order" ? ( ++order ) : // value ["anlOperCst", "dtfcltCpct", "prcsCpct"].includes( head.id ) ? ( formatCommaNumber(value) ) : key === "chckYmd" ? ( <>{formatYyyymmdd(value)} ) : ( <>{value} )} ); } // 기본 셀 return ( handleCellClick(rowIndex, head.id)} > {head.edit === true ? ( [ "levyAmt", "anlOperCst", "dtfcltCpct", "prcsCpct", ].includes(head.id) ? ( { handleCommaNumberChange(e, (val) => handleChange(rowIndex, head.id, val) ); }} value={formatCommaNumber(value) || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} onInput={(e: React.FormEvent) => { const el = e.currentTarget; if (head.type === "number" && el.value.length > numSize) { el.value = el.value.slice(0, numSize); } }} /> ) : ["vltnDt"].includes(head.id) ? ( { // 브라우저에서 선택한 값은 "YYYY-MM-DD" const rawDate = e.target.value; // e.g. "2025-06-01" const yyyymmdd = moment(rawDate).format("YYYYMMDD"); handleChange(rowIndex, head.id, yyyymmdd); }} value={ value ? moment(value, "YYYYMMDD").format("YYYY-MM-DD") : "" } disabled={!selectedIds.includes(row.chkgdnSn)} /> ) : ( { handleChange(rowIndex, head.id, e.target.value); }} value={value || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} onInput={(e: React.FormEvent) => { const el = e.currentTarget; if (head.type === "number" && el.value.length > numSize) { el.value = el.value.slice(0, numSize); } }} /> ) ) : ["smll", "ph"].includes(head.id) && head.type === "text" ? ( <>- ) : [ "totalSplyUswtrUtztnQnt", "totalRuseFcltUtztnQnt", "splyUswtrUtztnQnt", "ruseFcltUtztnQnt", "levyAmt", ].includes(head.id) ? ( {formatCommaNumber(value)} ) : ( <>{value} )} ); })} )) ) : ( 조회된 결과가 없습니다. )} ); }; export const headVisibleChange = ( headList: TableUpperProps[], isEdit: boolean ) => { return headList.forEach((head) => { if (head.edit !== undefined) head.edit = isEdit; if (head.id === "checkBox") { head.visible = false; } }); }; 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][key] === data[rowIndex - 1][key]) { 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) => { //냄새, ph면 계산 못하게 - 로 수정 if (["smll", "ph"].includes(head.id)) { return ( - ); } const isTextColumn = head.id === "구분" || head.id === "용수명"; 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) { 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={numSize} /> ) : (
{Number(data[head.id]) ? formatCommaNumber(data[head.id as keyof any]) : (data[head.id as keyof any] ?? "")}
); })}
); })} {chgRes.length === 0 && ( 조회된 결과가 없습니다. )} ); };