diff --git a/apps/sn/app/(map-layout)/layout.tsx b/apps/sn/app/(map-layout)/layout.tsx index 2077e5756..2ea6758b3 100644 --- a/apps/sn/app/(map-layout)/layout.tsx +++ b/apps/sn/app/(map-layout)/layout.tsx @@ -19,10 +19,11 @@ export default function MapLayout({ const setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo); - const { fetchCtpvSggData, fetchBIZ_SE_CD, fetchBIZ_TY_CD } = useStore(); + const { fetchCtpv, fetchSgg, fetchBIZ_SE_CD, fetchBIZ_TY_CD } = useStore(); useEffect(() => { - fetchCtpvSggData(); + fetchCtpv(); + fetchSgg(); fetchBIZ_SE_CD(); fetchBIZ_TY_CD(); }, []); diff --git a/apps/sn/app/(map-layout)/map/components/common/table.ts b/apps/sn/app/(map-layout)/map/components/common/table.ts index 1e34f5475..31f9d6dfc 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -31,17 +31,21 @@ export const MsSwagePplCol2 = [ { id: 'rowNumber', title: '일련\n번호' }, { id: 'fcltMngNo', title: '시설\n관리번호' }, { id: 'instMngNo', title: '기관\n관리번호' }, + { id: 'ctpvNm', title: '시도' }, + { id: 'sggNm', title: '시군구' }, + { id: "swtrmt_zone_nm", title: "처리구역" }, { id: 'instlYmd', title: '설치일자' }, // { id: 'mapshtNo', title: '도엽번호' }, + { id: 'usgCd', title: '용도' }, { id: 'pdmtVl', title: '관경\n(mm)' }, { id: 'prlgVl', title: '연장\n(m)' }, - { id: 'avgGrdn', title: '평균구배\n(m/m)' }, + // { id: 'avgGrdn', title: '평균구배\n(m/m)' }, { id: 'tmptDpth', title: '시점깊이\n(m)' }, { id: 'tmptMhlMngNo', title: '시점맨홀\n관리번호' }, { id: 'scendDpth', title: '종점깊이\n(m)' }, { id: 'scendMhlMngNo', title: '종점맨홀\n관리번호' }, - { id: 'vrtcLen', title: '세로길이\n(m)' }, - { id: 'wdthLen', title: '가로길이\n(m)' }, + // { id: 'vrtcLen', title: '세로길이\n(m)' }, + // { id: 'wdthLen', title: '가로길이\n(m)' }, // { id: 'fcltyShpCd', title: '시설물형태코드' }, // { id: 'fid', title: 'FID' }, // { id: 'ftreSmbolCd', title: '지형지물부호코드' }, @@ -388,6 +392,8 @@ export const MsSwageMhlCol2 = [ // { id: 'geom', title: 'geometry' }, // { id: 'ftreSmbolCd', title: '지형지물부호코드' }, { id: 'fcltMngNo', title: '시설관리번호' }, + { id: 'ctpvNm', title: '시도' }, + { id: 'sggNm', title: '시군구' }, // { id: 'pbadmsEmdCd', title: '행정읍면동코드' }, // { id: 'mapshtNo', title: '도엽번호' }, // { id: 'mngInstCd', title: '관리기관코드' }, @@ -496,12 +502,14 @@ export const ExclnSewagePumpCol2 = [ // { id: 'geom', title: 'geometry' }, // { id: 'ftreSmbolCd', title: '지형지물부호코드' }, { id: 'fcltMngNo', title: '시설관리번호' }, + { id: 'ctpvNm', title: '시도' }, + { id: 'sggNm', title: '시군구' }, // { id: 'pbadmsEmdCd', title: '행정읍면동코드' }, // { id: 'mapshtNo', title: '도엽번호' }, // { id: 'mngInstCd', title: '관리기관코드' }, - { id: 'instlYmd', title: '설치일자' }, { id: 'swagePmstnNm', title: '하수펌프장명' }, - { id: 'pllnArea', title: '부지면적(m²)' }, + { id: 'instlYmd', title: '설치일자' }, + // { id: 'pllnArea', title: '부지면적(m²)' }, // { id: 'opngSttsCd', title: '개통상태코드' }, // { id: 'usgCd', title: '용도코드' }, { id: 'dailyPrcsCpct', title: '일일처리용량(㎥)' }, @@ -539,11 +547,13 @@ export const DrtwSewagePumpCol2 = [ // { id: 'ftreSmbolCd', title: '지형지물부호코드' }, { id: 'fcltMngNo', title: '시설관리번호' }, // { id: 'pbadmsEmdCd', title: '행정읍면동코드' }, - { id: 'mapshtNo', title: '도엽번호' }, + // { id: 'mapshtNo', title: '도엽번호' }, + { id: 'ctpvNm', title: '시도' }, + { id: 'sggNm', title: '시군구' }, // { id: 'mngInstCd', title: '관리기관코드' }, - { id: 'instlYmd', title: '설치일자' }, { id: 'swagePmstnNm', title: '하수펌프장명' }, - { id: 'pllnArea', title: '부지면적(m²)' }, + { id: 'instlYmd', title: '설치일자' }, + // { id: 'pllnArea', title: '부지면적(m²)' }, // { id: 'opngSttsCd', title: '개통상태코드' }, // { id: 'usgCd', title: '용도코드' }, { id: 'dailyPrcsCpct', title: '일일처리용량(㎥)' }, @@ -585,6 +595,8 @@ export const stofcbrCol2 = [ { id: 'fcltMngNo', title: '시설관리번호' }, // { id: 'pbadmsEmdCd', title: '행정읍면동코드' }, { id: 'mapshtNo', title: '도엽번호' }, + { id: 'ctpvNm', title: '시도' }, + { id: 'sggNm', title: '시군구' }, // { id: 'mngInstCd', title: '관리기관코드' }, { id: 'instlYmd', title: '설치일자' }, // { id: 'fcltyShpCd', title: '시설물형태코드' }, @@ -657,7 +669,7 @@ export const dateFields = [ // 🎯 camelCase 버전 추가 'instlYmd', 'lastDrdgnYmd', 'cmcnYmd', 'oprtngStrtYmd', 'clsgYmd', 'mesureBeginDt', 'mesureEndDt', 'qltwtrBeginDt', 'qltwtrEndDt', - 'rainBeginDt', 'rainEndDt', + 'rainBeginDt', 'rainEndDt', 'flfmtBgngYmd', 'flfmtEndYmd', 'flfmtYr' ]; diff --git a/apps/sn/app/(map-layout)/map/searchModal/basicTab.tsx b/apps/sn/app/(map-layout)/map/searchModal/basicTab.tsx index 018f67883..70cd18884 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/basicTab.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/basicTab.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox'; import { SelectBox } from '@common/business_components/ui'; -import { Button, Input, Label } from '@common/components'; +import { Button, Label } from '@common/components'; import SwagePpl from './swagePpl'; import SwagePrcsFclt from './swagePrcsFclt'; import SwagePrcsZone from './swagePrcsZone'; import SwageMhl from './swageMhl'; import SwagePmstn from './swagePmstn'; +import Stofcbr from './stofcbr'; // 기본정보 '구분' 선택 목록 export const BASIC_TYPES = [ @@ -20,8 +21,8 @@ export const BASIC_TYPES = [ interface BasicTabProps { basicType: string; - city: number; - sgg: number; + city: string; + sgg: string; start1: string; start2: string; end1: string; @@ -31,9 +32,9 @@ interface BasicTabProps { yearFilter: { start: string; end: string; }; handleSearch: () => void; setBasicType: (type: string) => void; - setCity: (city: number) => void; + setCity: (city: string) => void; setCtpvName: (name: string) => void; - setSgg: (sgg: number) => void; + setSgg: (sgg: string) => void; setSggName: (name: string) => void; setYearFilter: (filter: { start: string; end: string }) => void; setStart1: (value: string) => void; @@ -97,6 +98,8 @@ const BasicTab: React.FC = ({ start2={start2} end1={end1} end2={end2} + city={city} + sgg={sgg} yearFilter={yearFilter} setVar1={setVar1} setVar2={setVar2} @@ -104,6 +107,8 @@ const BasicTab: React.FC = ({ setStart2={setStart2} setEnd1={setEnd1} setEnd2={setEnd2} + setCity={setCity} + setSgg={setSgg} setYearFilter={setYearFilter} handleSearch={handleSearch} /> @@ -152,10 +157,14 @@ const BasicTab: React.FC = ({ var1={var1} start1={start1} end1={end1} + city={city} + sgg={sgg} yearFilter={yearFilter} setVar1={setVar1} setStart1={setStart1} setEnd1={setEnd1} + setCity={setCity} + setSgg={setSgg} setYearFilter={setYearFilter} handleSearch={handleSearch} /> @@ -167,33 +176,30 @@ const BasicTab: React.FC = ({ start2={start2} end1={end1} end2={end2} + city={city} + sgg={sgg} yearFilter={yearFilter} setStart1={setStart1} setStart2={setStart2} setEnd1={setEnd1} setEnd2={setEnd2} + setCity={setCity} + setSgg={setSgg} setYearFilter={setYearFilter} handleSearch={handleSearch} /> )} {/* 분기처리: 우수토실 */} {basicType === "tb_ms_stofcbr" && ( -
- {/* 설치년도 */} -
- setYearFilter({ ...yearFilter, start: range.start, end: range.end })} - startLabel="설치년도" - endLabel="" - /> - {/* 조회 버튼 */} -
- -
-
-
+ )} ); diff --git a/apps/sn/app/(map-layout)/map/searchModal/operationTab.tsx b/apps/sn/app/(map-layout)/map/searchModal/operationTab.tsx index 155a17215..446632965 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/operationTab.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/operationTab.tsx @@ -24,7 +24,10 @@ export const operationTableHeadlist = [ export const operationMaintenanceTableHeadlist = [ { id: 'rowNumber', title: '일련번호' }, { id: "fcltMngNo", title: "관리번호" }, + { id: "swtrmt_zone_nm", title: "처리구역" }, { id: "usgCd", title: "용도" }, + // { id: "lgalDrblyr", title: "내용연수" }, + // { id: "acqsPrmpc", title: "취득금액" }, { id: "mqltCd", title: "재질" }, { id: "pdmtVl", title: "관경(mm)" }, { id: "prlgVl", title: "연장(m)" }, @@ -35,7 +38,10 @@ export const operationMaintenanceTableHeadlist = [ export const operationCleaningTableHeadlist = [ { id: 'rowNumber', title: '일련번호' }, { id: "fcltMngNo", title: "관리번호" }, + { id: "swtrmt_zone_nm", title: "처리구역" }, { id: "usgCd", title: "용도" }, + // { id: "lgalDrblyr", title: "내용연수" }, + // { id: "acqsPrmpc", title: "취득금액" }, { id: "mqltCd", title: "재질" }, { id: "pdmtVl", title: "관경(mm)" }, { id: "prlgVl", title: "연장(m)" }, @@ -45,13 +51,18 @@ export const operationCleaningTableHeadlist = [ export const operationInspectionTableHeadlist = [ { id: 'rowNumber', title: '일련번호' }, { id: "fcltMngNo", title: "관리번호" }, + { id: "swtrmt_zone_nm", title: "처리구역" }, { id: "usgCd", title: "용도" }, + // { id: "riskEval", title: "위험평가" }, + // { id: "acqsPrmpc", title: "취득금액" }, + // { id: "lgalDrblyr", title: "내용연수" }, + // { id: "remLife", title: "잔존수명" }, { id: "mqltCd", title: "재질" }, { id: "pdmtVl", title: "관경(mm)" }, { id: "prlgVl", title: "연장(m)" }, { id: "instlYmd", title: "설치일자" }, - { id: "strct_stat_grd", title: "구조적 상태등급" }, - { id: "oper_stat_grd", title: "운영적 상태등급" }, + { id: "strc_stts_grd", title: "구조적 상태등급" }, + { id: "oper_stts_grd", title: "운영적 상태등급" }, ]; interface OperationTabProps { diff --git a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx index fa4f35afd..0682ce8f5 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx @@ -4,7 +4,7 @@ import { Button, Label } from '@common/components'; import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox'; import { SelectBox, UiTable } from '@common/business_components/ui'; import LoadingOverlay from '@/components/ui/LoadingBar'; -import { addFileName, flattenFolders, getFileDownload, getDcTree, useDcTree, reportGubuns } from './searchModalReportApi'; +import { addFileName, flattenFolders, getFileDownload, useDcTree, reportGubuns } from './searchModalReportApi'; import useStore from '../../../store/useStore'; import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; @@ -37,8 +37,8 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg const [tableCount, setTableCount] = useState(0); const [curPage, setCurPage] = useState(1); const [yearFilter, setYearFilter] = useState({ start: "", end: "" }); - const [ctpvCd, setCtpvCd] = useState(0); - const [sggCd, setSggCd] = useState(0); + const [ctpvCd, setCtpvCd] = useState('0'); + const [sggCd, setSggCd] = useState('0'); const [reportGubun, setReportGubun] = useState("0"); // const [gubun, setGubun] = useState(""); @@ -49,8 +49,8 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg useEffect(() => { // filterParams 초기화 - setCtpvCd(0); - setSggCd(0); + setCtpvCd('0'); + setSggCd('0'); setReportGubun('0'); setYearFilter({ start: "", end: "" }); setShowTable(false); @@ -62,8 +62,8 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg }, [open]); useEffect(() => { - let newCtpvCd = ctpv.find((n) => n.name === ctpvNm)?.id || 0; - let newSggCd = sgg.find((n) => n.name === sggNm)?.id || 0; + const newCtpvCd = ctpv.find((n) => n.text === ctpvNm)?.val || '0'; + const newSggCd = sgg.find((n) => n.text === sggNm)?.val || '0'; setCtpvCd(newCtpvCd); setSggCd(newSggCd); @@ -84,9 +84,9 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg if (showTable) fetchFilteredData(); }, [curPage]); - const handleCtpvCdChange = (sidoId: number) => { + const handleCtpvCdChange = (sidoId: string) => { setCtpvCd(sidoId); - setSggCd(0); + setSggCd('0'); } // 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화 @@ -100,7 +100,6 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg // 하수도정비기본계획 테이블 cellClick 핸들러 const handleReportCellClick = (index: number | undefined) => { - console.log(index); if (typeof index === "number" && tableData[index]) { const reportData = tableData[index]; const fileName = reportData.fileName; @@ -119,21 +118,14 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg try { const reportData = flattenFolders(reportOriginData); let filterReportData = reportData; - console.log("필터링 전 보고서 데이터:", filterReportData); - console.log('ctpv, sgg, ctpvCd, sggCd', ctpv, sgg, ctpvCd, sggCd); - if (ctpvCd) { // 시도검색 - console.log('시도 실행되나'); - const sidoOption = ctpv.find((n) => String(n.id) === String(ctpvCd)); - console.log('sidoOption', sidoOption) - filterReportData = filterReportData.filter(item => item.sido === sidoOption?.name); - console.log('시도 필터링 후:', filterReportData); + if (ctpvCd !== '0') { // 시도검색 + const sidoOption = ctpv.find((n) => n.val.trim() === ctpvCd.trim()); + filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text); } - if (sggCd) { // 시군구검색 - console.log('시군구 실행되나'); - const sigunguOption = sgg.find((n) => String(n.id) === String(sggCd)); - filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.name); - console.log('시군구 필터링 후:', filterReportData); + if (sggCd !== '0') { // 시군구검색 + const sigunguOption = sgg.find((n) => String(n.val) === String(sggCd)); + filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text); } if (reportGubun !== "0") { // 보고서구분검색 const reportGubunOption = reportGubuns.find((n) => n.val === reportGubun); @@ -186,7 +178,7 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg { handleCtpvCdChange(sidoId) }} + onSidoChange={(sidoId: string) => { handleCtpvCdChange(sidoId) }} onSigunguChange={setSggCd} setIsLoading={setIsLoading} /> diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index e7f87105c..99d1741dd 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -20,6 +20,8 @@ const TABS = [ { val: "report", text: "하수도정비기본계획보고서" }, ]; +const OPERATION_TABLE_SIZE = 5; + // 날짜 조합 함수 const getDateString = ( year: string, @@ -62,9 +64,9 @@ const SearchModal: React.FC = ({ const [isResetSelection, setIsResetSelection] = useState(false); const [isResetDetailSelection, setIsResetDetailSelection] = useState(false); const [detailBizId, setDetailBizId] = useState(0); - const [city, setCity] = useState(0); + const [ctpvCd, setCtpvCd] = useState("0"); const [ctpvName, setCtpvName] = useState(''); - const [sgg, setSgg] = useState(0); + const [sggCd, setSggCd] = useState("0"); const [sggName, setSggName] = useState(''); const [start1, setStart1] = useState(""); const [start2, setStart2] = useState(""); @@ -77,7 +79,7 @@ const SearchModal: React.FC = ({ // 시작, 종료 const [yearFilter, setYearFilter] = useState({ start: "", end: "" }); - const { BIZ_TY_CD } = useStore(); + const { ctpv, sgg, BIZ_TY_CD, BIZ_SE_CD } = useStore(); // 오늘 날짜 구하기 @@ -105,8 +107,8 @@ const SearchModal: React.FC = ({ const dynamicHeadlist2: { id: string, title: string }[] = useMemo(() => { if (activeTab === 'operation') { if (workType === WORK_TYPES[0]?.val) return operationMaintenanceTableHeadlist; - if (workType === WORK_TYPES[1]?.val) return operationCleaningTableHeadlist; - if (workType === WORK_TYPES[2]?.val) return operationInspectionTableHeadlist; + if (workType === WORK_TYPES[1]?.val) return operationInspectionTableHeadlist; + if (workType === WORK_TYPES[2]?.val) return operationCleaningTableHeadlist; } return []; @@ -122,8 +124,8 @@ const SearchModal: React.FC = ({ setVar2(""); setVar3(""); setVar4(""); - setCity(0); - setSgg(0); + setCtpvCd("0"); + setSggCd("0"); setYearFilter({ start: "", end: "", @@ -201,14 +203,31 @@ const SearchModal: React.FC = ({ const dataWithRowNumber = (res.data.data || []).map((item: any, index: number) => { // 🎯 먼저 각 아이템을 포맷팅 const formattedItem = formatDisplayData(item); - const matched = MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim()); + + let ctpvCd = item?.ctpvNm ?? formattedItem?.ctpvNm ?? ''; + let sggCd = item?.sggNm ?? formattedItem?.sggNm ?? ''; + + if (!ctpvCd || !sggCd) { + const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '').trim(); + if (!ctpvCd && code) ctpvCd = code.slice(0, 2); + if (!sggCd && code) sggCd = code.slice(0, 5); + } + + const matchedMhlCode = MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim()); + const ctpvNm = ctpv.find(code => code.val.trim() === ctpvCd)?.text || ctpvCd; + const sggNm = sgg.find(code => code.val.trim() === sggCd)?.text || '-'; + const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item?.usgCd?.trim()); // 추가 처리 및 일련번호 추가 return { ...formattedItem, rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가 scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경 - mqltCd: matched ? matched.text : item.mqltCd, + mqltCd: matchedMhlCode ? matchedMhlCode.text : item.mqltCd, + usgCd: usgCdObj ? usgCdObj.text : item.usgCd, + ctpvNm, + sggNm, + swtrmt_zone_nm: item.swtrmtZoneNm || item.swtrmt_zone_nm || '-', }; }); @@ -218,7 +237,7 @@ const SearchModal: React.FC = ({ } else if (activeTab === 'operation') { const RequestDTO: Record = { page: `${curPage - 1}`, - size: '5', + size: `${OPERATION_TABLE_SIZE}`, }; if (var1) RequestDTO.bizInfoNm = var1; if (var2 != '0' && var2) RequestDTO.bizTypeCd = var2; @@ -250,7 +269,7 @@ const SearchModal: React.FC = ({ const matched = BIZ_TY_CD.find(code => code.lwpoCd === item.bizTypeCd); return { ...formattedItem, - rowNumber: (curPage - 1) * 5 + index + 1, // 1부터 시작하는 일련번호 추가 + rowNumber: (curPage - 1) * OPERATION_TABLE_SIZE + index + 1, // 1부터 시작하는 일련번호 추가 bizTypeCd: matched ? matched.lwpoCdNm : item.bizTypeCd, }; }); @@ -278,7 +297,8 @@ const SearchModal: React.FC = ({ // 추후 작성 예정 if (var1) cqlFilters.push(`usg_cd='${var1}'`); if (var2) cqlFilters.push(`mqlt_cd='${var2}'`); - if (var3) cqlFilters.push(`pbadms_emd_cd='${var3}'`); + if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -346,8 +366,8 @@ const SearchModal: React.FC = ({ // 하수처리구역용 CQL 필터 생성 const zoneCqlFilters = () => { const cqlFilters: string[] = []; - if (city) cqlFilters.push(`ctpv_cd='${city}'`); - if (sgg) cqlFilters.push(`sgg_cd='${sgg}'`); + if (ctpvCd !== '' && ctpvCd !== '0') cqlFilters.push(`ctpv_cd='${ctpvCd.trim()}'`); + if (sggCd !== '' && sggCd !== '0') cqlFilters.push(`sgg_cd='${sggCd.trim()}'`); if (var1) cqlFilters.push(`swtrmt_zone_nm ILIKE '%${var1}%'`); if (var2) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${var2}%'`); return cqlFilters; @@ -355,6 +375,9 @@ const SearchModal: React.FC = ({ // 하수맨홀용 CQL 필터 생성 const mhCqlFilters = () => { const cqlFilters: string[] = []; + // 시도, 시군구 + if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); // // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -382,6 +405,9 @@ const SearchModal: React.FC = ({ // 하수펌프장용 CQL 필터 생성 const psmtnCqlFilters = () => { const cqlFilters: string[] = []; + // 시도, 시군구 + if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -414,6 +440,9 @@ const SearchModal: React.FC = ({ // 우수토실용 CQL 필터 생성 const otflCqlFilters = () => { const cqlFilters: string[] = []; + // 시도, 시군구 + if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -470,8 +499,8 @@ const SearchModal: React.FC = ({ try { const RequestDTO: Record = { page: `${detailCurPage - 1}`, - size: '5', - bizId: tableData[index % 5].bizId, + size: `${OPERATION_TABLE_SIZE}`, + bizId: tableData[index % OPERATION_TABLE_SIZE].bizId, }; RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); @@ -484,7 +513,7 @@ const SearchModal: React.FC = ({ const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim()); return { ...formattedItem, - rowNumber: (detailCurPage - 1) * 5 + detailIndex + 1, + rowNumber: (detailCurPage - 1) * OPERATION_TABLE_SIZE + detailIndex + 1, usgCd: usgCdObj ? usgCdObj.text : item.usgCd, mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd, }; @@ -508,7 +537,7 @@ const SearchModal: React.FC = ({ try { const RequestDTO: Record = { page: `${detailCurPage - 1}`, - size: '5', + size: `${OPERATION_TABLE_SIZE}`, bizId: String(detailBizId), }; const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); @@ -520,7 +549,7 @@ const SearchModal: React.FC = ({ const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim()); return { ...formattedItem, - rowNumber: (detailCurPage - 1) * 5 + detailIndex + 1, + rowNumber: (detailCurPage - 1) * OPERATION_TABLE_SIZE + detailIndex + 1, usgCd: usgCdObj ? usgCdObj.text : item.usgCd, mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd, }; @@ -554,8 +583,8 @@ const SearchModal: React.FC = ({ {activeTab === 'basic' && ( = ({ yearFilter={yearFilter} handleSearch={handleSearch} setBasicType={setBasicType} - setCity={setCity} + setCity={setCtpvCd} setCtpvName={setCtpvName} - setSgg={setSgg} + setSgg={setSggCd} setSggName={setSggName} setYearFilter={setYearFilter} setStart1={setStart1} @@ -640,7 +669,7 @@ const SearchModal: React.FC = ({ } tableData={tableData} headlist={dynamicHeadlist} - pageSize={5} + pageSize={OPERATION_TABLE_SIZE} total={tableCount} cellClick={handleOperationCellClick} onPageChange={setCurPage} @@ -663,7 +692,7 @@ const SearchModal: React.FC = ({ } tableData={detailTableData} headlist={dynamicHeadlist2} - pageSize={5} + pageSize={OPERATION_TABLE_SIZE} total={detailTableCount} cellClick={undefined} onPageChange={setDetailCurPage} @@ -682,10 +711,10 @@ const SearchModal: React.FC = ({ open={open} onOpenChange={onOpenChange} bodyClass={` - h-[100vh] - w-[60vw] - max-w-[60vw] - max-h-[100vh] + h-[100dvh] + w-[60dvw] + max-w-[100dvw] + max-h-[100dvh] overflow-hidden z-[101] `} diff --git a/apps/sn/app/(map-layout)/map/searchModal/stofcbr.tsx b/apps/sn/app/(map-layout)/map/searchModal/stofcbr.tsx index 700d60cf0..2e4fd26f0 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/stofcbr.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/stofcbr.tsx @@ -3,32 +3,53 @@ import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox'; import { Button } from '@common/components'; import React from 'react'; +import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; interface StofcbrProps { + city: string; + sgg: string; yearFilter: { start: string; end: string }; + setCity: (value: string) => void; + setSgg: (value: string) => void; setYearFilter: (filter: { start: string; end: string }) => void; handleSearch: () => void; } const Stofcbr: React.FC = ({ + city, + sgg, yearFilter, + setCity, + setSgg, setYearFilter, handleSearch, }) => { return (
- {/* 설치년도 */}
- setYearFilter({ ...yearFilter, start: range.start, end: range.end })} - startLabel="설치년도" - endLabel="" + {/* 시도, 시군구 */} + { + setCity(sidoId); + setSgg("0"); + }} + onSigunguChange={setSgg} /> - {/* 조회 버튼 */} -
- + {/* 설치년도 */} +
+ setYearFilter({ ...yearFilter, start: range.start, end: range.end })} + startLabel="설치년도" + endLabel="" + /> + {/* 조회 버튼 */} +
+ +
diff --git a/apps/sn/app/(map-layout)/map/searchModal/swageMhl.tsx b/apps/sn/app/(map-layout)/map/searchModal/swageMhl.tsx index 0951ceb2f..6face5b38 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/swageMhl.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/swageMhl.tsx @@ -5,16 +5,21 @@ import { Button, Input, Label } from '@common/components'; import React from 'react'; import { MS_SWAGE_MHL_USE_CODES } from '../components/common/table'; import { SelectBox } from '@common/business_components/ui'; +import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; interface SwageMhlProps { var1: string; start1: string; end1: string; + city: string; + sgg: string; yearFilter: { start: string; end: string }; setVar1: (value: string) => void; setStart1: (value: string) => void; setEnd1: (value: string) => void; + setCity: (value: string) => void; + setSgg: (value: string) => void; setYearFilter: (filter: { start: string; end: string }) => void; handleSearch: () => void; } @@ -23,10 +28,14 @@ const SwageMhl: React.FC = ({ var1, start1, end1, + city, + sgg, yearFilter, setVar1, setStart1, setEnd1, + setCity, + setSgg, setYearFilter, handleSearch, }) => { @@ -34,6 +43,15 @@ const SwageMhl: React.FC = ({ return (
+ { + setCity(sidoId); + setSgg("0"); + }} + onSigunguChange={setSgg} + /> {/* 설치년도 */} void; setStart2: (value: string) => void; setEnd1: (value: string) => void; setEnd2: (value: string) => void; + setCity: (value: string) => void; + setSgg: (value: string) => void; setYearFilter: (filter: { start: string; end: string }) => void; handleSearch: () => void; } @@ -23,11 +28,15 @@ const SwagePmstn: React.FC = ({ start2, end1, end2, + city, + sgg, yearFilter, setStart1, setStart2, setEnd1, setEnd2, + setCity, + setSgg, setYearFilter, handleSearch, }) => { @@ -35,6 +44,16 @@ const SwagePmstn: React.FC = ({ return (
+ {/* 시도, 시군구 */} + { + setCity(sidoId); + setSgg("0"); + }} + onSigunguChange={setSgg} + /> {/* 설치년도 */} = ({ startLabel="설치년도" endLabel="" /> - {/* 일일처리용량 - 오수맨홀 */} + {/* 일일처리용량 */} = ({ } }} /> - {/* 양수능력 - 우수맨홀 */} - + {/* 양수능력 */} + {/* = ({ handleSearch(); } }} - /> + /> */} {/* 조회 버튼 */}
diff --git a/apps/sn/app/(map-layout)/map/searchModal/swagePpl.tsx b/apps/sn/app/(map-layout)/map/searchModal/swagePpl.tsx index f729ab6eb..ee599887a 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/swagePpl.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/swagePpl.tsx @@ -5,6 +5,7 @@ import { SelectBox } from '@common/business_components/ui'; import { Button, Input, Label } from '@common/components'; import React from 'react'; import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES } from '../components/common/table'; +import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; interface SwagePplProps { var1: string; @@ -13,6 +14,8 @@ interface SwagePplProps { start2: string; end1: string; end2: string; + city: string; + sgg: string; yearFilter: { start: string; end: string }; setVar1: (value: string) => void; setVar2: (value: string) => void; @@ -20,6 +23,8 @@ interface SwagePplProps { setStart2: (value: string) => void; setEnd1: (value: string) => void; setEnd2: (value: string) => void; + setCity: (value: string) => void; + setSgg: (value: string) => void; setYearFilter: (filter: { start: string; end: string }) => void; handleSearch: () => void; } @@ -31,6 +36,8 @@ const SwagePpl: React.FC = ({ start2, end1, end2, + city, + sgg, yearFilter, setVar1, setVar2, @@ -38,6 +45,8 @@ const SwagePpl: React.FC = ({ setStart2, setEnd1, setEnd2, + setCity, + setSgg, setYearFilter, handleSearch, }) => { @@ -45,6 +54,15 @@ const SwagePpl: React.FC = ({ return (
+ { + setCity(sidoId); + setSgg("0"); + }} + onSigunguChange={setSgg} + /> {/* 설치년도 */} = ({ {/* 용도 */} = ({ selectContentClass="z-[101]" /> {/* 재질 */} - + {/* = ({ setVar2(String(val)); }} selectContentClass="z-[101]" - /> + /> */} {/* 관경 */} = ({ }} /> {/* 연장 */} - + {/* = ({ handleSearch(); } }} - /> + /> */} {/* 조회 버튼 */}
diff --git a/apps/sn/app/(map-layout)/map/searchModal/swagePrcsFclt.tsx b/apps/sn/app/(map-layout)/map/searchModal/swagePrcsFclt.tsx index 8779f0fa5..a3db595a5 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/swagePrcsFclt.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/swagePrcsFclt.tsx @@ -12,17 +12,17 @@ interface SwagePrcsFcltProps { start2: string; end1: string; end2: string; - city: number; - sgg: number; + city: string; + sgg: string; yearFilter: { start: string; end: string }; setVar1: (value: string) => void; setStart1: (value: string) => void; setStart2: (value: string) => void; setEnd1: (value: string) => void; setEnd2: (value: string) => void; - setCity: (value: number) => void; + setCity: (value: string) => void; setCtpvName: (name: string) => void; - setSgg: (value: number) => void; + setSgg: (value: string) => void; setSggName: (name: string) => void; setYearFilter: (filter: { start: string; end: string }) => void; handleSearch: () => void; @@ -52,7 +52,7 @@ const SwagePrcsFclt: React.FC = ({ sigungu={sgg} onSidoChange={(sidoId) => { setCity(sidoId); - setSgg(0); + setSgg("0"); }} onSigunguChange={setSgg} setCtpvName={setCtpvName} diff --git a/apps/sn/app/(map-layout)/map/searchModal/swagePrcsZone.tsx b/apps/sn/app/(map-layout)/map/searchModal/swagePrcsZone.tsx index 8cb304d2a..9f8e8a181 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/swagePrcsZone.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/swagePrcsZone.tsx @@ -7,12 +7,12 @@ import React from 'react'; interface swagePrcsZoneProps { var1: string; var2: string; - city: number; - sgg: number; + city: string; + sgg: string; setVar1: (value: string) => void; setVar2: (value: string) => void; - setCity: (value: number) => void; - setSgg: (value: number) => void; + setCity: (value: string) => void; + setSgg: (value: string) => void; handleSearch: () => void; } @@ -34,9 +34,9 @@ const swagePrcsZone: React.FC = ({ { + onSidoChange={(sidoId: string) => { setCity(sidoId); - setSgg(0); + setSgg("0"); }} onSigunguChange={setSgg} /> diff --git a/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx b/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx index 275126474..bf056f4e7 100644 --- a/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx +++ b/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx @@ -39,18 +39,18 @@ type BaseLayout = { firstDivBtn?: boolean selectBox?: SelectBox showDatePicker?: boolean; - ctpvCd: number; - sggCd: number; + ctpvCd: string; + sggCd: string; sggName?: string; sigungu?: ApiSelectOption - selectedSwagePrcsFclt?: string | number; - onSwagePrcsFcltChange?: (value: string | number) => void; + selectedSwagePrcsFclt?: string; + onSwagePrcsFcltChange?: (value: string) => void; aplcnYmFrom?: string setAplcnYmFrom?: (aplcnYmFrom: string) => void; aplcnYmTo?: string setAplcnYmTo?: (aplcnYmTo: string) => void; - setCtpvCd: (ctpv: number) => void; - setSggCd: (sgg: number) => void; + setCtpvCd: (ctpv: string) => void; + setSggCd: (sgg: string) => void; setSggName?: (sggName: string) => void; setSwtrmtFcltNm?: (swtrmtFcltNm: string) => void; setIsLoading?: (isLoading: boolean) => void; @@ -178,9 +178,9 @@ const BaseLayout = ({ sido={ctpvCd} sigungu={sggCd} sggName={sggName} - onSidoChange={(sidoId: number) => { + onSidoChange={(sidoId: string) => { setCtpvCd(sidoId); - setSggCd(0); + setSggCd('0'); }} onSigunguChange={setSggCd} setSggName={setSggName} diff --git a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx index 0f3d710c7..de187da5d 100644 --- a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx @@ -9,7 +9,7 @@ import { zodResolver } from '@hookform/resolvers/zod' import { useState } from 'react' import { useForm } from 'react-hook-form' import { z } from 'zod' -import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData } from '@/components/lib/utils' +import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData, excelToRcptrInfoData } from '@/components/lib/utils' import BaseLayout from '../BaseLayout' import { validateMultipleFiles, FILE_EXTENSIONS, type FileValidationConfig } from '../../fileValidation'; import { validateBasicSelectionsWithAlert } from '../../selectionValidation'; @@ -42,19 +42,14 @@ interface UploadedFileInfo { // 초기 파일 정보 상태 const initialFileInfo: UploadedFileInfo = { file: null, atflCd: null, atflId: null }; -interface ApiSelectOption { - text: string; - val: string; -} - const IrSetting = () => { const [input1, setInput1] = useState('0.8'); // 오수전환율 초기값 const [input2, setInput2] = useState(''); - const [select1, setSelect1] = useState('0'); // 하수처리장코드 + const [select1, setSelect1] = useState('0'); // 하수처리장코드 const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태 const [isLoadingResult, setIsLoadingResult] = useState(false); // 로딩 상태 - const [ctpvCd, setCtpvCd] = useState(0); // 시도 코드 상태 - const [sggCd, setSggCd] = useState(0); // 시군구 코드 상태 + const [ctpvCd, setCtpvCd] = useState('0'); // 시도 코드 상태 + const [sggCd, setSggCd] = useState('0'); // 시군구 코드 상태 const [sggName, setSggName] = useState(''); // 시군구 이름 상태 const [swtrmtFcltNm, setSwtrmtFcltNm] = useState(''); // 하수처리시설명 상태 @@ -116,7 +111,7 @@ const IrSetting = () => { setSelectedDate(`${year}${month}`); setInput1(''); // 입력 필드 초기화 setInput2(''); // 입력 필드 초기화 - setSelect1(0); + setSelect1('0'); setSwtrmtFcltNm(''); setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경 }; @@ -177,12 +172,14 @@ const IrSetting = () => { const file1 = uploadedFilesData[0]; // 오수관망도 const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과 const file3 = uploadedFilesData[2]; // 오수량 시간패턴 - const file4 = uploadedFilesData[3]; // 유량자료 + const file4 = uploadedFilesData[3]; // 유량자료 + const file5 = uploadedFilesData[4]; // 수용가 상수사용량 console.log("오수관망도 정보:", file1?.file); console.log("수용가-맨홀 매칭결과 정보:", file2); console.log("오수량 시간패턴 정보:", file3); console.log("유량자료 정보:", file4); + console.log("수용가 상수사용량 정보:", file5); // 모든 필수 파일이 선택되었는지 확인 (UploadedFileInfo 객체와 그 안의 file 객체 모두 확인) // 유량자료는 선택값 @@ -219,6 +216,14 @@ const IrSetting = () => { fileDescription: '유량자료' }); } + // 수용가 상수사용량 파일이 있는 경우 검증 목록에 추가 + if (file5?.file) { + validationConfigs.push({ + file: file5.file, + allowedExtensions: FILE_EXTENSIONS.EXCEL, + fileDescription: '수용가 상수사용량' + }); + } // 파일 확장자 검증 실행 const validationResult = validateMultipleFiles(validationConfigs); @@ -247,18 +252,22 @@ const IrSetting = () => { type MatchingResult = { anlsSn: number; mhlMngNo: string; rcptrNo: string; }; type TimePattern = { hrId: number; rt?: number | null | undefined; }; type FlowRate = { pplMngNo: string; mtrngDt: string; oilq?: number | null | undefined; }; + type rcptrInfo = { rcptrNo: string; useYm: string; usqty?: number | null | undefined; }; const filePickers: { file: UploadedFileInfo; - parser: (excelData: any) => MatchingResult[] | TimePattern[] | FlowRate[]; + parser: (excelData: any) => MatchingResult[] | TimePattern[] | FlowRate[] | rcptrInfo[]; }[] = [ { file: file2, parser: excelToMatchingResultData }, { file: file3, parser: excelToTimePatternData } ]; - if (file4?.file) { + if (file4?.file) { // 유량자료 파일이 있는 경우 filePickers.push({ file: file4, parser: excelToFlowRateData }); } + if (file5?.file) { // 수용가 상수사용량 파일이 있는 경우 + filePickers.push({ file: file5, parser: excelToRcptrInfoData }); + } try { const parsedResults = await Promise.all( @@ -295,11 +304,14 @@ const IrSetting = () => { const flowRateData = file4?.file ? parsedResults.find(r => r && r.fileName === file4.file?.name)?.parsedData : null; + const rcptrInfoData = file5?.file + ? parsedResults.find(r => r && r.fileName === file5.file?.name)?.parsedData + : null; // 새로운 JSON 데이터 생성 const combinedAnalysisData = { - ctpvCd: (ctpvCd ? String(ctpvCd) : null), - sggCd: (sggCd ? String(sggCd) : null), // 시군구코드 (선택값) + ctpvCd: ctpvCd !== '0' ? String(ctpvCd).trim() : null, + sggCd: sggCd !== '0' ? String(sggCd) : null, // 시군구코드 (선택값) swtrmtFcltNm: swtrmtFcltNm ?? null, aplcnYm: selectedDate, // 적용연월 drtwSwtcRt: parseFloat(input1) || null, // 오수전환율 (숫자로 변환) @@ -315,6 +327,7 @@ const IrSetting = () => { tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과 tbSnHrPttn: timePatternData, // 오수량 시간패턴 tbSnOilqData: flowRateData, // 유량자료 (없을 수 있음) + tbSnRcptrInfo: rcptrInfoData, // 수용가 상수사용량 (없을 수 있음) }; console.log("통합 분석 데이터 (JSON):", combinedAnalysisData); @@ -329,7 +342,9 @@ const IrSetting = () => { return; } } - + // const handleAnalysisClick = () => { + // } + const rcptrInfoShow = false; return ( <> @@ -393,6 +408,15 @@ const IrSetting = () => { startYear={1900} // 시작 연도 설정 /> + {rcptrInfoShow && + handleFileSelection(4, file, cd, id)} + showButton={false} + showPreview={true} + accept={".xls, .xlsx, .xlsm, .xltx"} + fileIdx={4} /> + } (false); // 로딩 상태 @@ -112,9 +107,9 @@ export default function RTKForm() { initialFileInfo, // 2: 강우자료 ]); - const [select1, setSelect1] = useState('0'); // 하수처리장 (swtrmtFcltCd) - const [ctpvCd, setCtpvCd] = useState(0); // 시도 코드 상태 - const [sggCd, setSggCd] = useState(0); // 시군구 코드 상태 + const [select1, setSelect1] = useState('0'); // 하수처리장 (swtrmtFcltCd) + const [ctpvCd, setCtpvCd] = useState('0'); // 시도 코드 상태 + const [sggCd, setSggCd] = useState('0'); // 시군구 코드 상태 const [sggName, setSggName] = useState(''); // 시군구 이름 상태 const [swtrmtFcltNm, setSwtrmtFcltNm] = useState(''); // 하수처리시설명 상태 @@ -290,8 +285,8 @@ export default function RTKForm() { // 새로운 JSON 데이터 생성 const combinedAnalysisData = { aplcnYm: formData.date, // 적용연월 (YYYYMM 형식) - ctpvCd: ctpvCd ? String(ctpvCd) : null, // 시도코드 (선택값) - sggCd: sggCd ? String(sggCd) : null, // 시군구코드 (선택값) + ctpvCd: ctpvCd !== '0' ? String(ctpvCd).trim() : null, // 시도코드 (선택값) + sggCd: sggCd !== '0' ? String(sggCd) : null, // 시군구코드 (선택값) swtrmtFcltNm: swtrmtFcltNm ?? null, atflCd: file1?.atflCd, // 첨부파일CD (오수관망도) atflId: file1?.atflId, // 첨부파일ID (오수관망도) @@ -357,10 +352,6 @@ export default function RTKForm() { name: "initialDepths" }) - const onSubmit = (data: z.infer) => { - console.log("✅ 제출 데이터:", data) - } - // 단위도 계수 테이블 설정 (EditableTableHeadItem 타입 사용 및 id 수정) const unitFactorHeadlist: EditableTableHeadItem[] = [ { id: 'response', title: 'Response', editable: false, width: '150px' }, // width 속성 추가 @@ -377,11 +368,6 @@ export default function RTKForm() { { id: 'Do', title: 'Do', editable: true, inputProps: { type: 'text' } } ]; - // 테이블 페이지 변경 핸들러 - const handlePageChange = (page: number) => { - console.log('Page changed:', page) - } - return ( <> diff --git a/apps/sn/app/(with-base-layout)/ii/rdiiResult/page.tsx b/apps/sn/app/(with-base-layout)/ii/rdiiResult/page.tsx index 784aeb6a5..354467d9d 100644 --- a/apps/sn/app/(with-base-layout)/ii/rdiiResult/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/rdiiResult/page.tsx @@ -77,8 +77,8 @@ const Result = () => { const [resetSelection, setResetSelection] = useState(false); const [isResetPage, setIsResetPage] = useState(false); const [curPage, setCurPage] = useState(1); // 현재 페이지 - const [ctpvCd, setCtpvCd] = useState(0); // 시도 코드 상태 - const [sggCd, setSggCd] = useState(0); // 시군구 코드 상태 + const [ctpvCd, setCtpvCd] = useState('0'); // 시도 코드 상태 + const [sggCd, setSggCd] = useState('0'); // 시군구 코드 상태 const [sggName, setSggName] = useState(''); // 시군구 이름 상태 const {ctpv, sgg} = useStore(); @@ -112,8 +112,8 @@ const Result = () => { const inftwtAnlsSearchDto = { aplcnYmFrom: formValues.aplcnYmFrom?.trim() ? formValues.aplcnYmFrom : null, aplcnYmTo: formValues.aplcnYmTo?.trim() ? formValues.aplcnYmTo : null, - ctpvCd: ctpvCd || null, - sggCd: sggCd || null, + ctpvCd: ctpvCd !== '0' ? ctpvCd.trim() : null, + sggCd: sggCd !== '0' ? sggCd : null, swtrmtFcltNm: formValues.swtrmtFcltNm?.trim() ? formValues.swtrmtFcltNm : null, auth: AUTH, }; @@ -140,15 +140,15 @@ const Result = () => { const dataWithRowNumber = (data.content || []).map((item: any, index: number) => { // 🎯 먼저 각 아이템을 포맷팅 const formattedItem = formatDisplayData(item); - const ctpvOne = ctpv.find(code => code.id === item.ctpvCd); - const sggOne = sgg.find(code => code.id === item.sggCd); + const ctpvOne = ctpv.find(code => code.val.trim() === item?.ctpvCd?.trim()); + const sggOne = sgg.find(code => code.val === item.sggCd); // 추가 처리 및 일련번호 추가 return { ...formattedItem, rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가 scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경 - ctpvNm: ctpvOne ? ctpvOne.name : item.ctpvCd, - sggNm: sggOne ? sggOne.name : item.sggCd, + ctpvNm: ctpvOne ? ctpvOne.text.trim() : item?.ctpvCd?.trim(), + sggNm: sggOne ? sggOne.text : item.sggCd, }; }) .filter((item: any) => item.scsYn === 'Y'); diff --git a/apps/sn/app/(with-base-layout)/ii/result/page.tsx b/apps/sn/app/(with-base-layout)/ii/result/page.tsx index 6e929933a..8df0a60a8 100644 --- a/apps/sn/app/(with-base-layout)/ii/result/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/result/page.tsx @@ -74,8 +74,8 @@ const Result = () => { const [resetSelection, setResetSelection] = useState(false); const [isResetPage, setIsResetPage] = useState(false); const [curPage, setCurPage] = useState(1); // 현재 페이지 - const [ctpvCd, setCtpvCd] = useState(0); // 시도 코드 상태 - const [sggCd, setSggCd] = useState(0); // 시군구 코드 상태 + const [ctpvCd, setCtpvCd] = useState('0'); // 시도 코드 상태 + const [sggCd, setSggCd] = useState('0'); // 시군구 코드 상태 const [sggName, setSggName] = useState(''); // 시군구 이름 상태 const {ctpv, sgg} = useStore(); @@ -106,8 +106,8 @@ const Result = () => { let inftwtAnlsSearchDto = { "aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null, "aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null, - "ctpvCd": ctpvCd === 0 ? null : ctpvCd, - "sggCd": sggCd === 0 ? null : sggCd, + "ctpvCd": ctpvCd === '0' ? null : ctpvCd.trim(), + "sggCd": sggCd === '0' ? null : sggCd, "swtrmtFcltNm": formValues.swtrmtFcltNm?.trim() ? formValues.swtrmtFcltNm : null, "auth": AUTH, }; @@ -135,15 +135,15 @@ const Result = () => { const dataWithRowNumber = (data.content || []).map((item: any, index: number) => { // 🎯 먼저 각 아이템을 포맷팅 const formattedItem = formatDisplayData(item); - const ctpvOne = ctpv.find(code => code.id === item.ctpvCd); - const sggOne = sgg.find(code => code.id === item.sggCd); + const ctpvOne = ctpv.find(code => code.val.trim() === item?.ctpvCd?.trim()); + const sggOne = sgg.find(code => code.val === item.sggCd); // 추가 처리 및 일련번호 추가 return { ...formattedItem, rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가 scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경 - ctpvNm: ctpvOne ? ctpvOne.name : item.ctpvCd, - sggNm: sggOne ? sggOne.name : item.sggCd, + ctpvNm: ctpvOne ? ctpvOne.text : item.ctpvCd, + sggNm: sggOne ? sggOne.text : item.sggCd, }; }) .filter((item: any) => item.scsYn === 'Y'); diff --git a/apps/sn/app/(with-base-layout)/layout.tsx b/apps/sn/app/(with-base-layout)/layout.tsx index f8e046717..229415e53 100644 --- a/apps/sn/app/(with-base-layout)/layout.tsx +++ b/apps/sn/app/(with-base-layout)/layout.tsx @@ -20,10 +20,11 @@ export default function WithBaseLayout({ const setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo); - const { fetchCtpvSggData, fetchBIZ_SE_CD, fetchBIZ_TY_CD } = useStore(); + const { fetchCtpv, fetchSgg, fetchBIZ_SE_CD, fetchBIZ_TY_CD } = useStore(); useEffect(() => { - fetchCtpvSggData(); + fetchCtpv(); + fetchSgg(); fetchBIZ_SE_CD(); fetchBIZ_TY_CD(); }, []); diff --git a/apps/sn/app/components/lib/utils/excelToJson.ts b/apps/sn/app/components/lib/utils/excelToJson.ts index 837b099cc..b760dae13 100644 --- a/apps/sn/app/components/lib/utils/excelToJson.ts +++ b/apps/sn/app/components/lib/utils/excelToJson.ts @@ -157,6 +157,25 @@ export const excelToFlowRateData = (excelData: any) => { return flowRateData; }; +// 수용가 물사용량 +export const rcptrInfoZod = z.object({ + rcptrNo: z.string(), // 수용가번호 + useYm: z.string(), // 사용연월 + usqty: z.number().nullish(), // 사용량(㎥) +}); +export type rcptrInfoZodType = z.infer; +export const excelToRcptrInfoData = (excelData: any) => { + const flowRateData: rcptrInfoZodType[] = excelData.map((item: any) => { + + return { + rcptrNo: String(item["수용가번호"]), + useYm: String(item["사용연월"]), + usqty: Number(item["사용량(㎥)"]), + }; + }).filter((el: any) => el !== undefined); + return flowRateData; +}; + // 강우자료 (수정 필요) export const rainZod = z.object({ diff --git a/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx b/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx index a7472d4c0..6ff83f593 100644 --- a/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx +++ b/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { SelectBox } from '@common/business_components/ui' import { Label } from '@common/components'; import useStore from '../../store/useStore'; @@ -10,16 +10,16 @@ interface ApiSelectOption { val: string; } interface CtpvSggPrcsFcltSelectBoxProps { - sido: number; - sigungu: number; + sido: string; + sigungu: string; sggName?: string; - onSidoChange: (id: number) => void; - onSigunguChange: (id: number) => void; + onSidoChange: (id: string) => void; + onSigunguChange: (id: string) => void; setCtpvName?: (name: string) => void; setSggName?: (name: string) => void; className?: string; - selectedSwagePrcsFclt?: string | number; - onSwagePrcsFcltChange?: (value: string | number) => void; + selectedSwagePrcsFclt?: string; + onSwagePrcsFcltChange?: (value: string) => void; setSwtrmtFcltNm?: (name: string) => void; setIsLoading?: (isLoading: boolean) => void; } @@ -43,17 +43,22 @@ export function CtpvSggPrcsFcltSelectBox({ const [prcsFcltOptions, setPrcsFcltOptions] = useState([]); const { ctpv, sgg } = useStore(); + const sidoStr = String(sido ?? ''); + const sidoPrefix = sidoStr.slice(0, 2); useEffect(() => { (async () => { - onSidoChange(0); - onSigunguChange(0); + onSidoChange("0"); + onSigunguChange("0"); if (setCtpvName && setSggName) { setCtpvName(""); setSggName(""); } if (setSwtrmtFcltNm) setSwtrmtFcltNm(""); })(); + + console.log("ctpv", ctpv); + console.log("sgg", sgg); }, []); useEffect(() => { @@ -109,38 +114,28 @@ export function CtpvSggPrcsFcltSelectBox({
({ val: opt.id, text: opt.name })) - ] - } + selectArray={[defaultOption, ...ctpv]} value={sido} onSelectValue={val => { - onSidoChange(Number(val)); - if (setCtpvName) setCtpvName(ctpv.find(opt => Number(opt.id) === Number(val))?.name || ""); + onSidoChange(String(val)); + if (setCtpvName) setCtpvName(ctpv.find(opt => String(opt.val) === String(val))?.text || ""); if (setSwtrmtFcltNm) setSwtrmtFcltNm(""); }} selectContentClass="z-[101]" /> String(opt.id).slice(0, 2) === String(sido)) - .map(opt => ({ val: opt.id, text: opt.name })) - ] - } + selectArray={[defaultOption, ...sgg.filter(opt => opt.val.startsWith(sidoPrefix))]} value={sigungu} onSelectValue={val => { - onSigunguChange(Number(val)) - if (setSggName) setSggName(sgg.find(opt => Number(opt.id) === Number(val))?.name || ""); + onSigunguChange(String(val)) + if (setSggName) setSggName(sgg.find(opt => String(opt.val) === String(val))?.text || ""); if (setSwtrmtFcltNm) setSwtrmtFcltNm("") }} selectContentClass="z-[101]" @@ -155,7 +150,7 @@ export function CtpvSggPrcsFcltSelectBox({ selectArray={[defaultOption, ...prcsFcltOptions]} value={selectedSwagePrcsFclt} onSelectValue={(val) => { - onSwagePrcsFcltChange?.(val) + onSwagePrcsFcltChange?.(String(val)) if (setSwtrmtFcltNm) setSwtrmtFcltNm(prcsFcltOptions.find(opt => String(opt.val) === String(val))?.text || "") }} selectContentClass="z-[101]" diff --git a/apps/sn/app/store/index.ts b/apps/sn/app/store/index.ts index 40e094480..de827c9a1 100644 --- a/apps/sn/app/store/index.ts +++ b/apps/sn/app/store/index.ts @@ -1,8 +1,6 @@ import { getCmsData } from "@/components/lib/api"; -export interface Option { id: number, name: string } - export type BIZ_CD_TYPE = { lwpoCd: string, upCd: string, @@ -11,28 +9,34 @@ export type BIZ_CD_TYPE = { prntLwpoCd: null }; -export const fetchCtpvSggData = async (set: (state: any) => void) => { - try { - const ctpvData = await getCmsData('/api/v1/code/cache/ADSTDSTRCD/Y'); - const ctpvList: Option[] = ctpvData - .filter((item: any) => typeof item.lwpoCd === 'string' && item.lwpoCd.length === 2) - .map((feature: any) => ({ - id: feature.lwpoCd, - name: feature.lwpoCdNm - })); +export const fetchCtpv = async (set: (state: any) => void) => { + const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_ctpv&outputFormat=application/json&propertyName=mctpv_korn_nm,mctpv_cd`; + const res = await fetch(url).then(res => res.json()); - const sggList: Option[] = ctpvData - .filter((item: any) => typeof item.lwpoCd === 'string' && item.lwpoCd.length !== 2) - .map((item: any) => ({ - id: item.lwpoCd, - name: item.lwpoCdNm - })); + const list = res.features + .map((f: any) => ({ + text: f.properties.mctpv_korn_nm, + val: f.properties.mctpv_cd, + })) + .sort((a, b) => a.text.localeCompare(b.text, 'ko')); - set({ ctpv: ctpvList, sgg: sggList }); - } catch (e: any) { - // alert("시도, 시군구 데이터 로드 실패: " + e.message); - console.error("CTPV 데이터 로드 실패:", e); - } + console.log('시도 데이터', list); + set({ ctpv: list}); +}; + +export const fetchSgg = async (set: (state: any) => void) => { + const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_sgg&outputFormat=application/json&propertyName=sgg_cd,sgg_korn_nm`; + const res = await fetch(url).then(res => res.json()); + + const list = res.features + .map((f: any) => ({ + text: f.properties.sgg_korn_nm, + val: f.properties.sgg_cd, + })) + .sort((a, b) => a.text.localeCompare(b.text, 'ko')); + + console.log('시군구 데이터', list); + set({ sgg: list }); }; export const fetchBIZ_SE_CD = async (set: (state: any) => void) => { diff --git a/apps/sn/app/store/useStore.ts b/apps/sn/app/store/useStore.ts index b2ae014fa..678090ed7 100644 --- a/apps/sn/app/store/useStore.ts +++ b/apps/sn/app/store/useStore.ts @@ -1,8 +1,6 @@ import { create } from "zustand"; -import { getGeoData, getCmsData } from "@/components/lib/api"; -import { fetchCtpvSggData, fetchBIZ_SE_CD, fetchBIZ_TY_CD } from "./index"; - -interface Option { id: number, name: string } +import { fetchCtpv, fetchSgg, fetchBIZ_SE_CD, fetchBIZ_TY_CD } from "./index"; +import { type SelectDataType } from "@common/business_components"; export type BIZ_CD_TYPE = { lwpoCd: string, @@ -13,11 +11,12 @@ export type BIZ_CD_TYPE = { }; interface SnStoreState { - ctpv: Option[]; - sgg: Option[]; + ctpv: SelectDataType[]; + sgg: SelectDataType[]; BIZ_SE_CD: BIZ_CD_TYPE[]; BIZ_TY_CD: BIZ_CD_TYPE[]; - fetchCtpvSggData: () => Promise; + fetchCtpv: () => Promise; + fetchSgg: () => Promise; fetchBIZ_SE_CD: () => Promise; fetchBIZ_TY_CD: () => Promise; } @@ -27,7 +26,8 @@ const useStore = create((set) => ({ sgg: [], BIZ_SE_CD: [], BIZ_TY_CD: [], - fetchCtpvSggData: () => fetchCtpvSggData(set), + fetchCtpv: () => fetchCtpv(set), + fetchSgg: () => fetchSgg(set), fetchBIZ_SE_CD: () => fetchBIZ_SE_CD(set), fetchBIZ_TY_CD: () => fetchBIZ_TY_CD(set), })); diff --git a/packages/common/business_components/src/ui/map/searchBar/procSearchBar.tsx b/packages/common/business_components/src/ui/map/searchBar/procSearchBar.tsx index 21239bd7b..5fe3233e2 100644 --- a/packages/common/business_components/src/ui/map/searchBar/procSearchBar.tsx +++ b/packages/common/business_components/src/ui/map/searchBar/procSearchBar.tsx @@ -43,10 +43,12 @@ export const ProcSearchBar = ({ gisMaps, facilityTypes }: SearchBarProps) => { const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_ctpv&outputFormat=application/json&propertyName=mctpv_korn_nm,mctpv_cd`; const res = await fetch(url).then(res => res.json()); - const list = res.features.map((f: any) => ({ - text: f.properties.mctpv_korn_nm, - val: f.properties.mctpv_cd, - })); + const list = res.features + .map((f: any) => ({ + text: f.properties.mctpv_korn_nm, + val: f.properties.mctpv_cd, + })) + .sort((a, b) => a.text.localeCompare(b.text, 'ko')); setSidoData([{ text: "시도", val: "0" }, ...list]); }; @@ -54,10 +56,12 @@ export const ProcSearchBar = ({ gisMaps, facilityTypes }: SearchBarProps) => { const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_sgg&outputFormat=application/json&propertyName=sgg_cd,sgg_korn_nm`; const res = await fetch(url).then(res => res.json()); - const list = res.features.map((f: any) => ({ - text: f.properties.sgg_korn_nm, - val: f.properties.sgg_cd, - })); + const list = res.features + .map((f: any) => ({ + text: f.properties.sgg_korn_nm, + val: f.properties.sgg_cd, + })) + .sort((a, b) => a.text.localeCompare(b.text, 'ko')); setSigunData(list); }; @@ -90,10 +94,12 @@ export const ProcSearchBar = ({ gisMaps, facilityTypes }: SearchBarProps) => { url = `${url}&typeName=hasudo:${type}&cql_filter=${cqlFilter}`; const res = await fetch(url); const json = await res.json(); - list = json.features.map((f: any) => ({ - text: f.properties.swtrmt_fclt_nm ?? "(이름없음)", - val: f.id, - })); + list = json.features + .map((f: any) => ({ + text: f.properties.swtrmt_fclt_nm ?? "(이름없음)", + val: f.id, + })) + .sort((a, b) => a.text.localeCompare(b.text, 'ko')); } else if ( type === "tb_wr_bdst_ruse_fclt" // 재이용시설 ) { @@ -144,7 +150,9 @@ export const ProcSearchBar = ({ gisMaps, facilityTypes }: SearchBarProps) => { })); } - setFacilityData([{ text: "시도", val: "0" }, ...list]); + // 가나다순 정렬 후 set + const sorted = list.slice().sort((a, b) => a.text.localeCompare(b.text, 'ko')); + setFacilityData([{ text: "시도", val: "0" }, ...sorted]); setFacilityValue("0"); };