Files
frontend/apps/wr/components/lib/func/TableData.tsx
T

344 lines
11 KiB
TypeScript

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";
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
);
const operMngCd = root?.reuseBuilding.operMngCd;
flatRswtChk = {
...root,
...detail,
...findGuide,
...findPoluttion,
...flatRswtChk,
operMngCd: operMngCd,
};
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
) => {
let order = 0
// rowspan된 셀 렌더링 여부 추적용
const renderedMap = new Map<string, Set<number>>();
return (
<>
{/* {makeTableData.map((row, rowIndex) => ( */}
{makeTableData && makeTableData.length > 0 ? (makeTableData.map((row, rowIndex) => (
<TableRow key={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<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 === "checkBox" ? (
<Checkbox
checked={selectedIds.includes(row.chkgdnSn)}
onCheckedChange={(checked) =>
toggleSelection(row.chkgdnSn, checked === true)
}
></Checkbox>
) : key === "order" ? (
++order
) : (
value
)}
</TableCell>
);
}
// 기본 셀
return (
<TableCell
key={key}
className="text-center whitespace-nowrap"
onClick={() => handleCellClick(rowIndex, head.id)}
>
{head.edit === true ? (
<Input
className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`}
type={head.type}
onChange={(e) => {
handleChange(rowIndex, head.id, e.target.value);
}}
value={value || ""}
disabled={!selectedIds.includes(row.chkgdnSn)}
maxLength={numSize}
//input type number일때 slice 처리
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
if (head.type === 'number' && e.target.value.length > numSize) {
e.target.value = e.target.value.slice(0, numSize);
}
}}
/>
) : (
(head.id === "totalSplyUswtrUtztnQnt" || head.id === "totalRuseFcltUtztnQnt" || head.id === "splyUswtrUtztnQnt" || head.id === "ruseFcltUtztnQnt") ? (
<span className="underline text-blue-600 cursor-pointer">
{value}
</span>
) : (
<>{value}</>
)
)}
</TableCell>
);
})}
</TableRow>
))) : (
<TableRow>
<TableCell colSpan={headList.length} className="h-24 text-center text-gray-500">
조회된 결과가 없습니다.
</TableCell>
</TableRow>
)}
</>
);
};
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 | number,
setInputData: (data: any) => void
) => {
if (Number(value) < 0 || !Number(value)) {
value = 0;
} else {
value = Number(value);
}
setInputData((prevData: any) =>
prevData?.map((row: any, index: number) => {
if (index === rowIndex) {
return {
...row,
[field]: value,
};
}
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 (
<TableRow key={dataIndex + "row"}>
{headList.map((head, headIndex) => {
const isTextColumn = head.id === '구분' || head.id === '용수명';
const textAlignClass = isTextColumn ? 'text-center' : 'text-right';
// 총합계 행이면 "구분"에서 colSpan=2로 병합
if (
data["구분"] === 0 &&
head.id === "구분"
) {
return (
<TableCell
key={head.id + headIndex}
colSpan={2}
className="font-bold text-center bg-[#d5e1eb]"
>
총합계
</TableCell>
);
}
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
if (
data["구분"] === 0 &&
head.id === "용수명"
) {
return null;
}
// mergeCell 처리
if (head.mergeCell) {
const rowSpan = getRowSpan(chgRes, head.id, dataIndex);
if (rowSpan === 0) return null;
return (
<TableCell key={head.id + headIndex} rowSpan={rowSpan} className={`w-36 align-middle text-center whitespace-nowrap ${bgClass}`}>
{data[head.id as keyof any] ?? ""}
</TableCell>
);
}
// 기본 셀
// return head.edit === true && data.edit !== false ? (
return isEditable && head.edit === true && data.edit !== false ? (
<TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}>
<Input
// className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`}
className={`${textAlignClass} !text-xl h-10 w-full`}
onChange={(e) =>
handleChange(dataIndex, head.id, e.target.value, setInputData)
}
value={data[head.id as keyof any] ?? ""}
maxLength={numSize}
/>
</TableCell>
) : (
<TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}>
<div
// className={`
// ${head.id !== "utztnUsgCd" ? "text-right" : "text-center"}
// !text-xl w-full h-10
// `}
className={`${textAlignClass} !text-xl w-full h-10`}
>
{data[head.id as keyof any] ?? ""}
</div>
</TableCell>
);
})}
</TableRow>
);
})}
{chgRes.length === 0 && (
<TableRow>
<TableCell colSpan={headList.length} className="h-24 text-center text-gray-500">
조회된 결과가 없습니다.
</TableCell>
</TableRow>
)}
</>
);
};