하수재이용 시설 차수 등록 버튼 수정 및 빗물 지도점검 데이터 유형 변경 (ph) 수정

This commit is contained in:
leedj312
2025-08-04 13:07:52 +09:00
parent 9973b35802
commit 2c6327bc3a
7 changed files with 204 additions and 92 deletions
@@ -63,7 +63,7 @@ export const rainChkZod = z.object({
z.object({
chkgdnSn: z.number().nullable(),
utztnUsgCd: z.string(),
ph: z.number().nullable(),
ph:z.string().nullish(),
trbt: z.number().nullable(),
grssClfg: z.number().nullable(),
etc: z.string().nullable(),
+137 -57
View File
@@ -2,8 +2,17 @@ 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 { formatCommaNumber, handleCommaNumberChange } from "./commom";
import {
Checkbox,
Input,
TableCell,
TableRow,
} from "@common/components/ui";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "./commom";
import moment from "moment";
export const rainChkTableData = (chkData: rainChkZodType[]) => {
// 1. chkgdnSn + utztnUsgCd 기준 누계 정보 만들기
@@ -57,9 +66,7 @@ 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 = {};
@@ -96,20 +103,19 @@ export const chRowspanTable = (
toggleSelection: (pk: number, checked: boolean) => void,
selectedIds: number[],
handleCellClick: (index: number, headId: string) => void,
disable? : boolean
disable?: boolean
) => {
let order = 0
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) => {
{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];
@@ -118,7 +124,6 @@ export const chRowspanTable = (
if (isMerged) {
const renderedSet = renderedMap.get(key) ?? new Set<number>();
if (renderedSet.has(rowIndex)) {
return null; // 이미 렌더링된 셀은 skip
}
@@ -143,13 +148,13 @@ export const chRowspanTable = (
></Checkbox>
) : key === "order" ? (
++order
) : // value
["anlOperCst", "dtfcltCpct", "prcsCpct"].includes(
head.id
) ? (
formatCommaNumber(value)
) : (
// value
(["anlOperCst", "dtfcltCpct", "prcsCpct"].includes(head.id)) ? (
formatCommaNumber(value)
) : (
<>{value}</>
)
<>{value}</>
)}
</TableCell>
);
@@ -163,23 +168,48 @@ export const chRowspanTable = (
onClick={() => handleCellClick(rowIndex, head.id)}
>
{head.edit === true ? (
(["levyAmt", "anlOperCst", "dtfcltCpct", "prcsCpct"].includes(head.id)) ? (
[
"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))
handleCommaNumberChange(e, (val) =>
handleChange(rowIndex, head.id, val)
);
}}
value={formatCommaNumber(value) || ""}
disabled={!selectedIds.includes(row.chkgdnSn)}
maxLength={numSize}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
if (head.type === 'number' && e.target.value.length > numSize) {
if (
head.type === "number" &&
e.target.value.length > numSize
) {
e.target.value = e.target.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`}
@@ -191,28 +221,50 @@ export const chRowspanTable = (
disabled={!selectedIds.includes(row.chkgdnSn)}
maxLength={numSize}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
if (head.type === 'number' && e.target.value.length > numSize) {
if (
head.type === "number" &&
e.target.value.length > numSize
) {
e.target.value = e.target.value.slice(0, numSize);
}
}}
/>
)
) : [
"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>
) : (
(["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}</>
)
<>{value}</>
)}
</TableCell>
);
})}
</TableRow>
))) : (
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={headList.length} className="h-24 text-center text-gray-500">
<TableCell
colSpan={headList.length}
className="h-24 text-center text-gray-500"
>
조회된 결과가 없습니다.
</TableCell>
</TableRow>
@@ -221,16 +273,17 @@ export const chRowspanTable = (
);
};
export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean) => {
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
if (head.edit !== undefined) head.edit = isEdit;
if (head.id === "checkBox") {
head.visible = false;
}
});
}
};
const handleChange = (
rowIndex: number,
@@ -253,8 +306,6 @@ const handleChange = (
})
);
};
;
export const InputTableBody = (
chgRes: any[] | [],
headList: TableUpperProps[],
@@ -289,8 +340,10 @@ export const InputTableBody = (
return (
<TableRow key={dataIndex + "row"}>
{headList.map((head, headIndex) => {
const isTextColumn = head.id === '구분' || head.id === '용수명';
const textAlignClass = isTextColumn ? 'text-center' : 'text-right';
const isTextColumn = head.id === "구분" || head.id === "용수명";
const textAlignClass = isTextColumn
? "text-center"
: "text-right";
// 총합계 행이면 "구분"에서 colSpan=2로 병합
if (
@@ -318,8 +371,13 @@ export const InputTableBody = (
// 총합계 행의 나머지 셀에도 색상 적용
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
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>
);
}
@@ -328,21 +386,35 @@ export const InputTableBody = (
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
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}`}>
<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)
handleCommaNumberChange(e, (val) =>
handleChange(
dataIndex,
head.id,
String(val),
setInputData
)
)
}
value={formatCommaNumber(data[head.id as keyof any]) ?? ""}
@@ -350,15 +422,20 @@ export const InputTableBody = (
/>
</TableCell>
) : (
<TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}>
<TableCell
key={head.id + headIndex}
className={`w-36 ${bgClass}`}
>
<div
// className={`
// ${head.id !== "utztnUsgCd" ? "text-right" : "text-center"}
// !text-xl w-full h-10
// ${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] ?? ""}
{Number(data[head.id])
? formatCommaNumber(data[head.id as keyof any])
: (data[head.id as keyof any] ?? "")}
</div>
</TableCell>
);
@@ -368,11 +445,14 @@ export const InputTableBody = (
})}
{chgRes.length === 0 && (
<TableRow>
<TableCell colSpan={headList.length} className="h-24 text-center text-gray-500">
<TableCell
colSpan={headList.length}
className="h-24 text-center text-gray-500"
>
조회된 결과가 없습니다.
</TableCell>
</TableRow>
)}
</>
);
};
};
+10
View File
@@ -150,6 +150,15 @@ export const formatCommaNumber = (
return num.toLocaleString();
};
//내가 쓰는 값이 숫자이냐 아니냐
export const isNumeric =(value: string) => {
if(value&&value.length>0){
return value?.trim() !== "" && !isNaN(Number(value));
}else{
return false
}
}
export const getCityOrSgg = (ref: RefObject<ReturnType>) => {
let con;
if (ref.current != null)
@@ -163,6 +172,7 @@ export const getCityOrSgg = (ref: RefObject<ReturnType>) => {
export const safeDate = (value: any): Date | undefined => {
const date = new Date(value);
console.log(' date ', date)
return isNaN(date.getTime()) ? undefined : date;
};
@@ -24,10 +24,11 @@ import { SelectBox } from "@common/business_components";
import { DatePicker, FormUi, UiModal } from "@common/business_components/ui";
import { Form, FormField } from "@common/business_components/ui/form";
import { Button, Input, Label } from "@common/components";
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useFieldArray, type UseFormReturn } from "react-hook-form";
import MngRegFormDetail from "../mngReg/mngRegFormDetail/mngRegFormDetail";
import {
//confirmToast,
formatCommaNumber,
handleCommaNumberChange,
safeDate,
@@ -38,6 +39,9 @@ import type { DemandCompType } from "@common/business_components/ui/sdmInsertFor
import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm";
import type { SelectDataType } from "@common/business_components";
import { BuseWaterQulity } from "./buseWaterQulity/buseWaterQulity";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
//import { useTranslation } from "react-i18next";
export function PbnstBuseForm({
indexMng = 0,
@@ -54,6 +58,8 @@ export function PbnstBuseForm({
setIndexBiz: (index: number) => void;
readonly?: boolean;
}) {
const { t } = useTranslation("base");
const {
code,
pbnst,
@@ -157,22 +163,25 @@ export function PbnstBuseForm({
}
}, [fcBizs, indexBiz, setIndexBiz]);
// 2. Define a submit handler.
function addRuseFcltObj() {
const addRuseFcltObj = useCallback(async () => {
const formRuse = form.getValues(`items.${indexMng}`);
const formBizs = form.getValues(`items.${indexMng}.fcBizs`);
const pbnstBizCycle = formBizs.length;
// 🧩 새로운 fcBizs 기본 객체 생성 (필수값만 채우고 나머지는 default로)
const newItem = fcBizSchema.parse({
cycl: pbnstBizCycle + 1,
pbnstRuseFcltId: formRuse.pbnstRuseFcltId,
fcOrgwts: undefined,
pplInstls: undefined,
fcDemands: undefined,
});
appendBiz(newItem);
setIndexBiz(pbnstBizCycle); // 새로 추가된 차수로 포커스 이동
}
const newItem = fcBizSchema.parse({
cycl: pbnstBizCycle + 1,
pbnstRuseFcltId: formRuse.pbnstRuseFcltId,
fcOrgwts: [],
pplInstls: [],
fcDemands: [],
});
await appendBiz(newItem);
toast.success(t('insertRuseBiz',{cycle: pbnstBizCycle + 1}))
setIndexBiz(pbnstBizCycle);
}, [form, indexMng, indexBiz, appendBiz, setIndexBiz]);
const { fields: fcDemands, append: appendDemand } = useFieldArray({
control: form.control,
name: `items.${indexMng}.fcBizs.${indexBiz}.fcDemands`,
@@ -206,7 +215,7 @@ export function PbnstBuseForm({
}
}, [ppiYn, indexMng, indexBiz]);
const plusHist = () => {
const plusHist = useCallback(() => {
if (
form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.pbnstRuseFcltBizSn`)
) {
@@ -227,9 +236,9 @@ export function PbnstBuseForm({
cpct: 0,
});
}
};
}, [indexMng, indexBiz, appendPpl, form]);
const plusArray = () => {
const plusArray = useCallback(() => {
appendDemand({
dmdCoNm: "",
daddr: "",
@@ -248,7 +257,7 @@ export function PbnstBuseForm({
dmdInfoId: "",
//telno: "",
});
};
}, [indexMng, indexBiz, appendDemand, form]);
const removeSelected = () => {
const nowPPl = form.getValues(
@@ -293,7 +302,7 @@ export function PbnstBuseForm({
}, [form.formState, indexMng, indexBiz]);
useEffect(() => {
console.log("form.formState ", form.formState);
//console.log("form.formState ", form.formState);
}, [form.formState]);
const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`);
@@ -489,17 +498,16 @@ export function PbnstBuseForm({
</div>
<div>
<BuseWaterQulity
h1Title={`하수처리수 재이용시설 보증수질 `}
subTitle="※재처리시설이 있는 경우만 작성"
form={form}
moClass="grid grid-cols-12 gap-4"
sonClass="col-span-4"
indexMng={indexMng}
indexBiz={indexBiz}
readonly={readonly}
/>
<BuseWaterQulity
h1Title={`하수처리수 재이용시설 보증수질 `}
subTitle="※재처리시설이 있는 경우만 작성"
form={form}
moClass="grid grid-cols-12 gap-4"
sonClass="col-span-4"
indexMng={indexMng}
indexBiz={indexBiz}
readonly={readonly}
/>
</div>
<div key={"hasu"}>
@@ -533,7 +541,10 @@ export function PbnstBuseForm({
수요처 신규등록
</Button>
</div>
<div ref={demandsContainerRef}>
<div
key={`fcDemands-${indexMng}-${indexBiz}`}
ref={demandsContainerRef}
>
{fcDemands.map((field, index) => (
<div
key={field.id} // useFieldArray의 고유 id를 key로 사용
@@ -39,7 +39,6 @@ export function BuseWaterQulity({
form.getValues(`items.${indexMng}.pbnstRuseFcltId`) +
String(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cycl`));
console.log("checkPkAndCycle ", checkPkAndCycle);
if (chekcPk !== checkPkAndCycle) {
if (watchPH && watchPH !== undefined) {
const [frontPH, backPH] = watchPH.split("~");
@@ -78,16 +78,26 @@ export default function RainChkgdTable({
useEffect(() => {
setSelectedIds([])// 셀렉트 초기화를 위한 작업
if (rainChkData && rainChkData.length > 0) {
const rainChg = rainChkData?.map((item: any) => {
const bud = tableData([item.building], { code });
const rainyDetails= item.rainyDetails.map((detail)=>{
return(
{
...detail,
// 일단 테이블에서 사용하기 위해 넘버 전환
ph: detail.ph&& !isNaN(detail.ph)?Number(detail.ph):detail.ph
}
)
})
return {
...item,
...bud[0],
rainyDetails:rainyDetails,
rowspan: item.rainyDetails?.length,
};
});
console.log('rainChg ', rainChg)
setMakeTableData(rainChkTableData(rainChg));
} else {
setMakeTableData([]);
@@ -116,7 +126,8 @@ export default function RainChkgdTable({
if (findRain != undefined) {
return {
...rd,
ph: Number(findRain.ph),
// 조드 타입과 맞추기위해 스트링
ph: String(findRain.ph),
trbt: Number(findRain.trbt),
grssClfg: Number(findRain.grssClfg),
etc: findRain.etc,
+2 -1
View File
@@ -22,5 +22,6 @@
"saveRowErrMsg": "수정을 원하는 행을 선택하고 저장버튼을 눌러주세요.",
"costCalcErrMsg": "공사비 계산 진행 후 저장을 눌러주세요",
"maxYearErrMsg": "최대 {{year}}년까지 선택가능합니다.",
"checkQtyMsg": "요구수량(m³/일)을 확인해주세요 계산할 수 없습니다."
"checkQtyMsg": "요구수량(m³/일)을 확인해주세요 계산할 수 없습니다.",
"insertRuseBiz": "{{cycle}}차 사업이 추가되었습니다."
}