657 lines
22 KiB
TypeScript
657 lines
22 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";
|
|
import moment from "moment";
|
|
import {
|
|
codeToText,
|
|
formatCommaNumber,
|
|
formatYyyymmdd,
|
|
handleCommaNumberChange
|
|
} from "./common";
|
|
|
|
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;
|
|
};
|
|
|
|
// 재이용시설등록 > 지도점검결과 등록 - table body
|
|
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<string, Set<number>>();
|
|
|
|
// console.log(makeTableData);
|
|
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-normal break-words"
|
|
style={{ width: head.widthPercent ? `${head.widthPercent}%` : undefined }}
|
|
>
|
|
{key === "checkBox" ? (
|
|
<Checkbox
|
|
checked={selectedIds.includes(row.chkgdnSn)}
|
|
onCheckedChange={(checked) =>
|
|
toggleSelection(row.chkgdnSn, checked === true)
|
|
}
|
|
disabled={disable}
|
|
></Checkbox>
|
|
) : key === "order" ? (
|
|
++order
|
|
) : // value
|
|
["anlOperCst", "dtfcltCpct", "prcsCpct"].includes( //연간운영비, 저류조용량, 처리용량
|
|
head.id
|
|
) ? (
|
|
formatCommaNumber(value)
|
|
) : key === "chckYmd" ? (
|
|
<>{formatYyyymmdd(value)}</>
|
|
) :
|
|
(
|
|
<>{value}</>
|
|
)}
|
|
</TableCell>
|
|
);
|
|
}
|
|
|
|
// 기본 셀
|
|
return (
|
|
<TableCell
|
|
key={key}
|
|
className="text-center whitespace-normal break-words"
|
|
onClick={() => handleCellClick(rowIndex, head.id)}
|
|
style={{ width: head.widthPercent ? `${head.widthPercent}%` : undefined }}
|
|
>
|
|
{head.edit === true ? (
|
|
[
|
|
"levyAmt",
|
|
"anlOperCst",
|
|
"dtfcltCpct",
|
|
"prcsCpct",
|
|
].includes(head.id) ? (
|
|
<Input
|
|
className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`}
|
|
type={head.type}
|
|
onChange={(e) => {
|
|
handleCommaNumberChange(e, (val) =>
|
|
handleChange(rowIndex, head.id, val)
|
|
);
|
|
}}
|
|
value={formatCommaNumber(value) || ""}
|
|
disabled={!selectedIds.includes(row.chkgdnSn)}
|
|
maxLength={numSize}
|
|
onInput={(e: React.FormEvent<HTMLInputElement>) => {
|
|
const el = e.currentTarget;
|
|
if (head.type === "number" && el.value.length > numSize) {
|
|
el.value = el.value.slice(0, numSize);
|
|
}
|
|
}}
|
|
/>
|
|
) : ["vltnDt"].includes(head.id) ? (
|
|
<Input
|
|
type="date"
|
|
onChange={(e) => {
|
|
// 브라우저에서 선택한 값은 "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)}
|
|
/>
|
|
) : (
|
|
<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}
|
|
onInput={(e: React.FormEvent<HTMLInputElement>) => {
|
|
const el = e.currentTarget;
|
|
if (head.type === "number" && el.value.length > numSize) {
|
|
el.value = el.value.slice(0, numSize);
|
|
}
|
|
}}
|
|
/>
|
|
)
|
|
) : ["smll"].includes(head.id) && head.type === "text" ? (
|
|
<>-</>
|
|
) : [
|
|
"totalSplyUswtrUtztnQnt",
|
|
"totalRuseFcltUtztnQnt",
|
|
"splyUswtrUtztnQnt",
|
|
"ruseFcltUtztnQnt",
|
|
"levyAmt",
|
|
].includes(head.id) ? (
|
|
<span
|
|
className={
|
|
[
|
|
"totalSplyUswtrUtztnQnt",
|
|
"totalRuseFcltUtztnQnt",
|
|
"splyUswtrUtztnQnt",
|
|
"ruseFcltUtztnQnt",
|
|
].includes(head.id)
|
|
? "underline text-blue-600 cursor-pointer"
|
|
: ""
|
|
}
|
|
>
|
|
{formatCommaNumber(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,
|
|
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;
|
|
})
|
|
);
|
|
};
|
|
|
|
const handleTextChange = (
|
|
rowIndex: number,
|
|
field: any,
|
|
value: string,
|
|
setInputData: (data: any) => void
|
|
) => {
|
|
setInputData((prevData: any) =>
|
|
prevData?.map((row: any, index: number) => {
|
|
if (index === rowIndex) {
|
|
return {
|
|
...row,
|
|
[field]: value,
|
|
};
|
|
}
|
|
return row;
|
|
})
|
|
);
|
|
};
|
|
|
|
|
|
// 재이용실적등록 > 지도점검결과 등록 - 상세 modal의 body
|
|
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 === "utztnUsgCdText";
|
|
const textAlignClass = isTextColumn
|
|
? "text-center"
|
|
: "text-right";
|
|
|
|
// 총합계 행이면 "구분"에서 colSpan=2로 병합
|
|
if ( (data["구분"] === 0 || data["용수명"] === 0) && head.id === "구분" ) {
|
|
return (
|
|
<TableCell
|
|
key={head.id + headIndex}
|
|
colSpan={2}
|
|
className="font-bold text-center bg-[#d5e1eb]"
|
|
>
|
|
총합계
|
|
</TableCell>
|
|
);
|
|
}
|
|
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
|
|
if ( (data["구분"] === 0 || data["용수명"] === 0) && head.id === "용수명" ) {
|
|
return null;
|
|
}
|
|
|
|
// 총합계 행의 나머지 셀에도 색상 적용
|
|
if (data["구분"] === 0 || data["용수명"] === 0) {
|
|
return (
|
|
<TableCell
|
|
key={head.id + headIndex}
|
|
className="bg-[#d5e1eb] font-bold text-right"
|
|
>
|
|
{Number(data[head.id])
|
|
? formatCommaNumber(data[head.id as keyof any])
|
|
: (data[head.id as keyof any] ?? "")}
|
|
</TableCell>
|
|
);
|
|
}
|
|
// 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}`}
|
|
>
|
|
{Number(data[head.id])
|
|
? formatCommaNumber(data[head.id as keyof any])
|
|
: (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}`}
|
|
>
|
|
{head.id === "smll" ? (
|
|
<Input
|
|
className="text-center !text-xl h-10 w-full"
|
|
type="text"
|
|
onChange={(e) =>
|
|
handleTextChange(
|
|
dataIndex,
|
|
head.id,
|
|
e.target.value,
|
|
setInputData
|
|
)
|
|
}
|
|
value={String(data[head.id as keyof any] ?? "")}
|
|
maxLength={numSize}
|
|
/>
|
|
) : (
|
|
<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) =>
|
|
handleCommaNumberChange(e, (val) =>
|
|
handleChange(
|
|
dataIndex,
|
|
head.id,
|
|
String(val),
|
|
setInputData
|
|
)
|
|
)
|
|
}
|
|
value={formatCommaNumber(data[head.id as keyof any]) ?? ""}
|
|
maxLength={numSize}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
) : (
|
|
<TableCell
|
|
key={head.id + headIndex}
|
|
className={`w-36 ${bgClass}`}
|
|
>
|
|
<div className={`${textAlignClass} !text-xl w-full h-10`}>
|
|
{Number(data[head.id])
|
|
? formatCommaNumber(data[head.id as keyof any])
|
|
: (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>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
// 재이용실적등록 > 하수재이용실적등록 목록 body - 하수처리수재이용 실적(m³) 등록결과
|
|
export const pbnstTableBody = (
|
|
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) => {
|
|
//냄새, ph면 계산 못하게 - 로 수정
|
|
if (["smll", "ph"].includes(head.id)) {
|
|
return (
|
|
<TableCell
|
|
key={head.id + headIndex}
|
|
className={`w-36 ${bgClass} text-center !text-xl`}
|
|
>
|
|
<span>-</span>
|
|
</TableCell>
|
|
);
|
|
}
|
|
const isTextColumn = head.id === "구분" || head.id === "용수명";
|
|
const textAlignClass = isTextColumn
|
|
? "text-center"
|
|
: "text-right";
|
|
|
|
// 총합계 행이면 "구분"에서 colSpan=2로 병합
|
|
if (
|
|
(data["구분"] === 0 || data["용수명"] === 0) &&
|
|
head.id === "구분"
|
|
) {
|
|
return (
|
|
<TableCell
|
|
key={head.id + headIndex}
|
|
colSpan={2}
|
|
className="font-bold text-center bg-[#d5e1eb]"
|
|
>
|
|
총합계
|
|
</TableCell>
|
|
);
|
|
}
|
|
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
|
|
if (
|
|
(data["구분"] === 0 || data["용수명"] === 0) &&
|
|
head.id === "용수명"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
// 총합계 행의 나머지 셀에도 색상 적용
|
|
if (data["구분"] === 0 || data["용수명"] === 0) {
|
|
return (
|
|
<TableCell
|
|
key={head.id + headIndex}
|
|
className="bg-[#d5e1eb] font-bold text-right"
|
|
>
|
|
{Number(data[head.id])
|
|
? formatCommaNumber(data[head.id as keyof any])
|
|
: (data[head.id as keyof any] ?? "")}
|
|
</TableCell>
|
|
);
|
|
}
|
|
// 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}`}
|
|
>
|
|
{Number(data[head.id])
|
|
? formatCommaNumber(data[head.id as keyof any])
|
|
: (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) =>
|
|
handleCommaNumberChange(e, (val) =>
|
|
handleChange(
|
|
dataIndex,
|
|
head.id,
|
|
String(val),
|
|
setInputData
|
|
)
|
|
)
|
|
}
|
|
value={formatCommaNumber(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`}
|
|
>
|
|
{Number(data[head.id])
|
|
? formatCommaNumber(data[head.id as keyof any])
|
|
: (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>
|
|
)}
|
|
</>
|
|
);
|
|
};
|