diff --git a/apps/dm_new/app/(landing)/reuse/apply/modal/SimilarCaseModal.tsx b/apps/dm_new/app/(landing)/reuse/apply/modal/SimilarCaseModal.tsx index f226f2ca9..c363d13ee 100644 --- a/apps/dm_new/app/(landing)/reuse/apply/modal/SimilarCaseModal.tsx +++ b/apps/dm_new/app/(landing)/reuse/apply/modal/SimilarCaseModal.tsx @@ -1,6 +1,7 @@ +// apps/dm/app/(landing)/reuse/apply/modal/SimilarCaseModal.tsx (유사사례 모달) "use client"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useState, useId } from "react"; import { createPortal } from "react-dom"; type Criterion = "" | "supplyAmt" | "treatment" | "usePurpose"; @@ -59,8 +60,7 @@ export type SimilarCaseDetail = { }; function formatNumber(v: any) { - if (v === null || v === undefined || v === "" || Number.isNaN(Number(v))) - return "-"; + if (v === null || v === undefined || v === "" || Number.isNaN(Number(v))) return "-"; const n = typeof v === "number" ? v : Number(String(v).replaceAll(",", "")); if (!Number.isFinite(n)) return "-"; return n.toLocaleString("ko-KR"); @@ -77,6 +77,8 @@ export default function SimilarCaseModal({ rows: SimilarCaseRow[]; detailsByCaseId: Record; }) { + const titleId = useId(); + // open 타입 방어 if (typeof open !== "boolean") { console.error("SimilarCaseModal: open prop is not boolean:", open); @@ -133,7 +135,8 @@ export default function SimilarCaseModal({ const tdBase = "border border-[#C9C9C9] px-2 py-2 text-sm text-neutral-800 align-middle"; const thBase = - "border border-[#C9C9C9] bg-[#E7F4E2] px-2 py-3 text-sm font-semibold text-neutral-800 text-center align-middle"; + "border border-[#C9C9C9] bg-[#E7F4E2] px-2 py-2 text-sm font-semibold text-neutral-800 text-center align-middle"; + const highlightTd = "bg-neutral-200"; const showList = criterion !== ""; @@ -150,456 +153,385 @@ export default function SimilarCaseModal({ /> {/* panel */} -
+
{/* header */}
-

유사사례 조회

+

+ 유사사례 조회 +

- {/* body */} -
- {/* 상단 타이틀 + 유사기준 */} -
-
- 물 재이용 사업 유사 사례 -
- -
- - 유사기준 - - -
+ {/* body */} +
+ {/* 상단 타이틀 + 유사기준 */} +
+
+ 물 재이용 사업 유사 사례
- {/* 리스트 영역 */} -
- {!showList ? ( -
-
- 유사기준을 선택해주세요. -
-
- ) : !hasRows ? ( -
-
조회 결과가 없습니다.
-
- ) : ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {rows.map((r, idx) => { - const isFirst = groupInfo.firstIndex.get(r.caseId) === idx; - const rowSpan = groupInfo.counts.get(r.caseId) ?? 1; - const selected = selectedCaseId === r.caseId; - - const supplyAmtCls = criterion === "supplyAmt" ? highlightTd : ""; - const treatmentCls = criterion === "treatment" ? highlightTd : ""; - const usePurposeCls = criterion === "usePurpose" ? highlightTd : ""; - - return ( - setSelectedCaseId(r.caseId)} - > - {isFirst ? ( - <> - - - - - ) : null} - - - - - - - - - - - - - - - - - ); - })} - -
- 선택 - - 사업명 - - 사업기간 - - 공급처 - - 수요처 - - 재이용시설 -
시설종류공급처명 - 시설용량 -
- (m³/일) -
수요처명주소 - 시설용량 -
- (m³/일) -
- 관로 -
- (km) -
용도 - 공급량 -
- (m³/일) -
처리공법이용용도
- - - {r.projectName ?? "-"} - - {r.period ?? "-"} - - {r.supplyFacilityType ?? "-"} - {r.supplyName ?? "-"} - {formatNumber(r.supplyCapacity)} - {r.demandName ?? "-"}{r.demandAddress ?? "-"} - {formatNumber(r.facilityCapacity)} - - {formatNumber(r.pipeKm)} - {r.purpose ?? "-"} - {formatNumber(r.supplyAmt)} - - {r.treatment ?? "-"} - - {r.usePurpose ?? "-"} -
-
- )} +
+ 유사기준 +
+
- {/* 상세정보 */} -
-
- 상세정보 + {/* 리스트 영역 */} +
+ {!showList ? ( +
+
유사기준을 선택해주세요.
- - {/* 1) 보증수질 */} -
+ ) : !hasRows ? ( +
+
조회 결과가 없습니다.
+
+ ) : ( + // 여기만 스크롤: rows가 4개/더 많아져서 높이를 넘으면 자동 스크롤 생성 +
- + {/* thead sticky: bg + shadow + z-index로 겹침/투명 방지 */} + - + + + + + + - + + - + + + - - - - - - - - - - - - + + + + - - - - + {rows.map((r, idx) => { + const isFirst = groupInfo.firstIndex.get(r.caseId) === idx; + const rowSpan = groupInfo.counts.get(r.caseId) ?? 1; + const selected = selectedCaseId === r.caseId; - - - - - - - - + const supplyAmtCls = criterion === "supplyAmt" ? highlightTd : ""; + const treatmentCls = criterion === "treatment" ? highlightTd : ""; + const usePurposeCls = criterion === "usePurpose" ? highlightTd : ""; - - + return ( + setSelectedCaseId(r.caseId)} + > + {isFirst ? ( + <> + + + + + ) : null} + + + + + + + + + + + + + + + + + ); + })}
- 보증수질 + + 선택 + + 사업명 + + 사업기간 + + 공급처 + + 수요처 + + 재이용시설
- 총대장균군수
(개/100ml) +
시설
종류
공급처명 + 시설용량 +
+ (m³/일)
- 용도별 결합 잔류염소
(mg/L) + +
수요처명주소 + 시설용량 +
+ (m³/일)
- 탁도
(NTU) +
+ 관로 +
+ (km)
- 생물학적
산소요구량(BOD)(mg/L) -
냄새색도(도) - 총질소
(T-N)(mg/L) -
- 총인
(T-P)(mg/L) -
- 수소이온
농도(pH) -
- 염화물
(mgCL/L) -
- 전기전도도
(μs/cm) -
- 비고 + 용도 + 공급량 +
+ (m³/일)
처리공법이용용도
- {selectedDetail?.coliform ?? "-"} - - {selectedDetail?.residualCl ?? "-"} - - {selectedDetail?.turbidity ?? "-"} - - {selectedDetail?.bod ?? "-"} - - {selectedDetail?.odor ?? "-"} - - {selectedDetail?.color ?? "-"} - - {selectedDetail?.tn ?? "-"} - - {selectedDetail?.tp ?? "-"} - - {selectedDetail?.ph ?? "-"} - - {selectedDetail?.chloride ?? "-"} - - {selectedDetail?.ec ?? "-"} - - {selectedDetail?.note ?? "-"} -
+ + + {r.projectName ?? "-"} + + {r.period ?? "-"} + {r.supplyFacilityType ?? "-"}{r.supplyName ?? "-"}{formatNumber(r.supplyCapacity)}{r.demandName ?? "-"}{r.demandAddress ?? "-"}{formatNumber(r.facilityCapacity)}{formatNumber(r.pipeKm)}{r.purpose ?? "-"} + {formatNumber(r.supplyAmt)} + + {r.treatment ?? "-"} + + {r.usePurpose ?? "-"} +
+ )} +
- {/* 2) 사업비 */} -
- - - - - - - +
- - {/* 소요사업비 */} - - - - - - - - - - - - - + {/* 상세정보 */} +
+
상세정보
- {/* 재원조달 */} -
- - - - + {/* 1) 보증수질 */} +
+
- 소요사업비(백만원) - - 재원조달계획(백만원) - - 사업비 정산(백만원) -
총토목건축기계전기
·계측
GIS
DB
CCTV시공 및 준공폐기물설계비감리비시설부대기타총국비지방비원인자부담국고비율
+ + + + + + + + + + + + + + + + + + + + + - {/* 정산 */} - - - - - - + + + + + - - - - - + + + + + + + + - - - {/* 소요사업비 */} - - - - - - - - - - - - - - - - {/* 재원조달 */} - - - - - - - {/* 정산(필드 없으면 동일 값 표시) */} - - - - - - - -
+ 보증수질 +
+ 총대장균군수
(개/100ml) +
+ 용도별 결합 잔류염소
(mg/L) +
+ 탁도
(NTU) +
+ 생물학적
산소요구량(BOD)(mg/L) +
냄새색도(도)총질소
(T-N)(mg/L)
총인
(T-P)(mg/L)
수소이온
농도(pH)
염화물
(mgCL/L)
전기전도도
(μs/cm)
비고
총국비지방비원인자부담국고비율
{selectedDetail?.coliform ?? "-"}{selectedDetail?.residualCl ?? "-"}{selectedDetail?.turbidity ?? "-"}
재이용
시설
관로
{selectedDetail?.bod ?? "-"}{selectedDetail?.odor ?? "-"}{selectedDetail?.color ?? "-"}{selectedDetail?.tn ?? "-"}{selectedDetail?.tp ?? "-"}{selectedDetail?.ph ?? "-"}{selectedDetail?.chloride ?? "-"}{selectedDetail?.ec ?? "-"}
- {selectedDetail?.costTotal != null - ? formatNumber(selectedDetail.costTotal) - : "-"} - - {selectedDetail?.costFacility != null - ? formatNumber(selectedDetail.costFacility) - : "-"} - - {selectedDetail?.costPipe != null - ? formatNumber(selectedDetail.costPipe) - : "-"} - ------- - {selectedDetail?.costDesign != null - ? formatNumber(selectedDetail.costDesign) - : "-"} - - {selectedDetail?.costSupervision != null - ? formatNumber(selectedDetail.costSupervision) - : "-"} - - - {selectedDetail?.costOther != null - ? formatNumber(selectedDetail.costOther) - : "-"} - - {selectedDetail?.fundingTotal != null - ? formatNumber(selectedDetail.fundingTotal) - : "-"} - - {selectedDetail?.fundingNational != null - ? formatNumber(selectedDetail.fundingNational) - : "-"} - - {selectedDetail?.fundingLocal != null - ? formatNumber(selectedDetail.fundingLocal) - : "-"} - - {selectedDetail?.fundingSelf != null - ? formatNumber(selectedDetail.fundingSelf) - : "-"} - - {typeof selectedDetail?.fundingRate === "number" - ? `${selectedDetail.fundingRate}%` - : "-"} - - {selectedDetail?.fundingTotal != null - ? formatNumber(selectedDetail.fundingTotal) - : "-"} - - {selectedDetail?.fundingNational != null - ? formatNumber(selectedDetail.fundingNational) - : "-"} - - {selectedDetail?.fundingLocal != null - ? formatNumber(selectedDetail.fundingLocal) - : "-"} - - {selectedDetail?.fundingSelf != null - ? formatNumber(selectedDetail.fundingSelf) - : "-"} - - {typeof selectedDetail?.fundingRate === "number" - ? `${selectedDetail.fundingRate}%` - : "-"} -
-
- - {!selectedCaseId && showList ? ( -
- 리스트에서 사업을 선택하면 상세정보가 표시됩니다. -
- ) : null} + {selectedDetail?.note ?? "-"} + + +
+ + {/* 2) 사업비 */} +
+ + + + + + + + + + {/* 소요사업비 */} + + + + + + + + + + + + + + + {/* 재원조달 */} + + + + + + + {/* 정산 */} + + + + + + + + + + + + + + + + {/* 소요사업비 */} + + + + + + + + + + + + + + + + {/* 재원조달 */} + + + + + + + {/* 정산(필드 없으면 동일 값 표시) */} + + + + + + + +
+ 소요사업비(백만원) + + 재원조달계획(백만원) + + 사업비 정산(백만원) +
총토목건축기계전기
·계측
GIS
DB
CCTV시공 및 준공폐기물설계비감리비시설부대기타총국비지방비원인자부담국고비율총국비지방비원인자부담국고비율
재이용
시설
관로
+ {selectedDetail?.costTotal != null ? formatNumber(selectedDetail.costTotal) : "-"} + + {selectedDetail?.costFacility != null ? formatNumber(selectedDetail.costFacility) : "-"} + + {selectedDetail?.costPipe != null ? formatNumber(selectedDetail.costPipe) : "-"} + ------- + {selectedDetail?.costDesign != null ? formatNumber(selectedDetail.costDesign) : "-"} + + {selectedDetail?.costSupervision != null ? formatNumber(selectedDetail.costSupervision) : "-"} + - + {selectedDetail?.costOther != null ? formatNumber(selectedDetail.costOther) : "-"} + + {selectedDetail?.fundingTotal != null ? formatNumber(selectedDetail.fundingTotal) : "-"} + + {selectedDetail?.fundingNational != null ? formatNumber(selectedDetail.fundingNational) : "-"} + + {selectedDetail?.fundingLocal != null ? formatNumber(selectedDetail.fundingLocal) : "-"} + + {selectedDetail?.fundingSelf != null ? formatNumber(selectedDetail.fundingSelf) : "-"} + + {typeof selectedDetail?.fundingRate === "number" ? `${selectedDetail.fundingRate}%` : "-"} + + {selectedDetail?.fundingTotal != null ? formatNumber(selectedDetail.fundingTotal) : "-"} + + {selectedDetail?.fundingNational != null ? formatNumber(selectedDetail.fundingNational) : "-"} + + {selectedDetail?.fundingLocal != null ? formatNumber(selectedDetail.fundingLocal) : "-"} + + {selectedDetail?.fundingSelf != null ? formatNumber(selectedDetail.fundingSelf) : "-"} + + {typeof selectedDetail?.fundingRate === "number" ? `${selectedDetail.fundingRate}%` : "-"} +
+
+ + {!selectedCaseId && showList ? ( +
+ 리스트에서 사업을 선택하면 상세정보가 표시됩니다. +
+ ) : null}
+
); return createPortal(ui, document.body); diff --git a/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx b/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx index 0a6cac34a..4a57089fa 100644 --- a/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx +++ b/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx @@ -187,71 +187,124 @@ export default function DemandRegisterPage() { // 샘플 데이터 const similarRows: SimilarCaseRow[] = [ - { - caseId: "A", - projectName: "OO시 물 재이용(빗물, 중수도)\n시설 설치사업", - period: "2020.01.\n~2025.05.", - supplyFacilityType: "중수도", - supplyName: "OO에코복지타운", - supplyCapacity: 50, - demandName: "OO에코복지타운", - demandAddress: "OO시 수성로 192", - facilityCapacity: 50, - pipeKm: 0, - purpose: "청소, 화장실,\n조경", - supplyAmt: 50, - treatment: "MBLM", - usePurpose: "청소,화장실", - }, - { - caseId: "A", - projectName: "OO시 물 재이용(빗물, 중수도)\n시설 설치사업", - period: "2020.01.\n~2025.05.", - supplyFacilityType: "중수도", - supplyName: "OO스포츠센터", - supplyCapacity: 50, - demandName: "OO스포츠센터", - demandAddress: "OO시 경기둥로 33", - facilityCapacity: 50, - pipeKm: 0, - purpose: "청소, 화장실,\n조경", - supplyAmt: 50, - treatment: "MBLM", - usePurpose: "청소,화장실", - }, - { - caseId: "B", - projectName: "OO 염성저수지 하수처리수\n재이용사업", - period: "2019.12\n~2026.1", - supplyFacilityType: "하수", - supplyName: "OO 3.4단계 공공하수처리시설", - supplyCapacity: 70000, - demandName: "OO천", - demandAddress: "-", - facilityCapacity: 0, - pipeKm: 3, - purpose: "하천 등\n유지용수", - supplyAmt: 2300, - treatment: "탈황,총인,\n생물여과,소독", - usePurpose: "하천 등\n유지용수", - }, - { - caseId: "B", - projectName: "OO 염성저수지 하수처리수\n재이용사업", - period: "2019.12\n~2026.1", - supplyFacilityType: "하수", - supplyName: "OO 3.4단계 공공하수처리시설", - supplyCapacity: 70000, - demandName: "OO저수지", - demandAddress: "-", - facilityCapacity: 0, - pipeKm: 5, - purpose: "하천 등\n유지용수", - supplyAmt: 7700, - treatment: "탈황,총인,\n생물여과,소독", - usePurpose: "하천 등\n유지용수", - }, - ]; + // ---------------- A (2행) ---------------- + { + caseId: "A", + projectName: "OO시 물 재이용(빗물, 중수도) 시설 설치사업", + period: "2020.01.~2025.05.", + supplyFacilityType: "중수도", + supplyName: "OO폐수재이용", + supplyCapacity: 50, + + demandName: "OO시체육센터", + demandAddress: "OO시 수성로 192", + + facilityCapacity: 50, + pipeKm: 3, + purpose: "조경", + supplyAmt: 50, + treatment: "MBLM", + usePurpose: "청소, 화장실", + }, + // A의 공급처 row 추가(=표 1행 추가) → 나머지 컬럼도 같이 채움 + { + caseId: "A", + projectName: "OO시 물 재이용(빗물, 중수도) 시설 설치사업", + period: "2020.01.~2025.05.", + supplyFacilityType: "중수도", + supplyName: "OO스포츠센터", + supplyCapacity: 30, + + demandName: "OO시체육센터", + demandAddress: "OO시 수성로 192", + + facilityCapacity: 50, + pipeKm: 3, + purpose: "조경", + supplyAmt: 50, + treatment: "MBLM", + usePurpose: "청소, 화장실", + }, + + // ---------------- B (2행) ---------------- + { + caseId: "B", + projectName: "OO 염성저수지 하수처리수 재이용사업", + period: "2019.12~2026.1", + supplyFacilityType: "하수", + supplyName: "OO3.4단계 공공하수처리시설", + supplyCapacity: 70000, + + demandName: "OO천", + demandAddress: "-", + + facilityCapacity: 0, + pipeKm: 3, + purpose: "하천 등 유지용수", + supplyAmt: 2300, + treatment: "탈황, 총인, 생물산화, 소독", + usePurpose: "하천 등 유지용수", + }, + // B의 공급처 row 추가(=표 1행 추가) + { + caseId: "B", + projectName: "OO 염성저수지 하수처리수 재이용사업", + period: "2019.12~2026.1", + supplyFacilityType: "하수", + supplyName: "OO3.4단계 공공하수처리시설(2라인)", + supplyCapacity: 70000, + + demandName: "OO저수지", + demandAddress: "-", + + facilityCapacity: 0, + pipeKm: 5, + purpose: "하천 등 유지용수", + supplyAmt: 7700, + treatment: "탈황, 총인, 생물산화, 소독", + usePurpose: "하천 등 유지용수", + }, + + // ---------------- C (1행) ---------------- + { + caseId: "C", + projectName: "OO 산업단지 공업용수 재이용사업", + period: "2021.03~2024.12", + supplyFacilityType: "하수", + supplyName: "OO공공하수처리장", + supplyCapacity: 15000, + + demandName: "OO산업단지", + demandAddress: "OO시 산업로 25", + + facilityCapacity: 5000, + pipeKm: 4, + purpose: "공업용수", + supplyAmt: 5000, + treatment: "UF + RO", + usePurpose: "공업용수", + }, + + // ---------------- D (1행) ---------------- + { + caseId: "D", + projectName: "OO 공원 조경용수 재이용사업", + period: "2018.05~2022.11", + supplyFacilityType: "중수도", + supplyName: "OO정수장", + supplyCapacity: 8000, + + demandName: "OO중앙공원", + demandAddress: "OO시 공원로 88", + + facilityCapacity: 1200, + pipeKm: 2, + purpose: "조경용수", + supplyAmt: 1200, + treatment: "모래여과", + usePurpose: "조경", + }, +]; const similarDetailsByCaseId: Record = { A: { @@ -304,6 +357,56 @@ export default function DemandRegisterPage() { fundingSelf: 0, fundingRate: 0, }, + C: { + coliform: "불검출", + residualCl: "0.2이상", + turbidity: "1.5이하", + bod: "4.0이하", + odor: "불쾌하지않을것", + color: "10이하", + tn: "-", + tp: "-", + ph: "5.8~8.5", + chloride: "-", + ec: "-", + note: "-", + costTotal: 4305, + costFacility: 3518, + costPipe: 0, + costDesign: 350, + costSupervision: 417, + costOther: 12, + fundingTotal: 4305, + fundingNational: 3104, + fundingLocal: 1291, + fundingSelf: 0, + fundingRate: 70.0, + }, + D: { + coliform: "-", + residualCl: "-", + turbidity: "-", + bod: "-", + odor: "-", + color: "-", + tn: "-", + tp: "-", + ph: "-", + chloride: "-", + ec: "-", + note: "-", + costTotal: 0, + costFacility: 0, + costPipe: 0, + costDesign: 0, + costSupervision: 0, + costOther: 0, + fundingTotal: 0, + fundingNational: 0, + fundingLocal: 0, + fundingSelf: 0, + fundingRate: 0, + }, }; return ( @@ -528,8 +631,8 @@ export default function DemandRegisterPage() { setOpenSimilar(false)} - rows={rows} - detailsByCaseId={detailsByCaseId} + rows={similarRows} + detailsByCaseId={similarDetailsByCaseId} /> );