diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index 363f07e82..ba0d34f44 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -1,4 +1,8 @@ -"use client"; +/** + * 재이용실적등록 > 지도점검결과등록 + */ + +"use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; @@ -43,25 +47,25 @@ import { useTranslation } from "react-i18next"; export default function Page() { const { - auth, - code, - rainChkgdn, - rswtChkgdn, - rainChkInit, - rainChkDetilUpdate, - rainChkDelete, - rainChkExcelDownload, - rainChkHeadChange, - rainChkNmSearch, - isRainSttn, - rswtChkgdnInit, - rswtChkNmSearch, - rswtChkPolInit, - rswtChkDetilsUpdate, - rswtChkDelete, - rswtrChkExcelDownload, - rswtChkHeadChange, - getLaeSeCdCheck + auth, // 사용자 권한 및 로그인 사용자 기준 정보 + code, // 공통 코드 및 지역 코드 정보 + rainChkgdn, // 빗물이용시설 지도점검 화면 상태 + rswtChkgdn, // 중수도시설 지도점검 화면 상태 + rainChkInit, // 빗물이용시설 지도점검 목록 초기 조회 + rainChkDetilUpdate, // 빗물이용시설 지도점검 상세 수정 저장 + rainChkDelete, // 빗물이용시설 지도점검 선택 건 삭제 + rainChkExcelDownload, // 빗물이용시설 지도점검 엑셀 양식/데이터 조회 + rainChkHeadChange, // 빗물이용시설 테이블 헤더 선택 상태 변경 + rainChkNmSearch, // 빗물이용시설명으로 목록 조회 + isRainSttn, // 빗물이용시설 상세/선택 상태 초기화 + rswtChkgdnInit, // 중수도시설 지도점검 목록 초기 조회 + rswtChkNmSearch, // 중수도시설명으로 목록 조회 + rswtChkPolInit, // 중수도 시설구분 변경 시 조회조건 초기화 + rswtChkDetilsUpdate, // 중수도시설 지도점검 상세 수정 저장 + rswtChkDelete, // 중수도시설 지도점검 선택 건 삭제 + rswtrChkExcelDownload, // 중수도시설 지도점검 엑셀 양식/데이터 조회 + rswtChkHeadChange, // 중수도시설 테이블 헤더 선택 상태 변경 + getLaeSeCdCheck // 사용자 관할 기관 코드 조회 } = useWrStore((state) => { return state; }); @@ -83,12 +87,13 @@ export default function Page() { String(new Date().getFullYear()) ); - const useInitRef = useRef(false); + // laeSeCd가 아직 준비되기 전 초기 렌더에서 rainChkInit, rswtChkgdnInit 같은 초기 조회 로직이 먼저 도는 것을 막는다. + const useInitRef = useRef(false); //초기 마운트가 한 번 끝났는지 useEffect(() => { getLaeSeCdCheck(); - useInitRef.current = true; + useInitRef.current = true; // getLaeSeCdCheck()를 호출한 뒤 true로 바뀜 }, []); @@ -111,8 +116,8 @@ export default function Page() { } else { // 중수도 if (fcltNm) { rswtChkNmSearch( - rainPageNation, - rainPageSize, + rswtPageNation, + rswtPageSize, searchYear, laeSeCd, fcltNm @@ -145,7 +150,7 @@ export default function Page() { if (fcltNm) { rswtChkNmSearch( pageNo, - rainPageSize, + rswtPageSize, searchYear, laeSeCd, fcltNm diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx index 96d44fe36..4838624a2 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx @@ -1,4 +1,7 @@ -"use client"; +/** + * 재이용실적등록 > 하수재이용실적등록 + */ +"use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel"; @@ -15,7 +18,7 @@ import { yearOptions, } from "@/components/lib/func/common"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { InputTableBody } from "@/components/lib/func/TableData"; +import { pbnstTableBody } from "@/components/lib/func/TableData"; import PbnPrfExcelModal from "@/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal"; import { ContentsDiv, @@ -355,7 +358,7 @@ export default function PrfmncPrfmncReg() { {headMakeprfmnc(monthHeadList, [])} - {InputTableBody( + {pbnstTableBody( inputData ?? [], monthHeadList, setInputData, diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx index adb93cf22..dc3348a0a 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx @@ -6,7 +6,7 @@ import { initValInUsage, initValOutUsage, subTotalObj, totalObj, type FcPrfType, import { excelPrfmncSttnParsing } from "@/app/store/prfmncSttn/pbnstPrfmncSttn/excel"; import { excelDownloadNumStyle, yearOptions } from "@/components/lib/func/common"; import { type SheetParameter } from "@common/business_components/ui"; -import { InputTableBody } from "@/components/lib/func/TableData"; +import { pbnstTableBody } from "@/components/lib/func/TableData"; import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SearchBaseForm, SearchInput, SelectBox, UiTable, type ChartConfig, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; import { Label, Table, TableBody, TableHeader } from "@common/components/ui"; @@ -714,8 +714,8 @@ export default function PbnstPrfmncSttn() { {headMakeprfmnc(monthHeadList, [])} - {/* {InputTableBody(prfData ?? [], monthHeadList, setPrfData)} */} - {InputTableBody(prfData ?? [], monthHeadList, setPrfData, false)} + {/* {pbnstTableBody(prfData ?? [], monthHeadList, setPrfData)} */} + {pbnstTableBody(prfData ?? [], monthHeadList, setPrfData, false)} diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx index b615b6532..538135d0a 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -137,68 +137,25 @@ export const rainSplHeadNmExport: chHeadType[] = [ ]; export const rainChkgdnHeadListExport: TableUpperProps[] = [ - { id: "order", title: "순번", mergeCell: true }, - { id: "checkBox", title: "선택", mergeCell: true }, - { id: "cityText", title: "시도", mergeCell: true }, - { id: "sggText", title: "시군구", mergeCell: true }, - { id: "bdstNm", title: "시설명", mergeCell: true }, - { id: "operMngCdText", title: "운영관리형태", mergeCell: true }, - { id: "oilqMeasMthdText", title: "유량측정방법", mergeCell: true }, - { id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true }, - { id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true }, - { id: "chckYmd", title: "점검일", mergeCell: true }, - { id: "utztnUsgCdText", title: "이용용도" }, - { id: "totalSplyUswtrUtztnQnt", title: "연간대체수이용량(m³)" }, - { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" }, - { - id: "ph", - title: "수소이온농도(pH)", - upName: "useQuilty", - upSequnce: 1, - type: "number", - edit: true, - }, - { - id: "trbt", - title: "탁도(NTU)", - upName: "useQuilty", - upSequnce: 2, - type: "number", - edit: true, - }, - { - id: "grssClfg", - title: "총대장균군수(개/mg)", - upName: "useQuilty", - upSequnce: 3, - type: "number", - edit: true, - }, - - { - id: "vltnDt", - title: "부과일(위반일)", - upName: "feeHist", - upSequnce: 1, - type: "text", - edit: true, - }, - { - id: "vltnMttr", - title: "위반사항", - upName: "feeHist", - upSequnce: 2, - type: "text", - edit: true, - }, - { - id: "levyAmt", - title: "부과금액(천원)", - upName: "feeHist", - upSequnce: 3, - type: "text", - edit: true, - }, + { id: "order", title: "순번", mergeCell: true, widthPercent: 4 }, + { id: "checkBox", title: "선택", mergeCell: true, widthPercent: 4 }, + { id: "cityText", title: "시도", mergeCell: true, widthPercent: 5 }, + { id: "sggText", title: "시군구", mergeCell: true, widthPercent: 5 }, + { id: "bdstNm", title: "시설명", mergeCell: true, widthPercent: 7 }, + { id: "operMngCdText", title: "운영관리형태", mergeCell: true, widthPercent: 6 }, + { id: "oilqMeasMthdText", title: "유량측정방법", mergeCell: true, widthPercent: 6 }, + { id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true, widthPercent: 7 }, + { id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true, widthPercent: 6 }, + { id: "chckYmd", title: "점검일", mergeCell: true, widthPercent: 5 }, + { id: "utztnUsgCdText", title: "이용용도", widthPercent: 5 }, + { id: "totalSplyUswtrUtztnQnt", title: "연간대체수이용량(m³)", widthPercent: 7 }, + { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)", widthPercent: 7 }, + { id: "ph", title: "수소이온농도(pH)", upName: "useQuilty", upSequnce: 1, type: "number", edit: true, widthPercent: 5 }, + { id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 2, type: "number", edit: true, widthPercent: 4 }, + { id: "grssClfg", title: "총대장균군수(개/mg)", upName: "useQuilty", upSequnce: 3, type: "number", edit: true, widthPercent: 5 }, + { id: "vltnDt", title: "부과일(위반일)", upName: "feeHist", upSequnce: 1, type: "text", edit: true, widthPercent: 5 }, + { id: "vltnMttr", title: "위반사항", upName: "feeHist", upSequnce: 2, type: "text", edit: true, widthPercent: 6 }, + { id: "levyAmt", title: "부과금액(천원)", upName: "feeHist", upSequnce: 3, type: "text", edit: true, widthPercent: 5 }, ]; export const ruseHeadListExport: TableUpperProps[] = [ diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx index fe2d87bc3..6e1114781 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx @@ -13,6 +13,11 @@ import { } from "../rainChkgdn/excel"; import { codeToText, textToCode } from "@/components/lib/func/common"; +const normalizeExcelDateInput = (value: unknown) => { + if (value === undefined || value === null) return ""; + return String(value).replace(/[.\-\/\s]/g, ""); +}; + export const rswtMakeExcelSample = ( buildings: Buildingtype[], sidoCode: SelectDataType[], @@ -142,7 +147,9 @@ export const excelToRswtGuideData = ( const rswtChkgdData: rswtChkZodType[] = excelData .map((item: any) => { if (item["점검일"] !== undefined) { - const checkYmd = item["점검일"].replace(/\./g, ""); + const checkYmd = normalizeExcelDateInput(item["점검일"]); + const operMngCd = textToCode(item["운영관리형태"], "rainRswtMng") ?? item["운영관리형태"]; + const utztnUsgCd = textToCode(item["이용용도"], "dmdUszCd") ?? item["이용용도"]; const makeGuidDetail = Array.from({ length: 12 }, (_, i) => { let month = `${i + 1}`; @@ -150,26 +157,27 @@ export const excelToRswtGuideData = ( const rswtData = parseRawValue(item[`중수_${month}월`]); if (month.length < 2) month = "0" + month; return { + utztnUsgCd: utztnUsgCd, ym: year + month, chckYmd: checkYmd, splyUswtrUtztnQnt: splyData !== "" ? splyData : 0, ruseFcltUtztnQnt: rswtData !== "" ? rswtData : 0, + flg: "RST", }; }); - if (checkYmd.length > 7) { - return { - bdstRuseFcltSn: Number(item["NO"]), - operMngCd: textToCode(item["운영관리형태"], "rainRswtMng") ??item["운영관리형태"], - anlOperCst: parseCommaRawValue(item["연간운영비(원/년)"]), - prcsCpct: parseRawValue(item[`처리용량(m³)`]), - spwtPositn: item["채수지점"], - chckYmd: checkYmd, - utztnUsgCd: textToCode(item["이용용도"], "dmdUszCd")??item["이용용도"], - guideDetails: makeGuidDetail, - rgtr: TEST_UUID, - mdfr: TEST_UUID, - }; - } + return { + bdstRuseFcltSn: Number(item["NO"]), + flg: "RST", + operMngCd: operMngCd, + anlOperCst: parseCommaRawValue(item["연간운영비(원/년)"]), + prcsCpct: parseRawValue(item[`처리용량(m³)`]), + spwtPositn: item["채수지점"], + chckYmd: checkYmd, + utztnUsgCd: utztnUsgCd, + guideDetails: makeGuidDetail, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + }; } }) .filter((el: any) => el !== undefined); @@ -186,11 +194,13 @@ export const excelToRswtPolution = (excelData: any) => { const rswtChkgdData: rswtChkZodType[] = excelData .map((item: any) => { if (item["점검일"] !== undefined) { - const checkYmd = item["점검일"].replace(/\./g, ""); - let vltnDt = item["과태료 부과일(위반일)"]; - if (vltnDt !== undefined) { - vltnDt = vltnDt.replace(/\./g, ""); - } + const checkYmd = normalizeExcelDateInput(item["점검일"]); + const vltnDt = normalizeExcelDateInput(item["과태료 부과일(위반일)"]); + const operMngCd = + textToCode(item["운영관리형태"], "rainRswtMng") ?? + item["운영관리형태"]; + const utztnUsgCd = + textToCode(item["이용용도"], "dmdUszCd") ?? item["이용용도"]; const makePoluttion = Array.from({ length: 4 }, (_, i) => { const quarter = `${i + 1}`; @@ -207,6 +217,7 @@ export const excelToRswtPolution = (excelData: any) => { const ph = String(item[`${quarter}분기_PH`]) ; const clIonQnt = parseRawValue(item[`${quarter}분기_염화물`]); return { + utztnUsgCd: utztnUsgCd, node: quarter, chckYmd: checkYmd, grssClfgUswtr: grssClfgUswtr, @@ -226,9 +237,10 @@ export const excelToRswtPolution = (excelData: any) => { return { bdstRuseFcltSn: Number(item["NO"]), + flg: "RST", chckYmd: checkYmd, - utztnUsgCd: textToCode(item["이용용도"], "dmdUszCd")??item["이용용도"], - operMngCd: item["운영관리형태"], + utztnUsgCd: utztnUsgCd, + operMngCd: operMngCd, anlOperCst: parseCommaRawValue(item["연간운영비(원/년)"]), prcsCpct: parseRawValue(item["처리용량(m³)"]), spwtPositn: item["채수지점"], @@ -237,7 +249,7 @@ export const excelToRswtPolution = (excelData: any) => { mdfr: TEST_UUID, rswtrDetails: [ { - utztnUsgCd: textToCode(item["이용용도"], "dmdUszCd")??item["이용용도"], + utztnUsgCd: utztnUsgCd, vltnDt: vltnDt, vltnMttr: item["위반사항"], levyAmt: Number(item["과태료 부과금액(천원)"]) ?? 0, @@ -257,6 +269,7 @@ export const rswtExcelDataCheck = ( rswtChkgdData: rswtChkZodType[], issues: string[] ) => { + // const chkMngType = { "01": "위탁", "02": "자체" }; rswtChkgdData.forEach((rswt: any) => { const defaultMessage = `*중수도 수질 [NO: ${rswt.bdstRuseFcltSn}, 점검일: ${rswt.chckYmd}, 이용용도: ${codeToText(rswt.utztnUsgCd ,"dmdUszCd")??rswt.utztnUsgCd}] 항목에서 `; @@ -264,6 +277,17 @@ export const rswtExcelDataCheck = ( if (rswt.chckYmd && isInvalidDate(rswt.chckYmd)) { issues.push(defaultMessage + `점검일: 날짜 형식 아님 (${rswt.chckYmd})`); } + // if (rswt.operMngCd?.length > 1 && !chkMngType[rswt.operMngCd]) { + // issues.push( + // defaultMessage + + // `운영관리형태: 위탁 혹은 자체만 허용 (${rswt.operMngCd})` + // ); + // } + // if (rswt.utztnUsgCd?.length > 2 && !codeToText(rswt.utztnUsgCd, "dmdUszCd")) { + // issues.push( + // defaultMessage + `이용용도: 허용되지 않은 값 (${rswt.utztnUsgCd})` + // ); + // } if (rswt.anlOperCst.length > 2 && isInvalidNumber(rswt.anlOperCst)) { issues.push( @@ -327,6 +351,11 @@ export const rswtExcelDataCheck = ( defaultMessage + `${pol.node}분기_T-N: 숫자 형식 아님 (${pol.tn})` ); } + if (pol.ph?.length > 1 && isInvalidNumber(pol.ph)) { + issues.push( + defaultMessage + `${pol.node}분기_PH: 숫자 형식 아님 (${pol.ph})` + ); + } if (isInvalidNumber(pol.clIonQnt)) { issues.push( diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx index 6e94ec4e6..9a8b65ced 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx @@ -53,61 +53,63 @@ const buildingZod = z.object({ }); export const rswtrPolutionsZode = z.object({ - utztnUsgCdText:z.string().nullish(), - chkgdnDtlFk: z.string().nullish(), - utztnUsgCd: z.string().nullish(), - node: z.string().nullish(), - grssClfgUswtr: z.number(), - combResdch: z.number(), - trbt: z.number(), - bod: z.number(), - smll: z.number(), - crmty: z.number(), - tph: z.number(), - tn: z.number(), - ph: z.number(), - clIonQnt: z.number(), - regDt: z.string().nullish(), - rgtr: z.string().nullish(), - mdfcnDt: z.string().nullish(), - mdfr: z.string().nullish(), - edit:z.boolean().nullish() + utztnUsgCdText:z.string().nullish(), // 이용용도 코드의 화면 표시용 한글명 + chkgdnDtlFk: z.string().nullish(), // 지도점검 상세 PK와 연결되는 FK + utztnUsgCd: z.string().nullish(), // 이용용도 코드 + node: z.string().nullish(), // 분기 구분값(1~4분기) + grssClfgUswtr: z.number(), // 총대장균군수 + combResdch: z.number(), // 결합잔류염소 + trbt: z.number(), // 탁도 + bod: z.number(), // BOD + smll: z.string(), // 냄새 + crmty: z.number(), // 색도 + tph: z.number(), // 총인(T-P) + tn: z.number(), // 총질소(T-N) + ph: z.number(), // 수소이온농도(pH) + clIonQnt: z.number(), // 염화물 + regDt: z.string().nullish(), // 등록일시 + rgtr: z.string().nullish(), // 등록자 + mdfcnDt: z.string().nullish(), // 수정일시 + mdfr: z.string().nullish(), // 수정자 + edit:z.boolean().nullish() // 화면에서 수정 가능 여부 }); export const rswtrDetailsZod = z.object({ - chkgdnDtlPk: z.string().nullish(), - chkgdnSn: z.string().nullish(), - utztnUsgCd: z.string(), - vltnDt: z.string(), - vltnMttr: z.string(), - levyAmt: z.number(), - regDt: z.string().nullish(), - rgtr: z.string().nullish(), - mdfcnDt: z.string().nullish(), - mdfr: z.string().nullish(), + chkgdnDtlPk: z.string().nullish(), // 지도점검 상세 PK + chkgdnSn: z.string().nullish(), // 지도점검 일련번호 + utztnUsgCd: z.string(), // 이용용도 코드 + vltnDt: z.string(), // 위반일 + vltnMttr: z.string(), // 위반사항 + levyAmt: z.number(), // 부과금액 + regDt: z.string().nullish(), // 등록일시 + rgtr: z.string().nullish(), // 등록자 + mdfcnDt: z.string().nullish(), // 수정일시 + mdfr: z.string().nullish(), // 수정자 }) +// 중수도 점검 데이터를 검증하는 Zod 스키마 export const rswtChkgdZod = z.object({ - chkgdnSn: z.number().nullish(), - bdstRuseFcltSn: z.number().nullish(), - chckYmd: z.string(), - prcsCpct: z.number(), - dtfcltCpct: z.number(), - spwtPositn: z.string(), - regDt: z.string().nullish(), - rgtr: z.string().nullish(), - mdfcnDt: z.string().nullish(), - mdfr: z.string().nullish(), - anlOperCst:z.number(), - oilqMeasMthd: z.string().nullish(), - utztnUsgCd: z.string(), - building: buildingZod, - guideDetails: z.array(guideDetail), - reuseBuilding: reuseBuildingZod, + chkgdnSn: z.number().nullish(), // 점검 일련번호 + bdstRuseFcltSn: z.number().nullish(), // 재이용시설 일련번호 + chckYmd: z.string(), // 점검일자 + prcsCpct: z.number(), // 처리용량 + dtfcltCpct: z.number(), // 빗물저류조용량 + spwtPositn: z.string(), // 채수 지점 + regDt: z.string().nullish(), // 등록일시 + rgtr: z.string().nullish(), // 등록자 + mdfcnDt: z.string().nullish(), // 수정일시 + mdfr: z.string().nullish(), // 수정자 + flg: z.string().nullable(), // 플래그 + anlOperCst: z.number(), // 연간 운영비 + oilqMeasMthd: z.string().nullish(), // 유량측정방법 + utztnUsgCd: z.string(), // 이용용도 코드 + building: buildingZod, // 건물 정보 + guideDetails: z.array(guideDetail), // 가이드 점검 상세 목록 + reuseBuilding: reuseBuildingZod, // 재이용 건물 정보 rswtrDetails: z.array( rswtrDetailsZod - ), - rswtrPollutions: z.array(rswtrPolutionsZode), + ), // 점검 위반/내역 상세 배열 + rswtrPollutions: z.array(rswtrPolutionsZode), // 수질 오염 항목 배열 }); @@ -134,117 +136,45 @@ export const rswtHeadNmExport: chHeadType[] = [ ]; export const rswtChkHeadListExport: TableUpperProps[] = [ - { id: "order", title: "순번", mergeCell: true }, - { id: "checkBox", title: "선택", mergeCell: true }, - { id: "cityText", title: "시도" ,mergeCell: true }, - { id: "sggText", title: "시군구",mergeCell: true }, - { id: "bdstNm", title: "시설명" ,mergeCell: true}, - { id: "operMngCd", title: "운영관리형태" ,mergeCell: true}, - { id: "anlOperCst", title: "연간운영비(원/년)" ,mergeCell: true}, - { id: "prcsCpct", title: "처리용량(m³)" ,mergeCell: true}, - { id: "spwtPositn", title: "채수지점" ,mergeCell: true}, - { id: "chckYmd", title: "점검일" ,mergeCell: true}, - { id: "utztnUsgCdText", title: "이용용도" }, - { id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number"}, - { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number"}, - - { - id: "grssClfgUswtr", - title: "총대장균군수(개/mg)", - upName: "useQuilty", - upSequnce: 1, - type: "number" - }, - { - id: "combResdch", - title: "결합잔류염소(mh/L)", - upName: "useQuilty", - upSequnce: 2, - type: "number" - }, - { id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3 ,type: "number"}, - { id: "bod", title: "BOD(mg/L)", upName: "useQuilty", upSequnce: 4 ,type: "number"}, - { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 ,type: "text"}, - { id: "crmty", title: "색도(도)", upName: "useQuilty", upSequnce: 6 ,type: "number"}, - { id: "tn", title: "총질소(T-N)(mg/L)", upName: "useQuilty", upSequnce: 7 ,type: "number"}, - { id: "tph", title: "총인(T-P)(mg/L)", upName: "useQuilty", upSequnce: 8 ,type: "number"}, - { id: "ph", title: "수소이온농도(pH)", upName: "useQuilty", upSequnce: 9,type: "text" }, - { id: "clIonQnt", title: "염화물(mg/L)", upName: "useQuilty", upSequnce: 10 ,type: "number"}, - - { - id: "vltnDt", - title: "부과일(위반일)", - upName: "feeHist", - upSequnce: 1, - type: "text", - edit:true - }, - { - id: "vltnMttr", - title: "위반사항", - upName: "feeHist", - upSequnce: 2, - type: "text", - edit:true - }, - { - id: "levyAmt", - title: "부과금액(천원)", - upName: "feeHist", - upSequnce: 3, - type: "number", - edit:true - }]; + { id: "order", title: "순번", mergeCell: true, widthPercent: 4 }, + { id: "checkBox", title: "선택", mergeCell: true, widthPercent: 4 }, + { id: "cityText", title: "시도" ,mergeCell: true, widthPercent: 7 }, + { id: "sggText", title: "시군구",mergeCell: true, widthPercent: 5 }, + { id: "bdstNm", title: "시설명" ,mergeCell: true, widthPercent: 10 }, + { id: "operMngCd", title: "운영관리형태" ,mergeCell: true, widthPercent: 6 }, + { id: "anlOperCst", title: "연간운영비(원/년)" ,mergeCell: true, widthPercent: 7 }, + { id: "prcsCpct", title: "처리용량(m³)" ,mergeCell: true, widthPercent: 6 }, + { id: "spwtPositn", title: "채수지점" ,mergeCell: true, widthPercent: 6 }, + { id: "chckYmd", title: "점검일" ,mergeCell: true, widthPercent: 7 }, + { id: "utztnUsgCdText", title: "이용용도", widthPercent: 7 }, + { id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number", widthPercent: 5 }, + { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number", widthPercent: 5 }, + { id: "grssClfgUswtr", title: "총대장균군수(개/mg)", upName: "useQuilty", upSequnce: 1 ,type: "number", widthPercent: 6 }, + { id: "combResdch", title: "결합잔류염소(mh/L)", upName: "useQuilty", upSequnce: 2 ,type: "number", widthPercent: 6 }, + { id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3 ,type: "number", widthPercent: 4 }, + { id: "bod", title: "BOD\n(mg/L)", upName: "useQuilty", upSequnce: 4 ,type: "number", widthPercent: 4 }, + { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 ,type: "text", widthPercent: 4 }, + { id: "crmty", title: "색도(도)", upName: "useQuilty", upSequnce: 6 ,type: "number", widthPercent: 4 }, + { id: "tn", title: "총질소(T-N)(mg/L)", upName: "useQuilty", upSequnce: 7 ,type: "number", widthPercent: 5 }, + { id: "tph", title: "총인(T-P)(mg/L)", upName: "useQuilty", upSequnce: 8 ,type: "number", widthPercent: 5 }, + { id: "ph", title: "수소이온농도(pH)", upName: "useQuilty", upSequnce: 9,type: "text", widthPercent: 5 }, + { id: "clIonQnt", title: "염화물(mg/L)", upName: "useQuilty", upSequnce: 10 ,type: "number", widthPercent: 5 }, + { id: "vltnDt", title: "부과일(위반일)", upName: "feeHist", upSequnce: 1, type: "text", edit:true, widthPercent: 5 }, + { id: "vltnMttr", title: "위반사항", upName: "feeHist", upSequnce: 2, type: "text", edit:true, widthPercent: 6 }, + { id: "levyAmt", title: "부과금액(천원)", upName: "feeHist", upSequnce: 3, type: "number", edit:true, widthPercent: 5 }]; export const rswtDetailHeadListExport: TableUpperProps[] = [ - { id: "utztnUsgCdText", title: "이용용도" }, - - { - id: "grssClfgUswtr", - title: "총대장균군수", - upName: "detailQulity", - upSequnce: 2, - edit: true, - }, - { - id: "combResdch", - title: "결합잔류염소", - upName: "detailQulity", - upSequnce: 3, - edit: true, - }, - { - id: "trbt", - title: "탁도", - upName: "detailQulity", - upSequnce: 4, - edit: true, - }, - { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5, edit: true }, - { - id: "smll", - title: "냄새", - upName: "detailQulity", - upSequnce: 6, - edit: true, - }, - { - id: "crmty", - title: "색도", - upName: "detailQulity", - upSequnce: 7, - edit: true, - }, - { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8, edit: true }, - { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9, edit: true }, - { id: "ph", title: "pH", upName: "detailQulity", upSequnce: 10, edit: true }, - { - id: "clIonQnt", - title: "염화물", - upName: "detailQulity", - upSequnce: 11, - edit: true, - }, + { id: "utztnUsgCdText", title: "이용용도", widthPercent: 20 }, + { id: "grssClfgUswtr", title: "총대장균군수", upName: "detailQulity", upSequnce: 2, edit: true, widthPercent: 8 }, + { id: "combResdch", title: "결합잔류염소", upName: "detailQulity", upSequnce: 3, edit: true, widthPercent: 8 }, + { id: "trbt", title: "탁도", upName: "detailQulity", upSequnce: 4, edit: true, widthPercent: 8 }, + { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5, edit: true, widthPercent: 8 }, + { id: "smll", title: "냄새", upName: "detailQulity", upSequnce: 6, edit: true, widthPercent: 8 }, + { id: "crmty", title: "색도", upName: "detailQulity", upSequnce: 7, edit: true, widthPercent: 8 }, + { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8, edit: true, widthPercent: 8 }, + { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9, edit: true, widthPercent: 8 }, + { id: "ph", title: "pH", upName: "detailQulity", upSequnce: 10, edit: true, widthPercent: 8 }, + { id: "clIonQnt", title: "염화물", upName: "detailQulity", upSequnce: 11, edit: true, widthPercent: 8 }, ]; - \ No newline at end of file + diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index b480692f3..3ce6cc2ff 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -41,19 +41,19 @@ export type rswtChkZodType = z.infer; export type rswtrChkDetailsZodType = z.infer; export type rswtChkgdnState = { rswtChkgdn: { - rswtChkHeadList: TableUpperProps[]; - rswtDetailHeadList: TableUpperProps[]; - - rswtHeadNm: chHeadType[]; - excelList: SheetParameter[]; - rswtChkRes: rswtChkZodType[]; - rswtChkPolGuide: rswtChkZodType[]; - rswtSplHeadNm:chHeadType[] - rswtRuseHeadNm:chHeadType[] - isInit: boolean; - isSttn: boolean; - isLoading:boolean; - rswtChkCount: number; + rswtChkHeadList: TableUpperProps[]; // 중수도 지도점검 메인 목록 헤더 + rswtDetailHeadList: TableUpperProps[]; // 중수도 지도점검 상세 목록 헤더 + + rswtHeadNm: chHeadType[]; // 메인 목록 다단 헤더명 + excelList: SheetParameter[]; // 엑셀 다운로드용 시트 데이터 + rswtChkRes: rswtChkZodType[]; // 중수도 지도점검 메인 목록 데이터 + rswtChkPolGuide: rswtChkZodType[]; // 선택한 지도점검 1건의 수질/대체수 상세 데이터 + rswtSplHeadNm:chHeadType[] // 공급량 관련 다단 헤더명 + rswtRuseHeadNm:chHeadType[] // 재이용량 관련 다단 헤더명 + isInit: boolean; // 초기 목록 조회 완료 여부 + isSttn: boolean; // 테이블 선택/상세 상태 제어 플래그 + isLoading:boolean; // 등록/업로드 등 비동기 처리 진행 여부 + rswtChkCount: number; // 메인 목록 전체 건수 }; }; @@ -242,27 +242,29 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( console.log(error); } }, + // 중수도 지도점검결과 upload rswtChkExceDataUpload: async (excelData: rswtChkZodType[],laeSeCd:string) => { const successMessage = "지도점검 등록이 완료되었습니다."; const failMessage = "지도점검 등록이 실패되었습니다."; + console.log(excelData); try { - set((state: rswtChkgdnType) => ( - { rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); + set((state: rswtChkgdnType) => ( + { rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); const res = await postFectch("/reuseChk/reuseWater", excelData); - if (!res.ok) { - return failMessage; + if (res.status === 200) { + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, laeSeCd); + set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, + }, + })); + return successMessage; } - const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, laeSeCd); - set((state: rswtChkgdnState) => ({ - ...state, - rswtChkgdn: { - ...state.rswtChkgdn, - rswtChkRes: rswtChkRes.data, - rswtChkCount: rswtChkRes.count, - }, - })); - return successMessage; + return failMessage; } catch (error) { console.log(error); return failMessage; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx index 1568d567b..c45a772e7 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx @@ -56,57 +56,57 @@ export const splSttnHeadListExport: TableUpperProps[] = [ { id: "spl12", title: "12월", upName: "splWater", upSequnce: 13 }, ]; -export const rswtDetailHeadListExport: TableUpperProps[] = [ - { id: "utztnUsgCdText", title: "이용용도" }, +// export const rswtDetailHeadListExport: TableUpperProps[] = [ +// { id: "utztnUsgCdText", title: "이용용도" }, - { - id: "grssClfgUswtr", - title: "총대장균군수", - upName: "detailQulity", - upSequnce: 2, - }, - { - id: "combResdch", - title: "결합잔류염소", - upName: "detailQulity", - upSequnce: 3, - }, - { - id: "trbt", - title: "탁도", - upName: "detailQulity", - upSequnce: 4, - }, - { - id: "bod", - title: "BOD", - upName: "detailQulity", - upSequnce: 5, - }, - { - id: "smll", - title: "냄새", - upName: "detailQulity", - upSequnce: 6, - }, - { - id: "crmty", - title: "색도", - upName: "detailQulity", - upSequnce: 7, - }, - { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8}, - { - id: "tph", - title: "T-P", - upName: "detailQulity", - upSequnce: 9, - }, - { id: "ph", title: "pH", upName: "detailQulity", upSequnce: 10 }, - { - id: "clIonQnt", - title: "염화물", - upName: "detailQulity", - upSequnce: 11, - }, -]; +// { +// id: "grssClfgUswtr", +// title: "총대장균군수", +// upName: "detailQulity", +// upSequnce: 2, +// }, +// { +// id: "combResdch", +// title: "결합잔류염소", +// upName: "detailQulity", +// upSequnce: 3, +// }, +// { +// id: "trbt", +// title: "탁도", +// upName: "detailQulity", +// upSequnce: 4, +// }, +// { +// id: "bod", +// title: "BOD", +// upName: "detailQulity", +// upSequnce: 5, +// }, +// { +// id: "smll", +// title: "냄새", +// upName: "detailQulity", +// upSequnce: 6, +// }, +// { +// id: "crmty", +// title: "색도", +// upName: "detailQulity", +// upSequnce: 7, +// }, +// { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8}, +// { +// id: "tph", +// title: "T-P", +// upName: "detailQulity", +// upSequnce: 9, +// }, +// { id: "ph", title: "pH", upName: "detailQulity", upSequnce: 10 }, +// { +// id: "clIonQnt", +// title: "염화물", +// upName: "detailQulity", +// upSequnce: 11, +// }, +// ]; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index e99bdb7a8..7eb4f5396 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -43,16 +43,16 @@ export type rainChkSttnType = rainChkSttnState & rainChkSttnActions; export const rainChkSttnStateExport: rainChkSttnState = { rainChkSttn: { - rainChkRes: [], - fcltDiv: fcltDiv, - rainChkgdnHeadList: rainChkgdnHeadList, - ruseHeadList: ruseHeadList, - splHeadList: splHeadList, - rainHeadNm: rainHeadNm, - isInit: false, - sttnExcelList: [], - isSttn: false, - rainChkCount: 1, + rainChkRes: [], // 빗물이용시설 지도점검 현황 메인 목록 데이터 + fcltDiv: fcltDiv, // 시설 구분 RST 중수도시설, RNB 빗물이용시설 + rainChkgdnHeadList: rainChkgdnHeadList, // 메인 목록 테이블 헤더 정의 + ruseHeadList: ruseHeadList, // 연간 빗물이용량 상세 테이블 헤더 정의 + splHeadList: splHeadList, // 연간 대체수이용량 상세 테이블 헤더 정의 + rainHeadNm: rainHeadNm, // 메인 목록 다단 헤더명 정의 + isInit: false, // 현황 화면 초기 조회 완료 여부 + sttnExcelList: [], // 엑셀 다운로드용 가공 데이터 + isSttn: false, // 등록/현황 화면 구분 및 읽기 전용 제어 플래그 + rainChkCount: 1, // 메인 목록 전체 건수 }, }; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx index 2eba48db1..21a78b07d 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx @@ -31,16 +31,15 @@ export const rswtChkSttnHeadListExport: TableUpperProps[] = [ ]; export const rswtDetailSttnHeadListExport: TableUpperProps[] = [ - { id: "utztnUsgCd", title: "이용용도" }, - - { id: "grssClfgUswtr", title: "총대장균군수", upName: "detailQulity", upSequnce: 2 }, - { id: "combResdch", title: "결합잔류염소", upName: "detailQulity", upSequnce: 3 }, - { id: "trbt", title: "탁도", upName: "detailQulity", upSequnce: 4 }, - { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5 }, - { id: "smll", title: "냄새", upName: "detailQulity", upSequnce: 6 }, - { id: "crmty", title: "색도", upName: "detailQulity", upSequnce: 7 }, - { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8 }, - { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9 }, - { id: "ph", title: "pH", upName: "detailQulity", upSequnce: 10 }, - { id: "clIonQnt", title: "염화물", upName: "detailQulity", upSequnce: 11 }, + { id: "utztnUsgCd", title: "이용용도", widthPercent: 20 }, + { id: "grssClfgUswtr", title: "총대장균군수", upName: "detailQulity", upSequnce: 2, widthPercent: 8 }, + { id: "combResdch", title: "결합잔류염소", upName: "detailQulity", upSequnce: 3, widthPercent: 8 }, + { id: "trbt", title: "탁도", upName: "detailQulity", upSequnce: 4, widthPercent: 8 }, + { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5, widthPercent: 8 }, + { id: "smll", title: "냄새", upName: "detailQulity", upSequnce: 6, widthPercent: 8 }, + { id: "crmty", title: "색도", upName: "detailQulity", upSequnce: 7, widthPercent: 8 }, + { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8, widthPercent: 8 }, + { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9, widthPercent: 8 }, + { id: "ph", title: "pH", upName: "detailQulity", upSequnce: 10, widthPercent: 8 }, + { id: "clIonQnt", title: "염화물", upName: "detailQulity", upSequnce: 11, widthPercent: 8 }, ]; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index a616c8041..35458abd5 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -69,6 +69,7 @@ type pubFacActions = { pbnstInsert(pbnstRes: customPbnst): Promise; fileDelete: (atflCd: string) => void; fileDownLoad: (atflCd: string) => void; + fileDownLoadName: (atflCd: string, fileName: string) => void; pbnstRuseFcltUpdate: (ruseRes: customPbnst[]) => void; pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void; pubHasuSearch: (page: string, sggOrCity: string, searchTxt: string) => void; @@ -297,6 +298,34 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { throw new Error(`Error downloading file: ${error}`); } }, + // 파일다운로드(원본 파일명 지정) : 화면에 보여지는 파일명으로 저장 + fileDownLoadName: async (atflCd: string, fileName: string) => { + try { + const response = await fetch( + `/attach-service/api/v1/atflcd/download/${atflCd}`, + { + method: "GET", + headers: { + accept: "application/octet-stream", + }, + } + ); + if (!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + console.log(response); + const blob = await response.blob(); + const blobUrl = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = blobUrl; + a.download = fileName || "downloaded_file"; + a.click(); + window.URL.revokeObjectURL(blobUrl); + } catch (error) { + console.error("Error downloading file with name:", error); + throw new Error(`Error downloading file with name: ${error}`); + } + }, //원수취수 팝업의 하수처리 시설 리스트 조회 getFcOrgwts: async (fcltPkList: string[], size: number) => { const fcOrgwtsNmList = await postFectch( diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx index 2895d28c5..5eae0cbfc 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx @@ -10,6 +10,7 @@ export const sttnHeadListType = z.array( }) ); +// 빗물이용시설 export const rainSttHeadListExport: z.infer = [ { id: "cityText", title: "시도", visible: true, widthPercent: 4 }, { id: "sggText", title: "시군구", visible: true, widthPercent: 4 }, @@ -41,7 +42,7 @@ export const rainSttHeadListExport: z.infer = [ { id: "grntchBdstCertGrdText", title: "녹색건축물인증등급", visible: true, widthPercent: 4 }, ]; -// id는 다확인해야함 +// export const rainSttNodeListExport: CommonTableProps[] = [ { id: "bdnstInfo", title: "건축물정보", visible: true, row: 1, rowSpan: 1, colSpan: 10, }, { id: "rainFcltInfo", title: "빗물이용시설정보", visible: true, row: 1, rowSpan: 1, colSpan: 7, }, diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 0e87ad63c..89d347c24 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -94,6 +94,7 @@ export const rswtChkTableData = (chkData: rswtChkZodType[]) => { return makeRswtData; }; +// 재이용시설등록 > 지도점검결과 등록 - table body export const chRowspanTable = ( makeTableData: any[], headList: TableUpperProps[], @@ -152,7 +153,7 @@ export const chRowspanTable = ( ) : key === "order" ? ( ++order ) : // value - ["anlOperCst", "dtfcltCpct", "prcsCpct"].includes( + ["anlOperCst", "dtfcltCpct", "prcsCpct"].includes( //연간운영비, 저류조용량, 처리용량 head.id ) ? ( formatCommaNumber(value) @@ -233,7 +234,7 @@ export const chRowspanTable = ( }} /> ) - ) : ["smll", "ph"].includes(head.id) && head.type === "text" ? ( + ) : ["smll"].includes(head.id) && head.type === "text" ? ( <>- ) : [ "totalSplyUswtrUtztnQnt", @@ -311,6 +312,9 @@ const handleChange = ( }) ); }; + + +// 재이용실적등록 > 지도점검결과 등록 - 상세 modal의 body export const InputTableBody = ( chgRes: any[] | [], headList: TableUpperProps[], @@ -336,6 +340,157 @@ export const InputTableBody = ( const isSubTotal = data["용수명"] === "소계"; const isTotal = data["구분"] === 0; + const bgClass = isTotal + ? "bg-[#d5e1eb] font-bold" + : isSubTotal + ? "bg-[#e6eef5] font-semibold" + : ""; + + return ( + + {headList.map((head, headIndex) => { + //냄새면 계산 못하게 - 로 수정 + // if (["smll"].includes(head.id)) { + // return ( + // + // - + // + // ); + // } + const isTextColumn = head.id === "구분" || head.id === "utztnUsgCdText"; + 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 && ( + + + 조회된 결과가 없습니다. + + + )} + + ); +}; + +// 재이용실적등록 > 하수재이용실적등록 목록 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 @@ -471,5 +626,4 @@ export const InputTableBody = ( )} ); -}; - +}; \ No newline at end of file diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx index eef115a5c..85364f0d1 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx @@ -44,7 +44,7 @@ const diagramFileTypes = ".pdf,.jpg,.jpeg,.png,.bmp"; * - with-layout 적용 * - 하수처리시설에 하수재이용 시설 시설 정보를 등록한다. * - form으로 시설정보 및 차수별 정보의 컴포넌트 데이터를 관리한다 - * indexMng: 시설정보 + * indexMng: 재이용시설정보 * indexBiz: 차수별 정보 * 로 하수재이용 시설에 등록된 시설 및 차수별 정보를 관리한다. * */ @@ -61,7 +61,7 @@ export function PbnstMngForm({ setIndexMng: (index: number) => void; readonly: boolean; }) { - const { code, pbnst, fileDelete, fileDownLoad } = useWrStore( + const { code, pbnst, fileDelete, fileDownLoadName } = useWrStore( (state) => state ); const [planFiles, setPlanFiles] = useState(null); @@ -150,10 +150,10 @@ export function PbnstMngForm({ } else { fileList?.forEach((item, index) => { const idx = files?.indexOf(item.file); - if (idx < 0) { - setReFiles.splice(index); - fileDelete(item.atflCd as string); - } + if (idx < 0) { + setReFiles.splice(index, 1); + fileDelete(item.atflCd as string); + } }); } // eslint-disable-next-line @typescript-eslint/no-unused-expressions @@ -183,7 +183,7 @@ export function PbnstMngForm({ useEffect(() => { setRelationFiles([]); setPlanFiles([]); - console.log("indexMng ", indexMng); + // console.log("indexMng ", indexMng); const currentItem = form.getValues(`items.${indexMng}`); const defaultFiles = currentItem?.fcFiles || []; @@ -443,7 +443,7 @@ export function PbnstMngForm({ fileDownLoad(item.atflCd)} + onClick={() => fileDownLoadName(item.atflCd, item.file.name)} > {item.file.name} @@ -462,11 +462,12 @@ export function PbnstMngForm({ }} fileTypes={diagramFileTypes} onFileClick={(index) => { - fileDownLoad( - relationfiles?.find( - (item, fileIndex) => fileIndex === index - )?.atflCd as string + const target = relationfiles?.find( + (item, fileIndex) => fileIndex === index ); + if (target?.atflCd) { + fileDownLoadName(target.atflCd, target.file.name); + } }} > @@ -481,7 +482,7 @@ export function PbnstMngForm({ fileDownLoad(item.atflCd)} + onClick={() => fileDownLoadName(item.atflCd, item.file.name)} > {item.file.name} @@ -500,11 +501,12 @@ export function PbnstMngForm({ }} fileTypes={diagramFileTypes} onFileClick={(index) => { - fileDownLoad( - planFiles?.find( - (item, fileIndex) => fileIndex === index - )?.atflCd as string + const target = planFiles?.find( + (item, fileIndex) => fileIndex === index ); + if (target?.atflCd) { + fileDownLoadName(target.atflCd, target.file.name); + } }} > diff --git a/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx index 067723ccf..beaf26b81 100644 --- a/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx @@ -1,3 +1,6 @@ +/** + * map 시설물 layer > 하수처리시설 modal + */ import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import { pbnMapTableSize } from "@/app/store/map/pbnstMap"; diff --git a/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx b/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx index f10574148..86da242d7 100644 --- a/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx @@ -1,3 +1,6 @@ +/** + * map 시설물 layer > 온배수시설 modal + */ import { useWrStore } from "@/app/store"; import { htdfMapSize } from "@/app/store/map/htdflMap"; import { rainMapSize } from "@/app/store/map/rainMap"; diff --git a/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx index a980712e9..74bf0aa55 100644 --- a/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx @@ -1,3 +1,6 @@ +/** + * map 시설물 layer > 빗물이용시설 modal + */ import { useWrStore } from "@/app/store"; import { rainMapSize } from "@/app/store/map/rainMap"; import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; diff --git a/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx b/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx index 6cad8f105..4fe818568 100644 --- a/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx @@ -1,3 +1,6 @@ +/** + * map 시설물 layer > 중수도시설 modal + */ import { useWrStore } from "@/app/store"; import { rainMapSize } from "@/app/store/map/rainMap"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; diff --git a/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx index 59a8fcc57..8ed06293b 100644 --- a/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx @@ -1,3 +1,6 @@ +/** + * map 시설물 layer > 하수재이용시설 modal + */ import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import { ruseMapSize } from "@/app/store/map/ruseMap"; diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx index 637d6af01..595cff55e 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -1,3 +1,6 @@ +/** + * 지도점검 결과등록 엑셀 업로드 모달 + */ import { useWrStore } from "@/app/store"; import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import { excelToRainChkData } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel"; @@ -8,7 +11,6 @@ import { excelToRswtPolution, rswtExcelDataCheck, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel"; -import { showConfirmToast } from "@/components/lib/func/common"; import { useAlertStore } from "@/src/shared/lib"; import { filePicked, @@ -61,6 +63,8 @@ export default function ChkgdExcel({ }; const handleFileChange = (e: React.ChangeEvent) => { const selectedFile = e.target.files?.[0]; + setExcelData(undefined); + setExcelRswtData(undefined); if (selectedFile) { setFile(selectedFile); } @@ -105,12 +109,15 @@ export default function ChkgdExcel({ ); if (excelData.message) { alert(excelData.message); + setExcelData(undefined); } if (excelData.issues && excelData.issues.length > 0) { excelData.issues.some((err, index) => { - showConfirmToast(err); + // showConfirmToast(err); + alert(err); return index === 4; // true 반환 시 반복 종료 }); + setExcelData(undefined); clearFile(); } else { setExcelData(excelData.rainChkgdData); @@ -123,6 +130,7 @@ export default function ChkgdExcel({ setRswtChkLoading(true); filePickedArray(file, 0) .then((res) => { + setExcelRswtData(undefined); if (res && res?.length > 1) { const guideData = excelToRswtGuideData( res[0], @@ -167,16 +175,24 @@ export default function ChkgdExcel({ if (issues && issues.length > 0) { issues.some((err, index) => { - showConfirmToast(err); - return index === 6; // true 반환 시 반복 종료 + // showConfirmToast(err); + alert(err); + return index === 4; // true 반환 시 반복 종료 }); + console.log(issues.join("\n")); + setExcelRswtData(undefined); clearFile(); } else { setExcelRswtData(listTotalData); } + } else { + alert("중수도 엑셀은 2개 시트가 필요합니다. 양식을 확인해주세요."); + setExcelRswtData(undefined); + clearFile(); } }) .finally(() => setRswtChkLoading(false)); + console.log("업로드파일 점검 완료"); } } }, [file]); diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index 95dfb3e94..c5ba35e17 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -1,3 +1,6 @@ +/** + * 지도점검 상세조회/상세수정용 모달 + */ import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { splRuseWaterType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; @@ -11,10 +14,10 @@ import React, { useEffect, useState } from "react"; import type { z } from "zod"; export default function ChkgdModal({ - type, - obj, - setIsModal, - readonly, + type, // 시설 구분값. "RNB"이면 빗물, 그 외는 중수도 상세 모달로 처리 + obj, // 목록에서 선택한 1건의 상세 데이터. 가이드/수질/과태료 정보 렌더링과 저장에 사용 + setIsModal, // 모달 열림 상태를 제어하는 함수. 저장 완료 시 false로 닫는다 + readonly, // 편집 가능 여부. true면 입력 가능, false면 조회 전용으로 렌더링 }: { type: string; obj: any; @@ -57,7 +60,7 @@ export default function ChkgdModal({ combResdch: 0, trbt: 0, bod: 0, - smll: 0, + smll: '-', crmty: 0, tph: 0, tn: 0, @@ -69,7 +72,7 @@ export default function ChkgdModal({ useEffect(() => { if (rainChkSttn.isSttn) { setHeadList({ - splHeadList: rainChkSttn.splHeadList, + splHeadList: rainChkSttn.splHeadList, // 연간 대체수이용량 헤더 정의 ruseHeadList: rainChkSttn.ruseHeadList, rswtDetailHeadList: type === "RNB" ? [] : rswtChkSttn.rswtDetailSttnHeadList, @@ -82,9 +85,12 @@ export default function ChkgdModal({ }); } }, [ + type, rainChkSttn.isSttn, rainChkgdn.splHeadList, + rainChkgdn.ruseHeadList, rainChkSttn.splHeadList, + rainChkSttn.ruseHeadList, rswtChkgdn.rswtDetailHeadList, rswtChkSttn.rswtDetailSttnHeadList, ]); @@ -156,12 +162,13 @@ export default function ChkgdModal({ if (onlyData.length === 0) return; const total = initStats(); + // 분기별 원본 수질 데이터를 모두 더해 평균 계산의 분모/분자를 만든다. onlyData.forEach((item) => { total.grssClfgUswtr += item.grssClfgUswtr; total.combResdch += item.combResdch; total.trbt += item.trbt; total.bod += item.bod; - total.smll += item.smll; + // 냄새(smll)는 수치 평균 대상이 아니라 누적하지 않는다. total.crmty += item.crmty; total.tph += item.tph; total.tn += item.tn; @@ -169,6 +176,7 @@ export default function ChkgdModal({ total.clIonQnt += item.clIonQnt; }); + // 누적 합계를 항목 개수로 나눠 화면에 표시할 평균 행을 만든다. const average = { utztnUsgCdText: "평균", edit: false, @@ -176,7 +184,7 @@ export default function ChkgdModal({ combResdch: (total.combResdch / onlyData.length).toFixed(1), trbt: (total.trbt / onlyData.length).toFixed(1), bod: (total.bod / onlyData.length).toFixed(1), - smll: (total.smll / onlyData.length).toFixed(1), + smll: '-', crmty: (total.crmty / onlyData.length).toFixed(1), tph: (total.tph / onlyData.length).toFixed(1), tn: (total.tn / onlyData.length).toFixed(1), diff --git a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx index 3881e5813..0857340e3 100644 --- a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx @@ -1,3 +1,6 @@ +/** + * 재이용실적등록> 하수재이용 실적등록 파일등록 modal + */ import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel"; import { pbnPrfRegExcelZod } from "@/app/store/prfmncReg/pbnstPrfmncReg/headList"; import type { @@ -17,8 +20,9 @@ import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; -import toast from "react-hot-toast"; +// import toast from "react-hot-toast"; import type { z } from "zod"; +import { useAlertStore } from "@/src/shared/lib"; export default function PbnPrfExcelModal({ data, @@ -29,6 +33,7 @@ export default function PbnPrfExcelModal({ setIsExcelOPen: (open: boolean) => void; setModalExcelData: (excelData: PrfType) => void; }) { + const alert = useAlertStore((state) => state.setMessage); const [file, setFile] = useState(null); const [excelData, setExcelData] = useState(); const form = useForm>({ @@ -65,7 +70,8 @@ export default function PbnPrfExcelModal({ if (file) { const isFile = file; if (form.getValues("ym").length < 1) { - toast.error("파일업로드 이전에 등록년도를 작성해주세요 "); + // toast.error("파일업로드 이전에 등록년도를 작성해주세요 "); + alert("파일업로드 이전에 등록년도를 작성해주세요 "); setFile(null); return; } diff --git a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx index bfc958598..c1bfda0ff 100644 --- a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx +++ b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx @@ -8,9 +8,10 @@ import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useState } from "react"; import { useForm } from "react-hook-form"; -import toast from "react-hot-toast"; +// import toast from "react-hot-toast"; import type { z } from "zod"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { useAlertStore } from "@/src/shared/lib"; export default function CoRegExcel({ isModal, @@ -18,6 +19,7 @@ export default function CoRegExcel({ isModal: (open: boolean) => void; }) { const { confirm } = useConfirm(); + const alert = useAlertStore((state) => state.setMessage); const { ldrmnt, loadingOpen } = useWrStore((state) => state); const { isLoading } = ldrmnt; const form = useForm>({ @@ -76,11 +78,12 @@ export default function CoRegExcel({ await ldrmntRegData(array); isModal(false); loadingOpen(false); - toast.success(`잠재수요처 등록이 요청되었습니다.`); - } catch (e) { + // toast.success(`잠재수요처 등록이 요청되었습니다.`); + alert("잠재수요처 등록이 요청되었습니다."); + } catch (e: any) { console.log(e); loadingOpen(false); - alert(e); + // alert(e.message); return true; } }) @@ -90,7 +93,8 @@ export default function CoRegExcel({ }); } else { loadingOpen(false); - toast.error(`엑셀 파일을 선택해 주세요.`); + // toast.error(`엑셀 파일을 선택해 주세요.`); + alert("엑셀 파일을 선택해 주세요."); } }; diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index 89584ad3d..236797711 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -18,7 +18,8 @@ import { } from "@common/business_components"; import { Button, Input, Label } from "@common/components"; import { useEffect, useState } from "react"; -import toast from "react-hot-toast"; +// import toast from "react-hot-toast"; +import { useAlertStore } from "@/src/shared/lib"; export type objSearchType = { sgg: string | number; @@ -47,7 +48,7 @@ export function BdstReg({ const { code, bdst, auth, findBdst } = useWrStore((state) => { return state; }); - + const alert = useAlertStore((state) => state.setMessage); const { laeSeCd } = auth; const [bdstTable, setBdstTable] = useState(); @@ -73,7 +74,8 @@ export function BdstReg({ findBdst(String(data), searchTxt || "", moType); }else{ - toast.error("시도 또는 시군구를 선택하여 주십시오."); + // toast.error("시도 또는 시군구를 선택하여 주십시오."); + alert("시도 또는 시군구를 선택하여 주십시오."); } }; diff --git a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx index ac79d761a..3be1806f8 100644 --- a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -1,3 +1,6 @@ +/** + * 재이용시설등록 > 하수재이용시설등록 - 취수하수처리장 추가 modal + */ "use client"; import { useWrStore } from "@/app/store"; import type { @@ -12,7 +15,8 @@ import { Button } from "@common/business_components/ui/table/lib/button"; import { Input } from "@common/components/ui"; import { ArrowLeft, ArrowRight } from "lucide-react"; import { useEffect, useRef, useState } from "react"; -import toast from "react-hot-toast"; +// import toast from "react-hot-toast"; +import { useAlertStore } from "@/src/shared/lib"; export default function FcOrgwtsRegModal({ onRightSet, @@ -23,6 +27,7 @@ export default function FcOrgwtsRegModal({ closeModal?: (val: boolean) => void; initTableData?: any[]; }) { + const alert = useAlertStore((state) => state.setMessage); const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore( (state) => state ); @@ -53,7 +58,8 @@ export default function FcOrgwtsRegModal({ closeModal?.(false) } else { - toast.error("취수하수처리장 목록을 선택해주세요.") + // toast.error("취수하수처리장 목록을 선택해주세요."); + alert("취수하수처리장 목록을 선택해주세요."); } } diff --git a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx index 4c06f5722..1a07720a7 100644 --- a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx +++ b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx @@ -15,7 +15,7 @@ import { type fcBizType, } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import { formH1Class } from "@/components/lib/class/comClass"; -import { InputTableBody } from "@/components/lib/func/TableData"; +import { pbnstTableBody } from "@/components/lib/func/TableData"; import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; import { ScrollArea, Title, UiTable } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; @@ -341,7 +341,7 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; {headMakeprfmnc(pbnstPrfmncSttn.monthSttnHeadList, [])} - {InputTableBody(prfData[ruseTab as keyof typeof prfData] || [], pbnstPrfmncSttn.monthSttnHeadList, setPrfData as any)} + {pbnstTableBody(prfData[ruseTab as keyof typeof prfData] || [], pbnstPrfmncSttn.monthSttnHeadList, setPrfData as any)} {/* */} diff --git a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx index 4ff4ab14e..35745077c 100644 --- a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx @@ -1,3 +1,6 @@ +/** + * 재이용실적등록 > 지도점검결과등록 - 연간대체수이용량, 연간중수도이용량 click시 상세 model의 table body + */ "use client"; import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import type { TableUpperProps } from "@/app/store/common/type/header"; @@ -41,7 +44,17 @@ export function GuideDetailsTable({ return ( <>
- +
+ + {headList + .filter((head) => head.visible !== false) + .map((head) => ( + + ))} + {headMakeprfmnc(headList, upheadList)} {InputTableBody(inputData ?? [], headList, setInputData, readonly)} diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index 5694c8085..bac0e71a3 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -1,3 +1,6 @@ +/** + * 재이용실적등록 > 지도점검결과 등록 - 중수도 table 목록 + */ "use client"; import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; @@ -152,11 +155,7 @@ export default function RswtChkgdTable({ .map((head) => ( ))} diff --git a/packages/common/business_components/src/ui/file-upload/index.tsx b/packages/common/business_components/src/ui/file-upload/index.tsx index af63af7e7..6836691e2 100644 --- a/packages/common/business_components/src/ui/file-upload/index.tsx +++ b/packages/common/business_components/src/ui/file-upload/index.tsx @@ -1,6 +1,7 @@ "use client"; import { CloudUpload, Paperclip } from "lucide-react"; import { FileInput, FileUploader, FileUploaderContent, FileUploaderItem } from "./lib/file_upload"; + const dropZoneConfig = { maxFiles: 5, maxSize: 1024 * 1024 * 100, @@ -9,14 +10,19 @@ const dropZoneConfig = { interface FileUploadProps { - files: File[]; // 파일 배열 - setFiles: (files: File[] | null) => void; // 파일 배열 세터 - fileTypes?: string; // 옵션 (파일 확장자 문자열) - onFileClick?:(index:number)=> void; - maxFiles?: number; // 파일 클릭 핸들러 - + files: File[]; // 화면에 표시할 현재 파일 목록 + setFiles: (files: File[] | null) => void; // 추가/삭제 후 변경된 파일 목록을 부모에 전달 + fileTypes?: string; // 허용할 확장자 문자열(ex: ".pdf,.jpg") + onFileClick?:(index:number)=> void; // 파일명 영역 클릭 시 현재 목록 기준 index 전달 + maxFiles?: number; // 업로드 가능한 최대 파일 수 + } +/** + * 공통 파일 업로드 UI + * - 실제 파일 상태는 부모가 관리하고, 이 컴포넌트는 목록 표시/추가/삭제 이벤트만 위임한다. + * - 삭제 아이콘 클릭 시 FileUploader 내부에서 index 기준으로 파일을 제외한 배열을 만든 뒤 setFiles로 전달한다. + */ export const FileUpload = ({ files, setFiles, fileTypes ,onFileClick, maxFiles}: FileUploadProps) => { if(maxFiles){ @@ -51,6 +57,7 @@ export const FileUpload = {files&& files.length > 0 && files.map((file:File, i:number) => ( + // i는 현재 files 배열 순번이며, 삭제/클릭 이벤트에서 같은 순번값을 사용한다. {file.name} @@ -59,4 +66,4 @@ export const FileUpload = ) -} \ No newline at end of file +} diff --git a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx index 030ddda95..9e47d296e 100644 --- a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx +++ b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx @@ -37,6 +37,7 @@ type FileUploaderContextType = { const FileUploaderContext = createContext(null); +// FileUploader 하위 컴포넌트들이 공통 상태와 핸들러를 사용할 수 있도록 context를 노출한다. export const useFileUpload = () => { const context = useContext(FileUploaderContext); if (!context) { @@ -54,7 +55,10 @@ type FileUploaderProps = { }; /** - * File upload Docs: {@link: https://localhost:3000/docs/file-upload} + * 파일 업로드의 공통 동작을 담당하는 컨테이너 컴포넌트 + * - 현재 파일 목록(value)을 기준으로 drag & drop / 선택 / 삭제를 처리한다. + * - 실제 파일 상태는 부모가 관리하며, 변경 결과는 onValueChange로만 전달한다. + * - FileInput, FileUploaderContent, FileUploaderItem은 이 context를 통해 같은 상태를 공유한다. */ export const FileUploader = forwardRef< @@ -80,7 +84,7 @@ export const FileUploader = forwardRef< const [isLOF, setIsLOF] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); - // MIME 타입 매핑 함수 추가 + // ".pdf,.jpg" 형태의 확장자 문자열을 react-dropzone accept 객체로 변환한다. const getAcceptObject = (fileTypes?: string): Record => { if (!fileTypes) { return { "image/*": [".jpg", ".jpeg", ".png", ".gif"] }; @@ -131,6 +135,7 @@ export const FileUploader = forwardRef< const reSelectAll = maxFiles === 1 ? true : reSelect; const direction: DirectionOptions = dir === "rtl" ? "rtl" : "ltr"; + // 현재 value 배열에서 index 위치 파일만 제외한 새 배열을 부모로 전달한다. const removeFileFromSet = useCallback( (i: number) => { if (!value) return; @@ -140,6 +145,7 @@ export const FileUploader = forwardRef< [value, onValueChange], ); + // 키보드로 파일 목록 탐색 및 삭제를 지원한다. const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { e.preventDefault(); @@ -196,6 +202,7 @@ export const FileUploader = forwardRef< [value, activeIndex, removeFileFromSet], ); + // 드롭/선택된 파일을 현재 목록과 합쳐서 부모 상태로 반영한다. const onDrop = useCallback( (acceptedFiles: File[], rejectedFiles: FileRejection[]) => { const files = acceptedFiles; @@ -238,6 +245,7 @@ export const FileUploader = forwardRef< [reSelectAll, value], ); + // 최대 파일 수에 도달하면 입력 영역을 비활성화한다. useEffect(() => { if (!value) return; if (value.length === maxFiles) { @@ -247,6 +255,7 @@ export const FileUploader = forwardRef< setIsLOF(false); }, [value, maxFiles]); + // react-dropzone에 넘길 최종 옵션 구성 const opts = dropzoneOptions ? { ...dropzoneOptions, accept: getAcceptObject(acceptName) } : { accept, maxFiles, maxSize, multiple }; @@ -311,7 +320,7 @@ export const FileUploaderContent = forwardRef< {...props} ref={ref} className={cn( - "w-full flex rounded-xl gap-1", + "w-full flex rounded-xl gap-0", orientation === "horizontal" ? "flex-raw flex-wrap" : "flex-col", className, )} @@ -335,26 +344,31 @@ export const FileUploaderItem = forwardRef< ref={ref} className={cn( buttonVariants({ variant: "ghost" }), - "h-6 p-1 justify-between cursor-pointer relative w-full", + "h-9 justify-between cursor-pointer relative w-full", className, isSelected ? "bg-muted" : "", )} {...props} > -
{onFileClick?.(index) }}> {children}