diff --git a/apps/cs/src/entities/link/model/link-interface.ts b/apps/cs/src/entities/link/model/link-interface.ts index 86960f195..f8cf82016 100644 --- a/apps/cs/src/entities/link/model/link-interface.ts +++ b/apps/cs/src/entities/link/model/link-interface.ts @@ -191,6 +191,7 @@ export interface LinkSearchParam extends PageableParams { startDate?: string; endDate?: string; isSucc?: string; + linkClfsCd?: string; } export type LinkFormData = z.infer; diff --git a/apps/cs/src/pages/link/ui/link-page.tsx b/apps/cs/src/pages/link/ui/link-page.tsx index 95a9cc043..9d21570b9 100644 --- a/apps/cs/src/pages/link/ui/link-page.tsx +++ b/apps/cs/src/pages/link/ui/link-page.tsx @@ -4,7 +4,7 @@ import "@common/assets/styles/grid.css"; import { useState } from "react"; import { useRouter } from "next/navigation"; -import { Button, Input, Label, LoadingWithAnimation } from "@common/components"; +import { Button, Input, Label, LoadingWithAnimation, Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from "@common/components"; import { getCoreRowModel } from "@tanstack/react-table"; import { LinkApi, type Link, type LinkSearchParam } from "@/entities/link"; import { useQuery } from "@tanstack/react-query"; @@ -18,10 +18,12 @@ function LinkPage() { const [linkSn, setLinkSn] = useState(''); const [linkNm, setLinkNm] = useState(''); + const [linkClfsCd, setLinkClfsCd] = useState(''); const [linkSearchParam, setLinkSearchParam] = useState({ linkSn: '', - linkNm: '' + linkNm: '', + linkClfsCd: '', }); const query = useQuery({ @@ -32,10 +34,12 @@ function LinkPage() { const handleResetSearchParam = () => { setLinkSn(''); setLinkNm(''); + setLinkClfsCd('ALL'); setLinkSearchParam({ ...linkSearchParam, linkSn: '', linkNm: '', + linkClfsCd: 'ALL', }); }; @@ -44,7 +48,8 @@ function LinkPage() { setLinkSearchParam({ ...linkSearchParam, linkSn, - linkNm + linkNm, + linkClfsCd, }); }; @@ -100,6 +105,24 @@ function LinkPage() { onChange={(e) => setLinkNm(e.target.value)} autoComplete="off" /> + + diff --git a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx index c007645bb..69514da41 100644 --- a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx +++ b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx @@ -10,7 +10,7 @@ import { UiModal, } from "@common/business_components"; import { Input } from "@common/components"; -import { Button, Label } from "@common/components/ui"; +import { Button, Label } from "@common/components/ui"; import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; import { AccessibleFormField } from "./AccessibleFormField"; @@ -208,22 +208,23 @@ export function SdmInfoRegForm({ formIndex: 0, required: true, component: (field) => ( -
+
{ field.onChange(date as Date); }} - className="w-full" - defaultMonth={field.value as Date} + className="" + //defaultMonth={field.value as Date} field={{ value: field.value as Date, onChange: (date: Date | undefined) => { field.onChange(date as Date); }, }} - selectClass={undefined} + showOutsideDays={true} + selectClass={undefined} />
), @@ -269,7 +270,7 @@ export function SdmInfoRegForm({ }, { name: "money", - label: "희망단가(원/m3)", + label: "희망단가(원)", formIndex: 0, required: true, component: (field) => ( @@ -363,7 +364,7 @@ export function SdmInfoRegForm({ name === "money" ? (
{ component(field)}
diff --git a/apps/dm/eslint.config.mjs b/apps/dm/eslint.config.mjs index 29e63f653..f477f55e1 100644 --- a/apps/dm/eslint.config.mjs +++ b/apps/dm/eslint.config.mjs @@ -4,6 +4,7 @@ import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const isProd = process.env.NODE_ENV === "production"; const compat = new FlatCompat({ baseDirectory: __dirname, @@ -14,6 +15,7 @@ const eslintConfig = [ {rules:{ // other rules.. "@typescript-eslint/no-explicit-any": "off", + ...(isProd ? { "react-hooks/exhaustive-deps": "off" } : {}), }}, ]; diff --git a/apps/fo/app/components/common/DataForm.tsx b/apps/fo/app/components/common/DataForm.tsx index 66530aed9..c478826f0 100644 --- a/apps/fo/app/components/common/DataForm.tsx +++ b/apps/fo/app/components/common/DataForm.tsx @@ -4,6 +4,26 @@ item: Record; } +export const roundNumber = (value : any, decimals: number) => { + if (typeof value === 'number') { + return addCommas((Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals)).toFixed(decimals)); + } else if (typeof value === 'string' && !isNaN(Number(value))) { + return addCommas(Number(value).toFixed(decimals)); + } +} + +export const addCommas = (value: string | number): any => { + if (typeof value === 'number') { + return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",").split('.').map((part, index) => + index === 0 ? part : part.replace(/(\d{3})/g, '$1,') + ).join('.'); + } else if (typeof value === 'string') { + return value.includes('.')?value.split('.').map((part, index) => + index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part + ).join('.'):String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + } +} + export default function DataForm({ type, column, item }: DataFormProps) { if(item) { const value = item[column]; @@ -16,31 +36,30 @@ export default function DataForm({ type, column, item }: DataFormProps) { if(value && value != '') { item[column] = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } - break; - case 'float1': + break; + case 'float0': if(value) { - item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 10) / 10).split('.').map((part, index) => + item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1) / 1).split('.').map((part, index) => index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part ).join('.') : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); } break; + case 'float1': + if(value) { + item[column] = roundNumber(value, 1); + } + break; case 'float2': if(value) { - item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 100) / 100).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + item[column] = roundNumber(value, 2); } break; case 'float3': if(value) { - item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1000) / 1000).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + item[column] = roundNumber(value, 3); } break; } } -} +} \ No newline at end of file diff --git a/apps/fo/app/components/common/SearchInput.tsx b/apps/fo/app/components/common/SearchInput.tsx index 11bdd3676..c15ab7506 100644 --- a/apps/fo/app/components/common/SearchInput.tsx +++ b/apps/fo/app/components/common/SearchInput.tsx @@ -47,11 +47,6 @@ export default function SearchInputText({label=undefined, setValue=()=>{}, sidoS }, [fcltyName]); const handleSuggestionClick = useCallback((bscFctCd: string, bscFctNm: string, fctCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '') => { - console.log('bscFctNm : ', bscFctNm); - console.log('bscFctCd : ', bscFctCd); - console.log('fctCpc : ', fctCpc); - console.log('trtFrfNm : ', trtFrfNm); - console.log('efflWqltStndrdNm : ', efflWqltStndrdNm); setTextValue(bscFctNm); setValue(bscFctCd, bscFctNm, fctCpc, trtFrfNm, efflWqltStndrdNm); setIsOpen(false); diff --git a/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx index 9c16161f5..85aea5ea6 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx @@ -1,11 +1,12 @@ "use client" +import { roundNumber } from "@/app/components/common/DataForm"; import { useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/processFacility"; import { ContentsDiv, UiModal, UiTable } from "@common/business_components"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; import { useEffect, useState } from "react"; import DashBoardChartModal from "./modal/page"; -import LoadingOverlay from "@common/business_components/ui/loadingBar"; export type facilityInfoProps = { searchYear?: string; @@ -171,7 +172,6 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}: el['provisionalApproval'] = waterUsage1; } }); - console.log('planFcltyList :', planFcltyList); } }, [waterUsage1, waterUsage2, waterUsage3, searchYear, planFcltyList]); // 연도가 바뀌면 hasSearched를 false로 리셋 @@ -225,7 +225,7 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}: :(index < 5 || index > 5)&& (index!= 4) && (head.id ==='provisionalApproval'||head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') ?{el[head.id]} :(head.id === "provisionalApproval" && index === 4) - ?{Math.round((Number(waterUsage1)+Number(waterUsage2)+Number(waterUsage3))/3 * 100)/100} + ?{roundNumber(String((Number(waterUsage1)+Number(waterUsage2)+Number(waterUsage3))/3), 0)} :((head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') ) ?null :(head.id === 'item' && index != 5)? diff --git a/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx b/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx index ead9b56fd..5a8e77dcd 100644 --- a/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx @@ -148,8 +148,8 @@ export default function PrecipitationModal(props: PrecipitationModalProps) { return ( <> - -
+ +
@@ -186,7 +186,7 @@ export default function PrecipitationModal(props: PrecipitationModalProps) {
- + + ref={ref} isAdmin={false}/>
@@ -240,7 +240,7 @@ export default function FacilityPrecipitation() { {selectedRow !== null && ( } - bodyClass="sm:max-w-[90vw] w-[72vw] h-[40vw]" + bodyClass="sm:max-w-[90vw] w-[1150px] h-[700px]" open={selectedRow !== null} onOpenChange={(open) => !open && closeModal()} noHeaderYn="Y" diff --git a/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx b/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx index 92e81f61d..0685f93d6 100644 --- a/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx @@ -6,6 +6,7 @@ import { downloadExcel, UITab, UiTable } from "@common/business_components/ui"; import { Button, TabsContent } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import type { ReturnType } from "../../../../components/common/FacilityTermModalHeader"; +import { roundUp } from "@/app/store/common"; const tabList = [ { value: 'tab_term_year', label: '연간' }, @@ -74,6 +75,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st let avgefflAmnt = 0; let avginfoRnDay = 0; + console.log('filteredData', filteredData); filteredData.forEach((item: FlowRateModalInterface) => { const totInflAmnt = parseNumber(item.totInflAmnt); const inflAmnt = parseNumber(item.inflAmnt); @@ -106,32 +108,32 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st const newFilteredData = [ { msrDt: '평균', - totInflAmnt: Math.round(avgtotInflAmnt / filteredData.length * 10) / 10, - inflAmnt: Math.round(avginflAmnt / filteredData.length * 10) / 10, - sumFlow: Math.round(avgsumFlow / filteredData.length * 10) / 10, - returnAmnt: Math.round(avgreturnAmnt / filteredData.length * 10) / 10, - efflAmnt: Math.round(avgefflAmnt / filteredData.length * 10) / 10, - infoRnDay: avginfoRnDay > 0 ? Math.round(avginfoRnDay / filteredData.length * 10) / 10 : 0, + totInflAmnt: roundUp((avgtotInflAmnt / filteredData.length), 0), + inflAmnt: roundUp((avginflAmnt / filteredData.length), 0), + sumFlow: roundUp((avgsumFlow / filteredData.length), 0), + returnAmnt: roundUp((avgreturnAmnt / filteredData.length), 0), + efflAmnt: roundUp((avgefflAmnt / filteredData.length), 0), + infoRnDay: avginfoRnDay > 0 ? roundUp((avginfoRnDay / filteredData.length), 1) : '0.0', rainEffectDay: 0, }, { msrDt: '최대', - totInflAmnt: Math.round(maxtotInflAmnt * 10) / 10, - inflAmnt: Math.round(maxinflAmnt * 10) / 10, - sumFlow: Math.round(maxsumFlow * 10) / 10, - returnAmnt: Math.round(maxreturnAmnt * 10) / 10, - efflAmnt: Math.round(maxefflAmnt * 10) / 10, - infoRnDay: maxinfoRnDay > 0 ? (Math.round(maxinfoRnDay * 10) / 10).toFixed(1) : '0.0', + totInflAmnt: roundUp(maxtotInflAmnt, 0), + inflAmnt: roundUp(maxinflAmnt, 0), + sumFlow: roundUp(maxsumFlow, 0), + returnAmnt: roundUp(maxreturnAmnt, 0), + efflAmnt: roundUp(maxefflAmnt, 0), + infoRnDay: maxinfoRnDay > 0 ? roundUp(maxinfoRnDay, 1) : '0.0', rainEffectDay: 0, }, { msrDt: '최소', - totInflAmnt: Math.round(mintotInflAmnt * 10) / 10, - inflAmnt: Math.round(mininflAmnt * 10) / 10, - sumFlow: Math.round(minsumFlow * 10) / 10, - returnAmnt: Math.round(minreturnAmnt * 10) / 10, - efflAmnt: Math.round(minefflAmnt * 10) / 10, - infoRnDay: mininfoRnDay > 0 ? (Math.round(mininfoRnDay * 10) / 10).toFixed(1) : '0.0', + totInflAmnt: roundUp(mintotInflAmnt, 0), + inflAmnt: roundUp(mininflAmnt, 0), + sumFlow: roundUp(minsumFlow, 0), + returnAmnt: roundUp(minreturnAmnt, 0), + efflAmnt: roundUp(minefflAmnt, 0), + infoRnDay: mininfoRnDay > 0 ? roundUp(mininfoRnDay, 1) : '0.0', rainEffectDay: 0, }, ...filteredData diff --git a/apps/fo/app/pages/(with-layout)/flowRate/page.tsx b/apps/fo/app/pages/(with-layout)/flowRate/page.tsx index fdb0db487..bec068858 100644 --- a/apps/fo/app/pages/(with-layout)/flowRate/page.tsx +++ b/apps/fo/app/pages/(with-layout)/flowRate/page.tsx @@ -130,7 +130,7 @@ export default function ProcessFacilitySearch() { initializeCommonData(); searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0"); } - }, [isInit, initializeCommonData, searchFlowRateList]); + }, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear]); useEffect(() => { if (excelList && excelList.length > 0) { @@ -187,7 +187,7 @@ export default function ProcessFacilitySearch() { queryClick={chkVal} excelClick={excelDownload} initClick={resetVal} - ref={ref} isAdmin={false} /> + ref={ref} isAdmin={false}/>
diff --git a/apps/fo/app/pages/(with-layout)/processFacility/page.tsx b/apps/fo/app/pages/(with-layout)/processFacility/page.tsx index 109b64562..381c64ea5 100644 --- a/apps/fo/app/pages/(with-layout)/processFacility/page.tsx +++ b/apps/fo/app/pages/(with-layout)/processFacility/page.tsx @@ -24,7 +24,6 @@ export default function ProcessFacilitySearch() { isInit, selectUpdownData, selectOperationData, - selectFacilityPartData, selectSidoData, selectSigunData, selectRegionData, @@ -249,7 +248,6 @@ export default function ProcessFacilitySearch() { setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> - setFacilityPartValue(val.toString())} value={facilityPartValue} selectClass={undefined} /> } rightChildren={undefined} selectSidoData={selectSidoData} diff --git a/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx b/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx index 1e37569c6..79c35b251 100644 --- a/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx @@ -7,8 +7,9 @@ import { Table, TableCell, TableHead, TableRow, TabsContent } from "@common/comp import { useEffect, useState } from "react"; type ModalProps ={ - fcltyName: string; - searchYear : string; + fcltyCd?: string; + fcltyName?: string; + searchYear? : string; } const tabList1 = [ @@ -17,7 +18,7 @@ const tabList1 = [ { value: 'tab_term_winter', label: '동절기' }, ]; -export default function UnderWaterInputModal({fcltyName='', searchYear=new Date().getFullYear().toString()} : ModalProps) { +export default function UnderWaterInputModal({fcltyCd='', fcltyName='',searchYear=new Date().getFullYear().toString()} : ModalProps) { // 그리드 관련 const {UnderWaterDetail, unserWaterModal} = useFoStore((state) => state); @@ -34,64 +35,11 @@ export default function UnderWaterInputModal({fcltyName='', searchYear=new Date( gridListUnderWaterDetail, } = UnderWaterDetail; - // 필터링 된 후 최대, 최소, 평균 값 계산 - const setFilterAfterData = (filteredData: UnderWaterInputModalProps[]) => { - let maxRealSewageInput = Number(filteredData[0]?.realSewageInput ?? 0); - let maxSewageOccur = Number(filteredData[0]?.sewageOccur ?? 0); - let maxUnderWaterInput = Number(filteredData[0]?.underWaterInput ?? 0); - - let minRealSewageInput = Number(filteredData[0]?.realSewageInput ?? 0); - let minSewageOccur = Number(filteredData[0]?.sewageOccur ?? 0); - let minUnderWaterInput = Number(filteredData[0]?.underWaterInput ?? 0); - - let avgRealSewageInput = 0; - let avgSewageOccur = 0; - let avgUnderWaterInput = 0; - - filteredData.forEach((item: UnderWaterInputModalProps) => { - maxRealSewageInput = Math.max(Number(maxRealSewageInput), Number(item.realSewageInput)); - maxSewageOccur = Math.max(Number(maxSewageOccur), Number(item.sewageOccur)); - maxUnderWaterInput = Math.max(Number(maxUnderWaterInput), Number(item.underWaterInput)); - - minRealSewageInput = Math.min(Number(minRealSewageInput) || 0, Number(item.realSewageInput)); - minSewageOccur = Math.min(Number(minSewageOccur) || 0, Number(item.sewageOccur)); - minUnderWaterInput = Math.min(Number(minUnderWaterInput) || 0, Number(item.underWaterInput)); - - avgRealSewageInput += Number(item.realSewageInput ?? 0); - avgSewageOccur += Number(item.sewageOccur ?? 0); - avgUnderWaterInput += Number(item.underWaterInput ?? 0); - }) - - const newFilteredData = [ - { - msrDt: '평균', - realSewageInput: filteredData.length==0?0:Math.round(Number(avgRealSewageInput) / filteredData.length * 10) / 10, - sewageOccur: filteredData.length==0?0:Math.round(Number(avgSewageOccur) / filteredData.length * 1000) / 1000 , - underWaterInput: filteredData.length==0?0:Math.round(Number(avgUnderWaterInput) / filteredData.length * 1000) / 1000, - }, - { - msrDt : '최대', - realSewageInput : Math.round(maxRealSewageInput * 10) / 10, - sewageOccur : Math.round(maxSewageOccur * 1000) / 1000 , - underWaterInput : Math.round(maxUnderWaterInput * 1000) / 1000, - }, - { - msrDt : '최소', - realSewageInput : Math.round(minRealSewageInput * 10) / 10, - sewageOccur : Math.round(minSewageOccur * 1000) / 1000 , - underWaterInput : Math.round(minUnderWaterInput * 1000) / 1000, - }, - ...filteredData - ]; - return newFilteredData; - } - // 날짜 필터링 관련 상태 const [copyYearList, setCopyYearList] = useState([]); useEffect(() => { - const filteredYearData = setFilterAfterData(gridListUnderWaterDetail); - setCopyYearList(filteredYearData as UnderWaterInputModalProps[]); + setCopyYearList(gridListUnderWaterDetail); }, [gridListUnderWaterDetail]); useEffect(() => { @@ -115,9 +63,11 @@ export default function UnderWaterInputModal({fcltyName='', searchYear=new Date( setPrevEndYmDate(searchYear+'12'); } }, [searchYear, tabTerm]); + useEffect(()=>{ - unserWaterModal(fcltyName, searchYear); - }, []) + unserWaterModal(fcltyCd, searchYear, searchYear+'01', searchYear+'12'); + }, [fcltyCd, searchYear, unserWaterModal]); + useEffect(() => { if(endYmDate < startYmDate) { alert('종료일이 시작일보다 작습니다.') @@ -130,16 +80,13 @@ export default function UnderWaterInputModal({fcltyName='', searchYear=new Date( // 유효한 경우 현재 값을 이전 값으로 저장 setPrevStartYmDate(startYmDate); setPrevEndYmDate(endYmDate); - - const filteredData = gridListUnderWaterDetail.filter((item: UnderWaterInputModalProps) => { - // msrDt가 "2024-01" 형태이므로 "202401" 형태로 변환 - const itemDate = item.msrDt.replace('-', ''); - return itemDate >= startYmDate && itemDate <= endYmDate; - }); - const filteredYearData = setFilterAfterData(filteredData); - setCopyYearList(filteredYearData as UnderWaterInputModalProps[]); + }, [startYmDate, endYmDate, gridListUnderWaterDetail, prevStartYmDate, prevEndYmDate]); + useEffect(() => { + unserWaterModal(fcltyCd, searchYear, prevStartYmDate, prevEndYmDate); + } , [fcltyCd, searchYear, unserWaterModal, prevStartYmDate, prevEndYmDate]); + return ( <> {setTabTerm(val)}}> diff --git a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx index 8debdb15e..e731fb7a3 100644 --- a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx +++ b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx @@ -66,11 +66,11 @@ export default function UnderWaterInput() { // 모달 상태 추가 const [selectedRow, setSelectedRow] = useState(null); + const [fcltyCd, setFcltyCd] = useState(""); const [fcltyNm, setFcltyNm] = useState(""); // 그리드 관련 const searchInputRef = useRef(null); - // 초기화 변수 const [resetPage, setResetPage] = useState(false); // 표 너비 @@ -116,7 +116,8 @@ export default function UnderWaterInput() { // 셀 클릭 시 모달 표시 const cellClick = (index: number) => { if (underWater[index]) { - setFcltyNm(underWater[index].bscFctCd); + setFcltyCd(underWater[index].bscFctCd || ""); + setFcltyNm(underWater[index].bscFctNm || ""); setSelectedRow(index); // 클릭한 행의 index 저장 } }; @@ -129,7 +130,7 @@ export default function UnderWaterInput() { initializeCommonData(); searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); } - }, [isInit, initializeCommonData, searchUnderWaterList]); + }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]); useEffect(() => { if (excelList && excelList.length > 0) { @@ -227,7 +228,7 @@ export default function UnderWaterInput() { {selectedRow !== null && ( } + body={} bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]" open={selectedRow !== null} onOpenChange={(open) => !open && closeModal()} diff --git a/apps/fo/app/store/common/index.tsx b/apps/fo/app/store/common/index.tsx index b68fd9aea..5646ea126 100644 --- a/apps/fo/app/store/common/index.tsx +++ b/apps/fo/app/store/common/index.tsx @@ -131,7 +131,7 @@ const selectEnvPartSigunData: SelectDataType[] = [ // 상향 하향 데이터 const selectUpdownData:SelectDataType[] = [ { text: "전체", val: '0' }, - { text: "500이하", val: '400' }, + { text: "500미만", val: '400' }, { text: "500이상", val: '500' }, ]; @@ -201,6 +201,16 @@ const selectMiddleRegionData: SelectDataType[] = [ { text: "전체", val: '0' } ]; +/** + * 반올림 함수 + * @param value : 대상 숫자 + * @param precision : 반올림할 자릿수 + * @returns + */ +export const roundUp = (value: number, precision: number = 1): number => { + return Math.round(value * (10 ** precision)) / (10 ** precision); +} + /** * STORE - API */ @@ -226,7 +236,6 @@ export const fetchPlanInfo = async (targetYear : string | undefined, ctpvNm : st } } - // 시도 API 호출 함수 추가 const fetchSidoData = async (): Promise => { try { diff --git a/apps/fo/app/store/dashboard/index.tsx b/apps/fo/app/store/dashboard/index.tsx index b23006c10..8ad56e070 100644 --- a/apps/fo/app/store/dashboard/index.tsx +++ b/apps/fo/app/store/dashboard/index.tsx @@ -4,7 +4,7 @@ import { getFetch } from "@/app/components/api"; import DataForm from "@/app/components/common/DataForm"; -import { fetchPlanInfo } from "../common"; +import { fetchPlanInfo, roundUp } from "../common"; import type { FacilityWaterQltyProps } from "../facilityWaterQlty"; import { calRainEffectDay, type ApiResponse, type FlowDataItem } from "../underWaterInput"; @@ -152,21 +152,21 @@ export interface RainManageExcelType { 강우량: string, 강우조건비율: number | '', 일수: number | '', - 유입유량: number | '', - 시설용량대비: number | '', - 설계유입수질BOD: number | '', - 설계유입수질TOC: number | '', - 설계유입수질SS: number | '', + 유입유량: string | number | '', + 시설용량대비: string |number | '', + 설계유입수질BOD: string | number | '', + 설계유입수질TOC: string | number | '', + 설계유입수질SS: string | number | '', 설계유입수질TN: string | number | '', 설계유입수질TP: string | number | '', - 유입수질BOD: number | '', - 유입수질TOC: number | '', - 유입수질SS: number | '', + 유입수질BOD: string | number | '', + 유입수질TOC: string | number | '', + 유입수질SS: string | number | '', 유입수질TN: string | number | '', 유입수질TP: string | number | '', - 방류수질BOD: number | '', - 방류수질TOC: number | '', - 방류수질SS: number | '', + 방류수질BOD: string | number | '', + 방류수질TOC: string |number | '', + 방류수질SS: string | number | '', 방류수질TN: string | number | '', 방류수질TP: string | number | '', } @@ -355,29 +355,29 @@ const upHeadListOperationStatus : TableUpperProps[] = [ const upHeadListRainManage : TableUpperProps[] = [ { id: "rainItem", title: "강우량", row:0, rowSpan:2 }, - { id: "rainRate", title: "강우조건\n비율(%)", row:0, rowSpan:2 }, - { id: "rainDay", title: "일수", row:0, rowSpan:2 }, + { id: "rainRate", title: "강우조건\n비율(%)", row:0, rowSpan:2, align: "right" }, + { id: "rainDay", title: "일수", row:0, rowSpan:2, align: "right" }, { id: "A", title: "유입(㎥/일)", row:0, colSpan:2 }, - { id: "inflAmnt", title: "유입유량", row:1 }, - { id: "fctCpc", title: "시설용량대비", row:1 }, + { id: "inflAmnt", title: "유입유량", row:1, align: "right" }, + { id: "fctCpc", title: "시설용량대비", row:1, align: "right" }, { id: "B", title: "설계유입수질(㎎/L)", row:0, colSpan:5 }, - { id: "dsInflWqltBod", title: "BOD", row:1 }, - { id: "dsInflWqltCod", title: "TOC\n(COD)", row:1 }, - { id: "dsInflWqltSs", title: "SS", row:1 }, - { id: "dsInflWqltTn", title: "T-N", row:1 }, - { id: "dsInflWqltTp", title: "T-P", row:1 }, + { id: "dsInflWqltBod", title: "BOD", row:1, align: "right" }, + { id: "dsInflWqltCod", title: "TOC\n(COD)", row:1, align: "right" }, + { id: "dsInflWqltSs", title: "SS", row:1, align: "right" }, + { id: "dsInflWqltTn", title: "T-N", row:1, align: "right" }, + { id: "dsInflWqltTp", title: "T-P", row:1, align: "right" }, { id: "C", title: "유입수질(㎎/L)", row:0, colSpan:5 }, - { id: "inflAssctBfrWqltBod", title: "BOD", row:1 }, - { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)", row:1 }, - { id: "inflAssctBfrWqltSs", title: "SS", row:1 }, - { id: "inflAssctBfrWqltTn", title: "T-N", row:1 }, - { id: "inflAssctBfrWqltTp", title: "T-P", row:1 }, + { id: "inflAssctBfrWqltBod", title: "BOD", row:1, align: "right" }, + { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)", row:1, align: "right" }, + { id: "inflAssctBfrWqltSs", title: "SS", row:1, align: "right" }, + { id: "inflAssctBfrWqltTn", title: "T-N", row:1, align: "right" }, + { id: "inflAssctBfrWqltTp", title: "T-P", row:1, align: "right" }, { id: "D", title: "방류수질(㎎/L)", row:0, colSpan:5 }, - { id: "efflWqltBod", title: "BOD", row:1 }, - { id: "efflWqltToc", title: "TOC\n(COD)", row:1 }, - { id: "efflWqltSs", title: "SS", row:1 }, - { id: "efflWqltTn", title: "T-N", row:1 }, - { id: "efflWqltTp", title: "T-P", row:1 }, + { id: "efflWqltBod", title: "BOD", row:1, align: "right" }, + { id: "efflWqltToc", title: "TOC\n(COD)", row:1, align: "right" }, + { id: "efflWqltSs", title: "SS", row:1, align: "right" }, + { id: "efflWqltTn", title: "T-N", row:1, align: "right" }, + { id: "efflWqltTp", title: "T-P", row:1, align: "right" }, ] const upHeadListDrainageEquipment : TableUpperProps[] = [ @@ -401,7 +401,7 @@ const upHeadListDrainageEquipment : TableUpperProps[] = [ { id: "etc", title: "비고", row:0, upName:"upChangeRe7", upSequnce:1 }, ] -const operationalStatusList : OperationalStatusListProps[] = [ +let operationalStatusList : OperationalStatusListProps[] = [ { flowRate : '유량\n(㎥/일)', waterQlty : '유입', @@ -872,30 +872,49 @@ export const dashBoardInitState:DashBoardType = { /** * 데이터 폼 포맷설정 */ -export const dataFormSettingPlan=(item : planFcltyListProps) => { - DataForm({ type: 'float2', column: 'provisionalApproval', item: item as unknown as Record }); - DataForm({ type: 'float2', column: 'now', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'stepOne', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'stepTwo', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'stepThree', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'stepFour', item: item as unknown as Record}); +export const dataFormSettingPlan=(item : planFcltyListProps, decmials : number) => { + DataForm({ type: 'float'+decmials, column: 'provisionalApproval', item: item as unknown as Record }); + DataForm({ type: 'float'+decmials, column: 'now', item: item as unknown as Record}); + DataForm({ type: 'float'+decmials, column: 'stepOne', item: item as unknown as Record}); + DataForm({ type: 'float'+decmials, column: 'stepTwo', item: item as unknown as Record}); + DataForm({ type: 'float'+decmials, column: 'stepThree', item: item as unknown as Record}); + DataForm({ type: 'float'+decmials, column: 'stepFour', item: item as unknown as Record}); } export const dataFormSetting=(item : OperationalStatusListProps)=> { - DataForm({ type: 'float2', column: 'yearAvg', item: item as unknown as Record }); - DataForm({ type: 'float2', column: 'yearMax', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'yearMin', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'summerAll', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'summerThree', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'summerRain', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'winterAll', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'winterSunnyAvg', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'winterSunnyMax', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'winterSunnyMin', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'groundWaterAvg', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'groundWaterSummer', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'groundWaterWinter', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'rainRate', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'yearAvg', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'yearMax', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'yearMin', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'summerAll', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'summerThree', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'summerRain', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'winterAll', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'winterSunnyAvg', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'winterSunnyMax', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'winterSunnyMin', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'groundWaterAvg', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'groundWaterSummer', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'groundWaterWinter', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'rainRate', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'rdi', item: item as unknown as Record}); +}; + +export const dataFormSettingWqlty=(item : OperationalStatusListProps)=> { + DataForm({ type: 'float1', column: 'yearAvg', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'yearMax', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'yearMin', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'summerAll', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'summerThree', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'summerRain', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'winterAll', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'winterSunnyAvg', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'winterSunnyMax', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'winterSunnyMin', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'groundWaterAvg', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'groundWaterSummer', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'groundWaterWinter', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'rainRate', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'rdi', item: item as unknown as Record}); }; const changeValue=(operationalStatus, data, keyTxt = 'INFL_AMNT')=>{ @@ -921,7 +940,7 @@ const changeValue=(operationalStatus, data, keyTxt = 'INFL_AMNT')=>{ const winterAvgBod = data.winterSunny && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD) : 0; const summerLowBod = data.summerBod && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD) : 0; const summerLowRain = data.summerBod && data.summerBod.INFO_RN_DAY && data.summerBod.INFO_RN_DAY != 'null' ? Number(data.summerBod.INFO_RN_DAY) : 0; - operationalStatus.rdi = winterAvgBod === 0 ? 0 : summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatus.rdi = winterAvgBod === 0 ? 0 : roundUp(summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod), 1); operationalStatus.rdiRain = summerLowRain; } @@ -1012,12 +1031,12 @@ export const calRainEffectDayRainManage = (data: RainManageListProps[]) => { for(let idx = 0; idx < data.length; idx++) { if(data[idx] && data[idx]?.rainEffectDay === 1) { if(idx > 1){ - if(data[idx-1]?.infoRnDay && Number(data[idx-1]?.infoRnDay) >= 3) { + if(data[idx-1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-1]?.infoRnDay) >= 3) { data[idx]!.infoRnDay = data[idx-1]!.infoRnDay; } } if(idx > 2){ - if(data[idx-2]?.infoRnDay && Number(data[idx-2]?.infoRnDay) >= 3) { + if(data[idx-2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-2]?.infoRnDay) >= 3) { data[idx]!.infoRnDay = data[idx-2]!.infoRnDay; } } @@ -1068,21 +1087,21 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) let minDiff=9999999; let minIndex; res.forEach((el:any, idx) => { - if(minDiff>Math.abs(Number(targetYear) - Number(el.yr))){ - minDiff=Math.abs(Number(targetYear) - Number(el.yr)); + if(minDiff>Math.abs(Number(targetYear) - Number(el.planYr))){ + minDiff=Math.abs(Number(targetYear) - Number(el.planYr)); minIndex=idx; } }); const rtnPlanData = res[minIndex]; - planFcltyList.map((el, index) => { planYearList = []; - planYearList.push(rtnPlanData?rtnPlanData.yrDt4.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.yrDc1.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.yrDc2.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.yrDc3.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.yrDc4.toString():""); + planYearList.push(rtnPlanData?rtnPlanData.planYrDt4.toString():""); + planYearList.push(rtnPlanData?rtnPlanData.planYr.toString():""); + planYearList.push(rtnPlanData?rtnPlanData.planYrDc1.toString():""); + planYearList.push(rtnPlanData?rtnPlanData.planYrDc2.toString():""); + planYearList.push(rtnPlanData?rtnPlanData.planYrDc3.toString():""); + planYearList.push(rtnPlanData?rtnPlanData.planYrDc4.toString():""); if(index === 0){ el.provisionalApproval = rtnPlanData?rtnPlanData.planSwageQntSumDt4:''; @@ -1111,17 +1130,22 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) if(index === 4){ el.provisionalApproval = rtnPlanData?rtnPlanData.ugwtUseSum:''; } + + if(index == 0 || index === 2){ + dataFormSettingPlan(el, 1); + }else if(index == 4){ + }else{ + dataFormSettingPlan(el, 0); + } + - // 임시시 - dataFormSettingPlan(el); - console.log('planFcltyList', planFcltyList); - console.log('rtnPlanData', rtnPlanData); set((state: DashBoardType) => ({ ...state, PlanFclty: { ...state.PlanFclty, planFcltyList: planFcltyList, isLoadingPlanFclty: false, + planYearList: planYearList, }, })); }); @@ -1139,6 +1163,17 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const res = await fetchOperationStatus(searchYear, fcltyCd); const data = res.data; + let yearMaxSumFlow = 1; + let yearMinSumFlow = 1; + let yearAvgSumFlow = 1; + let summerAll = 1; + let summerThree = 1; + let summerRain = 1; + let winterAll = 1; + let winterSunnyMin = 1; + let winterSunnyAvg = 1; + let winterSunnyMax = 1; + if(data && operationalStatusList){ // 유량 - 유입 if (operationalStatusList?.[0]) { @@ -1146,6 +1181,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const yearAvg = data.yearData===null?'':(data.yearData.AVG_INFL_AMNT?data.yearData.AVG_INFL_AMNT:0)+(data.yearData.AVG_SUM_FLOW?data.yearData.AVG_SUM_FLOW:0)+(data.yearData.AVG_RETURN_AMNT?data.yearData.AVG_RETURN_AMNT:0); const yearMax = data.yearData===null?'':(data.yearData.MAX_INFL_AMNT?data.yearData.MAX_INFL_AMNT:0)+(data.yearData.MAX_SUM_FLOW?data.yearData.MAX_SUM_FLOW:0)+(data.yearData.MAX_RETURN_AMNT?data.yearData.MAX_RETURN_AMNT:0); const yearMin = data.yearData===null?'':(data.yearData.MIN_INFL_AMNT?data.yearData.MIN_INFL_AMNT:0)+(data.yearData.MIN_SUM_FLOW?data.yearData.MIN_SUM_FLOW:0)+(data.yearData.MIN_RETURN_AMNT?data.yearData.MIN_RETURN_AMNT:0); + operationalStatusList[0].yearAvg = yearAvg; operationalStatusList[0].yearMax = yearMax; operationalStatusList[0].yearMin = yearMin; @@ -1166,52 +1202,130 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) (data.summerSunny.AVG_TP_INFL_WQLT_TP ? data.summerSunny.AVG_TP_INFL_WQLT_TP : 0) ); // 겨울 - operationalStatusList[0].winterAll = data.winter === null ? '' : (data.winter.AVG_INFL_AMNT ? data.winter.AVG_INFL_AMNT : 0) + (data.winter.AVG_SUM_FLOW ? data.winter.AVG_SUM_FLOW : 0) + (data.winter.AVG_TP_INFL_WQLT_TP ? data.winter.AVG_TP_INFL_WQLT_TP : 0); - operationalStatusList[0].winterSunnyMin = data.winterSunny === null ? '' : (data.winterSunny.MIN_INFL_AMNT ? data.winterSunny.MIN_INFL_AMNT : 0) + (data.winterSunny.MIN_SUM_FLOW ? data.winterSunny.MIN_SUM_FLOW : 0) + (data.winterSunny.MIN_TP_INFL_WQLT_TP ? data.winterSunny.MIN_TP_INFL_WQLT_TP : 0); - operationalStatusList[0].winterSunnyAvg = data.winterSunny === null ? '' : (data.winterSunny.AVG_INFL_AMNT ? data.winterSunny.AVG_INFL_AMNT : 0) + (data.winterSunny.AVG_SUM_FLOW ? data.winterSunny.AVG_SUM_FLOW : 0) + (data.winterSunny.AVG_TP_INFL_WQLT_TP ? data.winterSunny.AVG_TP_INFL_WQLT_TP : 0); - operationalStatusList[0].winterSunnyMax = data.winterSunny === null ? '' : (data.winterSunny.MAX_INFL_AMNT ? data.winterSunny.MAX_INFL_AMNT : 0) + (data.winterSunny.MAX_SUM_FLOW ? data.winterSunny.MAX_SUM_FLOW : 0) + (data.winterSunny.MAX_TP_INFL_WQLT_TP ? data.winterSunny.MAX_TP_INFL_WQLT_TP : 0); - + operationalStatusList[0].winterAll = data.winter === null ? '' : ( + (data.winter.AVG_INFL_AMNT ? data.winter.AVG_INFL_AMNT : 0) + + (data.winter.AVG_SUM_FLOW ? data.winter.AVG_SUM_FLOW : 0) + + (data.winter.AVG_TP_INFL_WQLT_TP ? data.winter.AVG_TP_INFL_WQLT_TP : 0) + ); + operationalStatusList[0].winterSunnyMin = data.winterSunny === null ? '' : ( + (data.winterSunny.MIN_INFL_AMNT ? data.winterSunny.MIN_INFL_AMNT : 0) + + (data.winterSunny.MIN_SUM_FLOW ? data.winterSunny.MIN_SUM_FLOW : 0) + + (data.winterSunny.MIN_TP_INFL_WQLT_TP ? data.winterSunny.MIN_TP_INFL_WQLT_TP : 0) + ); + operationalStatusList[0].winterSunnyAvg = data.winterSunny === null ? '' : ( + (data.winterSunny.AVG_INFL_AMNT ? data.winterSunny.AVG_INFL_AMNT : 0) + + (data.winterSunny.AVG_SUM_FLOW ? data.winterSunny.AVG_SUM_FLOW : 0) + + (data.winterSunny.AVG_TP_INFL_WQLT_TP ? data.winterSunny.AVG_TP_INFL_WQLT_TP : 0) + ); + operationalStatusList[0].winterSunnyMax = data.winterSunny === null ? '' : ( + (data.winterSunny.MAX_INFL_AMNT ? data.winterSunny.MAX_INFL_AMNT : 0) + + (data.winterSunny.MAX_SUM_FLOW ? data.winterSunny.MAX_SUM_FLOW : 0) + + (data.winterSunny.MAX_TP_INFL_WQLT_TP ? data.winterSunny.MAX_TP_INFL_WQLT_TP : 0) + ); + // 연계처리수 + let yearDay = 0; + let summerDay = 0; + let winterDay = 0; + let summerRainDay = 0; + let summerChungchunDay = 0; + let winterChungchuDay = 0; + + let yearSumFlow = 0; + let maxYearSumFlow = 0; + let minYearSumFlow = 9999999; + let summerSumFlow = 0; + let summerChungchunSumFlow = 0; + let summerRainDaySumFlow = 0; + let winterSumFlow = 0; + let winterChungchunSumFlow = 0; + let minWinterChungchunSumFlow = 9999999; + let maxWinterChungchunSumFlow = 0; + // 연계처리수,지하수유입량 const bscFctCd = fcltyCd; const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as ApiResponse; - - const calData = calRainEffectDay(res2.data, 'SUNNY'); - let chungcunSumFlow = 0; - - let sumSummerChungcunDay = 0; - let sumSummerChungcunFlow = 0; - - let sumWinterChungcunDay = 0; - let sumWinterChungcunFlow = 0; - - /* let yearSumFlow = 0; - let summerSumFlow = 0; - let winterSumFlow = 0; */ - - calData.forEach((flowItem: FlowDataItem) => { - chungcunSumFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - //yearSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - - if(flowItem.msrDt.substring(4,6) ==='06' || flowItem.msrDt.substring(4,6) ==='07' || flowItem.msrDt.substring(4,6) ==='08'){ - sumSummerChungcunDay += 1; - sumSummerChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - //summerSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - }else if(flowItem.msrDt.substring(4,6) ==='01' || flowItem.msrDt.substring(4,6) ==='02'){ - sumWinterChungcunDay += 1; - sumWinterChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - // winterSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; + yearDay = res2.data?res2.data.length:1; + res2.data.forEach((item: FlowDataItem) => { + yearSumFlow += item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + if(maxYearSumFlow < (item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0)) { + maxYearSumFlow = item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + } + if(minYearSumFlow > (item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0) || minYearSumFlow === 0) { + minYearSumFlow = item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + } + if(item.msrDt.substring(4,6) === '06' || item.msrDt.substring(4,6) === '07' || item.msrDt.substring(4,6) === '08') { + summerDay += 1; + summerSumFlow += item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + } else if(item.msrDt.substring(4,6) === '01' || item.msrDt.substring(4,6) === '02') { + winterDay += 1; + winterSumFlow += item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; } }); - + + const calData = calRainEffectDay(res2.data, 'SUNNY'); + const calRainDate = calRainEffectDay(res2.data, 'RAIN'); + + // 지하수유입량 - 청천시 기준 + let chungcunSumFlow = 0; + let sumSummerChungcunFlow = 0; + let sumWinterChungcunFlow = 0; + + // 하절기, 동절기 청천시 계산 + calData.forEach((flowItem: FlowDataItem) => { + chungcunSumFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; + + if(flowItem.msrDt.substring(4,6) ==='06' || flowItem.msrDt.substring(4,6) ==='07' || flowItem.msrDt.substring(4,6) ==='08'){ + summerChungchunDay += 1; + summerChungchunSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; + sumSummerChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; + }else if(flowItem.msrDt.substring(4,6) ==='01' || flowItem.msrDt.substring(4,6) ==='02'){ + if(minWinterChungchunSumFlow > (flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0) || minWinterChungchunSumFlow === 0) { + minWinterChungchunSumFlow = flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; + } + if(maxWinterChungchunSumFlow < (flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0)) { + maxWinterChungchunSumFlow = flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; + } + winterChungchuDay += 1; + winterChungchunSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; + sumWinterChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; + } + }); + + // 하절기 강우시 계산 + calRainDate.forEach((flowItem: FlowDataItem) => { + if(flowItem.msrDt.substring(4,6) === '06' || flowItem.msrDt.substring(4,6) === '07' || flowItem.msrDt.substring(4,6) === '08') { + summerRainDay += 1; + summerRainDaySumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; + } + }); + + operationalStatusList[0].yearAvg = roundUp(Number(operationalStatusList[0].yearAvg) + (yearSumFlow&&Number(yearSumFlow) != 0?Number(yearSumFlow / yearDay):0), 0); + operationalStatusList[0].yearMax = roundUp(Number(operationalStatusList[0].yearMax) + Number(maxYearSumFlow), 0); + operationalStatusList[0].yearMin = roundUp(Number(operationalStatusList[0].yearMin) + Number(minYearSumFlow), 0); + operationalStatusList[0].summerAll = roundUp(Number(operationalStatusList[0].summerAll) + (summerSumFlow&&Number(summerSumFlow)!=0?Number(summerSumFlow / summerDay):0), 0); + operationalStatusList[0].summerThree = roundUp(Number(operationalStatusList[0].summerThree) + (summerRainDaySumFlow&&Number(summerRainDaySumFlow)!=0?Number(summerRainDaySumFlow / summerRainDay):0), 0); + operationalStatusList[0].summerRain = roundUp(Number(operationalStatusList[0].summerRain) + (summerChungchunSumFlow&&Number(summerChungchunSumFlow)!=0?Number(summerChungchunSumFlow / summerChungchunDay):0), 0); + operationalStatusList[0].winterAll = roundUp(Number(operationalStatusList[0].winterAll) + (winterSumFlow&&Number(winterSumFlow)!=0?Number(winterSumFlow / winterDay):0), 0); + operationalStatusList[0].winterSunnyMin = roundUp(Number(operationalStatusList[0].winterSunnyMin) + Number(minWinterChungchunSumFlow), 0); + operationalStatusList[0].winterSunnyMax = roundUp(Number(operationalStatusList[0].winterSunnyMax) + (winterChungchunSumFlow&&Number(winterChungchunSumFlow)!=0?Number(winterChungchunSumFlow / winterChungchuDay):0), 0); + operationalStatusList[0].winterSunnyAvg = roundUp(Number(operationalStatusList[0].winterSunnyAvg) + Number(maxWinterChungchunSumFlow), 0); + // 연계처리수 - /* const avgYearSumFlow = yearSumFlow / calData.length; - const avgSummerSumFlow = summerSumFlow / calData.length; - const avgWinterSumFlow = winterSumFlow / calData.length; */ + yearAvgSumFlow = Number((yearSumFlow&&Number(yearSumFlow)!=0)?(yearSumFlow / yearDay):''); + yearMaxSumFlow = maxYearSumFlow; + yearMinSumFlow = minYearSumFlow; + summerAll = summerSumFlow&&Number(summerSumFlow)!=0?Number(summerSumFlow / summerDay):0 + summerThree= summerRainDaySumFlow&&Number(summerRainDaySumFlow)!=0?Number(summerRainDaySumFlow / summerRainDay):0; + summerRain = summerChungchunSumFlow&&Number(summerChungchunSumFlow)!=0?Number(summerChungchunSumFlow / summerChungchunDay):0; + winterAll = winterSumFlow&&Number(winterSumFlow)!=0?Number(winterSumFlow / winterDay):0; + winterSunnyMin = minWinterChungchunSumFlow; + winterSunnyAvg = winterChungchunSumFlow&&Number(winterChungchunSumFlow)!=0?(winterChungchunSumFlow / winterChungchuDay):0; + winterSunnyMax = maxWinterChungchunSumFlow; // 지하수유입량 const usage = chungcunSumFlow / calData.length; - const summerUsage = sumSummerChungcunFlow / sumSummerChungcunDay; - const winterUsage = sumWinterChungcunFlow / sumWinterChungcunDay; + const summerUsage = sumSummerChungcunFlow / summerChungchunDay; + const winterUsage = sumWinterChungcunFlow / winterChungchuDay; operationalStatusList[0].groundWaterAvg = Math.round((usage-(usage*0.65*0.8)) * 100) / 100; operationalStatusList[0].groundWaterSummer = Math.round((summerUsage-(summerUsage*0.65*0.8)) * 100) / 100; @@ -1227,19 +1341,19 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0; const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0; const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0; - operationalStatusList[0].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatusList[0].rdi = winterAvgBod===0?0:roundUp(summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod), 2); operationalStatusList[0].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[0]); } if(operationalStatusList[0] && operationalStatusList[7]){ - operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : data.yearData && data.yearData.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg) / (data.yearData && data.yearData.AVG_INFL_AMNT ? Number(data.yearData.AVG_INFL_AMNT) : 0) * 100) * 10) / 10+'%'; - operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : data.summer && data.summer.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer) / (data.summer && data.summer.AVG_INFL_AMNT ? Number(data.summer.AVG_INFL_AMNT) : 0) * 100) * 10) / 10+'%'; - operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : data.winter && data.winter.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter) / (data.winter && data.winter.AVG_INFL_AMNT ? Number(data.winter.AVG_INFL_AMNT) : 0) * 100) * 10) / 10+'%'; - operationalStatusList[7].rdi = operationalStatusList[0].rdi === null ? '' : data.summerBod && data.summerBod.INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].rdi) / (data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0) * 100) * 10) / 10+'%'; - /* operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : Number(Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT)) * 100; + operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : data.yearData && data.yearData.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg.toString().replaceAll(',', '')) / (data.yearData && data.yearData.AVG_INFL_AMNT ? Number(data.yearData.AVG_INFL_AMNT.toString().replaceAll(',','')) : 0) * 100) * 10) / 10+'%'; + operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : data.summer && data.summer.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer.toString().replaceAll(',','')) / (data.summer && data.summer.AVG_INFL_AMNT ? Number(data.summer.AVG_INFL_AMNT.toString().replaceAll(',','')) : 0) * 100) * 10) / 10+'%'; + operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : data.winter && data.winter.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter.toString().replaceAll(',','')) / (data.winter && data.winter.AVG_INFL_AMNT ? Number(data.winter.AVG_INFL_AMNT.toString().replaceAll(',','')) : 0) * 100) * 10) / 10+'%'; + operationalStatusList[7].rdi = operationalStatusList[0].rdi === null ? '' : data.summerBod && data.summerBod.INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].rdi.toString().replaceAll(',','')) / (data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT.toString().replaceAll(',','')):0) * 100) * 10) / 10+'%'; + /* operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : Number(Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT)) * 100; operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : Number(Number(operationalStatusList[0].groundWaterSummer) / Number(data.summer.AVG_INFL_AMNT)) * 100; operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : Number(Number(operationalStatusList[0].groundWaterWinter) / Number(data.winter.AVG_INFL_AMNT)) * 100;*/ - dataFormSetting(operationalStatusList[0]); } for(let i=1; i DashBoardType) if(operationalStatusList[i]){ if(i < 7){ changeValue(operationalStatusList[i], data, txtArray[i]); - dataFormSetting(operationalStatusList[i]!); + if(i < 5){ + dataFormSetting(operationalStatusList[i]!); + }else{ + dataFormSettingWqlty(operationalStatusList[i]!); + } + }else{ calcValue(operationalStatusList[i], data, txtArray[i]); } } } + + if(operationalStatusList[2]){ + operationalStatusList[2].yearAvg = yearAvgSumFlow; + operationalStatusList[2].yearMax = yearMaxSumFlow; + operationalStatusList[2].yearMin = yearMinSumFlow; + operationalStatusList[2].summerAll = summerAll; + operationalStatusList[2].summerThree = summerThree; + operationalStatusList[2].summerRain = summerRain; + operationalStatusList[2].winterAll = winterAll; + operationalStatusList[2].winterSunnyMin = winterSunnyMin; + operationalStatusList[2].winterSunnyAvg = winterSunnyAvg; + operationalStatusList[2].winterSunnyMax = winterSunnyMax; + dataFormSetting(operationalStatusList[2]); + } + } + + // + if(data.yearData.TP_INFL_WQLT_TP === null || data.yearData.TP_INFL_WQLT_TP === undefined || data.yearData.TP_INFL_WQLT_TP === 'null') { + operationalStatusList = operationalStatusList.slice(0, 10); } - //const excelDownList: RainManageExcelType[] = []; - /* if(isExcel){ - res.data.forEach(item => { - dataFormSetting(item) - item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; - excelDownList.push({ - 시설코드: item.bscFctCd, - 시설이름: item.bscFctNm, - 측정일자: item.msrDt, - 시도: item.sidoNm, - 시군구: item.sggNm, - 주소: item.addr, - 용량: item.fctCpc, - 유입BOD: item.inflAssctBfrWqltBod, - 유입TN: item.inflAssctBfrWqltTn, - 유입TP: item.inflAssctBfrWqltTp, - 유출BOD: item.efflWqltBod, - 유출TN: item.efflWqltTn, - 유출TP: item.efflWqltTp, - 환경청: item.rgnNm, - 유역: item.rvbsn, - 대유역: item.lgbsn, - 중유역: item.mdmbsn, - }); - }); - }else{ */ - /* data.forEach((item: OperationalStatusListProps) => { - }); */ - /* } */ set((state: DashBoardType) => ({ ...state, OperationStatus: { @@ -1295,7 +1402,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) }, })) }, - + searchMainGraph: async (searchYear: string, fcltyCd: string) => { set((state: DashBoardType) => ({ ...state, @@ -1449,7 +1556,31 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const rainEffectList = calRainEffectDayRainManage(res.data.wqltObjs); // Process the data and create rain management list + + const totItemCnt = rainEffectList?rainEffectList.length : 1; + let totInflAssctBfrWqltBod = 0; + let totInflAssctBfrWqltToc = 0; + let totInflAssctBfrWqltSs = 0; + let totInflAssctBfrWqltTn = 0; + let totInflAssctBfrWqltTp = 0; + let totEfflWqltBod = 0; + let totEfflWqltToc = 0; + let totEfflWqltSs = 0; + let totEfflWqltTn = 0; + let totEfflWqltTp = 0; + rainEffectList.forEach((item: RainManageListProps) => { + totInflAssctBfrWqltBod += item.inflAssctBfrWqltBod ? Number(item.inflAssctBfrWqltBod.toString().trim()) : 0; + totInflAssctBfrWqltToc += item.inflAssctBfrWqltToc ? Number(item.inflAssctBfrWqltToc.toString().trim()) : 0; + totInflAssctBfrWqltSs += item.inflAssctBfrWqltSs ? Number(item.inflAssctBfrWqltSs.toString().trim()) : 0; + totInflAssctBfrWqltTn += item.inflAssctBfrWqltTn ? Number(item.inflAssctBfrWqltTn.toString().trim()) : 0; + totInflAssctBfrWqltTp += item.inflAssctBfrWqltTp ? Number(item.inflAssctBfrWqltTp.toString().trim()) : 0; + totEfflWqltBod += item.efflWqltBod ? Number(item.efflWqltBod.toString().trim()) : 0; + totEfflWqltToc += item.efflWqltToc ? Number(item.efflWqltToc.toString().trim()) : 0; + totEfflWqltSs += item.efflWqltSs ? Number(item.efflWqltSs.toString().trim()) : 0; + totEfflWqltTn += item.efflWqltTn ? Number(item.efflWqltTn.toString().trim()) : 0; + totEfflWqltTp += item.efflWqltTp ? Number(item.efflWqltTp.toString().trim()) : 0; + if(Number(item.infoRnDay) >= 3 && Number(item.infoRnDay) < 10){ rainDate[1] = Number(rainDate[1] || 0) + 1; inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1; @@ -1537,7 +1668,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) inflAssctBfrWqltSsCnt[4] = Number(inflAssctBfrWqltSsCnt[4] || 0) + 1; inflAssctBfrWqltSs[4] = Number(inflAssctBfrWqltSs[4] || 0) + Number(item.inflAssctBfrWqltSs || 0); inflAssctBfrWqltTnCnt[4] = Number(inflAssctBfrWqltTnCnt[4] || 0) + 1; - inflAssctBfrWqltTn[4] = Number(inflAssctBfrWqltTn[5] || 0) + Number(item.inflAssctBfrWqltTn || 0); + inflAssctBfrWqltTn[4] = Number(inflAssctBfrWqltTn[4] || 0) + Number(item.inflAssctBfrWqltTn || 0); inflAssctBfrWqltTpCnt[4] = Number(inflAssctBfrWqltTpCnt[4] || 0) + 1; inflAssctBfrWqltTp[4] = Number(inflAssctBfrWqltTp[4] || 0) + Number(item.inflAssctBfrWqltTp || 0); efflWqltBodCnt[4] = Number(efflWqltBodCnt[4] || 0) + 1; @@ -1546,9 +1677,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltToc[4] = Number(efflWqltToc[4] || 0) + Number(item.efflWqltToc || 0); efflWqltSsCnt[4] = Number(efflWqltSsCnt[4] || 0) + 1; efflWqltSs[4] = Number(efflWqltSs[4] || 0) + Number(item.efflWqltSs || 0); - efflWqltTnCnt[4] = Number(efflWqltTnCnt[5] || 0) + 1; + efflWqltTnCnt[4] = Number(efflWqltTnCnt[4] || 0) + 1; efflWqltTn[4] = Number(efflWqltTn[4] || 0) + Number(item.efflWqltTn || 0); - efflWqltTpCnt[4] = Number(efflWqltTpCnt[5] || 0) + 1; + efflWqltTpCnt[4] = Number(efflWqltTpCnt[4] || 0) + 1; efflWqltTp[4] = Number(efflWqltTp[4] || 0) + Number(item.efflWqltTp || 0); }else if(Number(item.infoRnDay) >= 40 && Number(item.infoRnDay) < 50){ rainDate[5] = Number(rainDate[5] || 0) + 1; @@ -1683,7 +1814,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) // 평균 및 합계 계산 const calculateAverage = (value: number, count: number) => { - return count > 0 ? Number((value / count).toFixed(1)) : 0; + return count > 0 ? Number((value / count)) : 0; }; // 데이터 있는 항목만 계산 @@ -1710,44 +1841,44 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) // 합계 계산 (index 0) const calculateTotal = (arr: number[]) => { - return Number((arr.slice(1).reduce((sum, val) => sum + Number(val), 0)).toFixed(1)); + return Number((arr.slice(1).reduce((sum, val) => sum + Number(val), 0))); }; inflAmnt[0] = calculateAverage(calculateTotal(inflAmnt), dataIsCount); fcltyCpct[0] = calculateTotal(fcltyCpct); - inflAssctBfrWqltBod[0] = Number((calculateTotal(inflAssctBfrWqltBod)/7).toFixed(1)); - inflAssctBfrWqltToc[0] = Number((calculateTotal(inflAssctBfrWqltToc)/7).toFixed(1)); - inflAssctBfrWqltSs[0] = Number((calculateTotal(inflAssctBfrWqltSs)/7).toFixed(1)); - inflAssctBfrWqltTn[0] = Number((calculateTotal(inflAssctBfrWqltTn)/7).toFixed(3)); - inflAssctBfrWqltTp[0] = Number((calculateTotal(inflAssctBfrWqltTp)/7).toFixed(3)); - efflWqltBod[0] = Number((calculateTotal(efflWqltBod)/7).toFixed(1)); - efflWqltToc[0] = Number((calculateTotal(efflWqltToc)/7).toFixed(1)); - efflWqltSs[0] = Number((calculateTotal(efflWqltSs as number[])/7).toFixed(1)); - efflWqltTn[0] = Number((calculateTotal(efflWqltTn as number[])/7).toFixed(3)); - efflWqltTp[0] = Number((calculateTotal(efflWqltTp as number[])/7).toFixed(3)); + inflAssctBfrWqltBod[0] = Number((Math.round((totInflAssctBfrWqltBod / totItemCnt) * 10) / 10).toFixed(1)); + inflAssctBfrWqltToc[0] = Number((Math.round((totInflAssctBfrWqltToc / totItemCnt) * 10) / 10).toFixed(1)); + inflAssctBfrWqltSs[0] = Number((Math.round((totInflAssctBfrWqltSs / totItemCnt) * 10) / 10).toFixed(1)); + inflAssctBfrWqltTn[0] = Number((Math.round((totInflAssctBfrWqltTn / totItemCnt) * 1000) / 1000).toFixed(3)); + inflAssctBfrWqltTp[0] = Number((Math.round((totInflAssctBfrWqltTp / totItemCnt) * 1000) / 1000).toFixed(3)); + efflWqltBod[0] = Number((Math.round((totEfflWqltBod / totItemCnt) * 10) / 10).toFixed(1)); + efflWqltToc[0] = Number((Math.round((totEfflWqltToc / totItemCnt) * 10) / 10).toFixed(1)); + efflWqltSs[0] = Number((Math.round((totEfflWqltSs / totItemCnt) * 10) / 10).toFixed(1)); + efflWqltTn[0] = Number((Math.round((totEfflWqltTn / totItemCnt) * 1000) / 1000).toFixed(3)); + efflWqltTp[0] = Number((Math.round((totEfflWqltTp / totItemCnt) * 1000) / 1000).toFixed(3)); // 최종 리스트 const filteredData = itemList.map((item, index) => ({ rainItem: item, rainRate: rainRate[index] || '0', - rainDay: rainDate[index] ? Number(rainDate[index].toFixed(1)) : '', - inflAmnt: inflAmnt[index] ? Number(inflAmnt[index].toFixed(1)) : '', - fctCpc: inflAmnt[index] ? Number((inflAmnt[index] / fcltyCpcValue).toFixed(1)) : '', - dsInflWqltBod: dsInflWqltBod[index] ? Number(dsInflWqltBod[index].toFixed(1)) : '', - dsInflWqltCod: dsInflWqltCod[index] ? Number(dsInflWqltCod[index].toFixed(1)) : '', - dsInflWqltSs: dsInflWqltSs[index] ? Number(dsInflWqltSs[index].toFixed(1)) : '', - dsInflWqltTn: dsInflWqltTn[index] ? dsInflWqltTn[index].toFixed(3) : '', - dsInflWqltTp: dsInflWqltTp[index] ? dsInflWqltTp[index].toFixed(3) : '', - inflAssctBfrWqltBod: inflAssctBfrWqltBod[index] ? Number(inflAssctBfrWqltBod[index].toFixed(1)) : '', - inflAssctBfrWqltToc: inflAssctBfrWqltToc[index] ? Number(inflAssctBfrWqltToc[index].toFixed(1)) : '', - inflAssctBfrWqltSs: inflAssctBfrWqltSs[index] ? Number(inflAssctBfrWqltSs[index].toFixed(1)) : '', - inflAssctBfrWqltTn: inflAssctBfrWqltTn[index] ? inflAssctBfrWqltTn[index].toFixed(3) : '', - inflAssctBfrWqltTp: inflAssctBfrWqltTp[index] ? inflAssctBfrWqltTp[index].toFixed(3) : '', - efflWqltBod: efflWqltBod[index] ? Number(efflWqltBod[index].toFixed(1)) : '', - efflWqltToc: efflWqltToc[index] ? Number(efflWqltToc[index].toFixed(1)) : '', - efflWqltSs: efflWqltSs[index] ? Number(efflWqltSs[index].toFixed(1)) : '', - efflWqltTn: efflWqltTn[index] ? efflWqltTn[index].toFixed(3) : '', - efflWqltTp: efflWqltTp[index] ? efflWqltTp[index].toFixed(3) : '' + rainDay: rainDate[index] ? Number(rainDate[index].toFixed(1)) : '0', + inflAmnt: inflAmnt[index] ? Number((Math.round(inflAmnt[index] * 1) / 1)) : '', + fctCpc: inflAmnt[index] ? (Math.round((inflAmnt[index] / fcltyCpcValue) * 10) / 10).toFixed(1) : '', + dsInflWqltBod: dsInflWqltBod[index] ? (Math.round(dsInflWqltBod[index] * 10) / 10).toFixed(1) : '', + dsInflWqltCod: dsInflWqltCod[index] ? (Math.round(dsInflWqltCod[index] * 10) / 10).toFixed(1) : '', + dsInflWqltSs: dsInflWqltSs[index] ? (Math.round(dsInflWqltSs[index] * 10) / 10).toFixed(1) : '', + dsInflWqltTn: dsInflWqltTn[index] ? (Math.round(dsInflWqltTn[index] * 1000) / 1000).toFixed(3) : '', + dsInflWqltTp: dsInflWqltTp[index] ? (Math.round(dsInflWqltTp[index] * 1000) / 1000).toFixed(3) : '', + inflAssctBfrWqltBod: inflAssctBfrWqltBod[index] ? (Math.round(inflAssctBfrWqltBod[index] * 10) / 10).toFixed(1) : '', + inflAssctBfrWqltToc: inflAssctBfrWqltToc[index] ? (Math.round(inflAssctBfrWqltToc[index] * 10) / 10).toFixed(1) : '', + inflAssctBfrWqltSs: inflAssctBfrWqltSs[index] ? (Math.round(inflAssctBfrWqltSs[index] * 10) / 10).toFixed(1) : '', + inflAssctBfrWqltTn: inflAssctBfrWqltTn[index] ? (Math.round(inflAssctBfrWqltTn[index] * 1000) / 1000).toFixed(3) : '', + inflAssctBfrWqltTp: inflAssctBfrWqltTp[index] ? (Math.round(inflAssctBfrWqltTp[index] * 1000) / 1000).toFixed(3) : '', + efflWqltBod: efflWqltBod[index] ? (Math.round(efflWqltBod[index] * 10) / 10).toFixed(1): '', + efflWqltToc: efflWqltToc[index] ? (Math.round(efflWqltToc[index] * 10) / 10).toFixed(1) : '', + efflWqltSs: efflWqltSs[index] ? (Math.round(efflWqltSs[index] * 10) / 10).toFixed(1) : '', + efflWqltTn: efflWqltTn[index] ? (Math.round(efflWqltTn[index] * 1000) / 1000).toFixed(3) : '', + efflWqltTp: efflWqltTp[index] ? (Math.round(efflWqltTp[index] * 1000) / 1000).toFixed(3) : '' })); const excelList : RainManageExcelType[] = []; @@ -1756,24 +1887,24 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) excelList.push({ 강우량: item.rainItem, 강우조건비율: item.rainRate ? Number(Number(item.rainRate).toFixed(1)) : '', - 일수: item.rainDay ? Number(Number(item.rainDay).toFixed(1)) : '', - 유입유량: item.inflAmnt ? Number(Number(item.inflAmnt).toFixed(1)) : '', - 시설용량대비: item.fctCpc ? Number(Number(item.fctCpc).toFixed(1)) : '', - 설계유입수질BOD: item.dsInflWqltBod ? Number(Number(item.dsInflWqltBod).toFixed(1)) : '', - 설계유입수질TOC: item.dsInflWqltCod ? Number(Number(item.dsInflWqltCod).toFixed(1)) : '', - 설계유입수질SS: item.dsInflWqltSs ? Number(Number(item.dsInflWqltSs).toFixed(1)) : '', - 설계유입수질TN: item.dsInflWqltTn ? Number(item.dsInflWqltTn).toFixed(3) : '', - 설계유입수질TP: item.dsInflWqltTp ? Number(item.dsInflWqltTp).toFixed(3) : '', - 유입수질BOD: item.inflAssctBfrWqltBod ? Number(Number(item.inflAssctBfrWqltBod).toFixed(1)) : '', - 유입수질TOC: item.inflAssctBfrWqltToc ? Number(Number(item.inflAssctBfrWqltToc).toFixed(1)) : '', - 유입수질SS: item.inflAssctBfrWqltSs ? Number(Number(item.inflAssctBfrWqltSs).toFixed(1)) : '', - 유입수질TN: item.inflAssctBfrWqltTn ? Number(item.inflAssctBfrWqltTn).toFixed(3) : '', - 유입수질TP: item.inflAssctBfrWqltTp ? Number(item.inflAssctBfrWqltTp).toFixed(3) : '', - 방류수질BOD: item.efflWqltBod ? Number(Number(item.efflWqltBod).toFixed(1)) : '', - 방류수질TOC: item.efflWqltToc ? Number(Number(item.efflWqltToc).toFixed(1)) : '', - 방류수질SS: item.efflWqltSs ? Number(Number(item.efflWqltSs).toFixed(1)) : '', - 방류수질TN: item.efflWqltTn ? Number(item.efflWqltTn).toFixed(3) : '', - 방류수질TP: item.efflWqltTp ? Number(item.efflWqltTp).toFixed(3) : '', + 일수: item.rainDay ? Number(Number(item.rainDay).toFixed(1)) : 0, + 유입유량: item.inflAmnt ? Number((Math.round(Number(item.inflAmnt) * 1) / 1)) : '', + 시설용량대비: item.fctCpc ? (Math.round(Number(item.fctCpc) * 10) / 10).toFixed(1) : '', + 설계유입수질BOD: item.dsInflWqltBod ? (Math.round(Number(item.dsInflWqltBod) * 10) / 10).toFixed(1) : '', + 설계유입수질TOC: item.dsInflWqltCod ? (Math.round(Number(item.dsInflWqltCod) * 10) / 10).toFixed(1) : '', + 설계유입수질SS: item.dsInflWqltSs ? (Math.round(Number(item.dsInflWqltSs) * 10) / 10).toFixed(1) : '', + 설계유입수질TN: item.dsInflWqltTn ?(Math.round(Number(item.dsInflWqltTn) * 1000) / 1000).toFixed(3) : '', + 설계유입수질TP: item.dsInflWqltTp ? (Math.round(Number(item.dsInflWqltTp) * 1000) / 1000).toFixed(3) : '', + 유입수질BOD: item.inflAssctBfrWqltBod ? (Math.round(Number(item.inflAssctBfrWqltBod) * 10) / 10).toFixed(1) : '', + 유입수질TOC: item.inflAssctBfrWqltToc ? (Math.round(Number(item.inflAssctBfrWqltToc) * 10) / 10).toFixed(1) : '', + 유입수질SS: item.inflAssctBfrWqltSs ? (Math.round(Number(item.inflAssctBfrWqltSs) * 10) / 10).toFixed(1) : '', + 유입수질TN: item.inflAssctBfrWqltTn ? (Math.round(Number(item.inflAssctBfrWqltTn) * 1000) / 1000).toFixed(3) : '', + 유입수질TP: item.inflAssctBfrWqltTp ? (Math.round(Number(item.inflAssctBfrWqltTp) * 1000) / 1000).toFixed(3) : '', + 방류수질BOD: item.efflWqltBod ? (Math.round(Number(item.efflWqltBod) * 10) / 10).toFixed(1) : '', + 방류수질TOC: item.efflWqltToc ? (Math.round(Number(item.efflWqltToc) * 10) / 10).toFixed(1) : '', + 방류수질SS: item.efflWqltSs ? (Math.round(Number(item.efflWqltSs) * 10) / 10).toFixed(1) : '', + 방류수질TN: item.efflWqltTn ? (Math.round(Number(item.efflWqltTn) * 1000) / 1000).toFixed(3) : '', + 방류수질TP: item.efflWqltTp ? (Math.round(Number(item.efflWqltTp) * 1000) / 1000).toFixed(3) : '', }); }); } @@ -1782,12 +1913,12 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) ...state, RainManage: isExcel ? { ...state.RainManage, - rainManageList: filteredData as RainManageListProps[], + rainManageList: filteredData as any[], excelList: excelList, isLoadingRainManage : false, } : { ...state.RainManage, - rainManageList: filteredData as RainManageListProps[], + rainManageList: filteredData as any[], isLoadingRainManage : false, } })); diff --git a/apps/fo/app/store/facilityPrecipitation/index.tsx b/apps/fo/app/store/facilityPrecipitation/index.tsx index 833100a75..66727122d 100644 --- a/apps/fo/app/store/facilityPrecipitation/index.tsx +++ b/apps/fo/app/store/facilityPrecipitation/index.tsx @@ -68,36 +68,33 @@ const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시 // 리스트 헤더 const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2}, { id: "A", title: "행정구역", row: 0, colSpan: 2 }, { id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row: 1 }, { id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2, row: 1 }, { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2}, { id: "addr", title: "주소", row: 0, rowSpan: 2}, - { id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2}, + { id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2, align:"right"}, { id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2}, - { id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2}, - { id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2}, - { id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2} + { id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2, align:"right"}, + { id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2, align:"right"}, + { id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2, align:"right"} ]; const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2}, { id: "A", title: "유역", row: 0, colSpan: 3} , { id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row: 1} , { id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row: 1}, { id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row: 1}, { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2}, { id: "addr", title: "주소", row: 0, rowSpan: 2}, - { id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2}, + { id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2, align:"right"}, { id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2}, - { id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2}, - { id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2}, - { id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2} + { id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2, align:"right"}, + { id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2, align:"right"}, + { id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2, align:"right"} ]; const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2}, { id: "A", title: "유역", row: 0, colSpan: 3} , { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row: 1}, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row: 1}, @@ -108,11 +105,11 @@ const upHeadList3:TableUpperProps[] = [ { id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2, row: 1}, { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2}, { id: "addr", title: "주소", row: 0, rowSpan: 2}, - { id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2}, + { id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2, align:"right"}, { id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2}, - { id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2}, - { id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2}, - { id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2} + { id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2, align:"right"}, + { id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2, align:"right"}, + { id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2, align:"right"} ]; const facilityPrecipitation:FacilityPrecipitationProps[] = [ @@ -124,9 +121,9 @@ const facilityPrecipitation:FacilityPrecipitationProps[] = [ // 강수현황 (헤더) const upHeadListPrecipitationDetail:TableUpperProps[] = [ { id: "rainDt", title: "날짜"}, - { id: "infoRnDay", title: "강수량(mm/일)"}, - { id: "rainDay", title: "강우일(일)"}, - { id: "rainEffectDay", title: "강우영향일(일)"} + { id: "infoRnDay", title: "강수량(mm/일)", align:"right"}, + { id: "rainDay", title: "강우일(일)", align:"right"}, + { id: "rainEffectDay", title: "강우영향일(일)", align:"right"} ] // 강수현황 (리스트) const gridListPrecipitationDetail:PrecipitationPropsDetailProps[] = [ diff --git a/apps/fo/app/store/facilityWaterQlty/index.tsx b/apps/fo/app/store/facilityWaterQlty/index.tsx index 53e430d47..5b7bcf816 100644 --- a/apps/fo/app/store/facilityWaterQlty/index.tsx +++ b/apps/fo/app/store/facilityWaterQlty/index.tsx @@ -122,7 +122,6 @@ const mainLabelArray: string[] = ['구분', '시도', '시군구', '용량별', // 시설수질리스트 헤더(행정별) const upHeadList1: TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", rowSpan: 2, row:0 }, { id: "C", title: "행정명", colSpan: 2, row: 0 }, { id: "sidoNm", title: "시도", row: 1 }, { id: "sggNm", title: "시군구", row: 1 }, @@ -145,7 +144,6 @@ const upHeadList1: TableUpperProps[] = [ // 시설수질리스트 헤더(권역별) const upHeadList2: TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "C", title: "유역", colSpan: 3, row: 0 }, { id: "rvbsn", title: "권역", upName: "upChangeRe1", upSequnce: 1, row:1 }, { id: "lgbsn", title: "단위유역", upName: "upChangeRe1", upSequnce: 2, row:1 }, @@ -169,7 +167,6 @@ const upHeadList2: TableUpperProps[] = [ // 시설수질리스트 헤더(환경청별) const upHeadList3: TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "C", title: "유역", colSpan: 3, row: 0 }, { id: "rvbsn", title: "권역", row:1 }, { id: "lgbsn", title: "단위유역", row:1 }, @@ -269,7 +266,7 @@ const fetchFacilityWaterQltyTmsList = async (facilityCd: string|undefined) => { }; // 시설정보 수질 리스트 데이터 호출(모달-이력) -const fetchFacilityWaterQltyListHist = async (searchYear: string|undefined, facilityCd: string|undefined): Promise => { +export const fetchFacilityWaterQltyListHist = async (searchYear: string|undefined, facilityCd: string|undefined): Promise => { try {// const res = await getFetch('fo', '/fclt/qlty/hist/' + searchYear + '/' + facilityCd); return res; @@ -452,6 +449,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.inflAssctBfrWqltToc = Number(searchYear)<2021? "("+item.inflAssctBfrWqltToc+")" : item.inflAssctBfrWqltToc; + item.efflWqltToc = Number(searchYear)<2021? "("+item.efflWqltToc+")" : item.efflWqltToc; excelDownList.push({ 시설코드: item.bscFctCd, 권역: item.rvbsn, @@ -481,6 +480,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.inflAssctBfrWqltToc = Number(searchYear)<2021? "("+item.inflAssctBfrWqltToc+")" : item.inflAssctBfrWqltToc; + item.efflWqltToc = Number(searchYear)<2021? "("+item.efflWqltToc+")" : item.efflWqltToc; }); } set((state: FacilityWaterQltyType) => ({ @@ -603,12 +604,12 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType for(let idx = 0; idx < data.length; idx++) { if(data[idx] && data[idx]?.rainEffectDay === 1) { if(idx > 1){ - if(data[idx-1]?.infoRnDay && Number(data[idx-1]?.infoRnDay) > 3) { + if(data[idx-1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-1]?.infoRnDay) >= 3) { data[idx]!.infoRnDay = data[idx-1]!.infoRnDay; } } if(idx > 2){ - if(data[idx-2]?.infoRnDay && Number(data[idx-2]?.infoRnDay) > 3) { + if(data[idx-2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-2]?.infoRnDay) >= 3) { data[idx]!.infoRnDay = data[idx-2]!.infoRnDay; } } @@ -659,7 +660,6 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType }else{ item.tpInflWqltBod = ''; } - if(item.tpInflWqltTp){ item.tpInflWqltTp = item.tpInflWqltTp?Number(Math.round(Number(item.tpInflWqltTp) * 1000)/1000).toFixed(3): Number(item.tpInflWqltTp)===0?'0.000':''; }else{ diff --git a/apps/fo/app/store/flowRate/index.tsx b/apps/fo/app/store/flowRate/index.tsx index a7be5bf60..ec64465d0 100644 --- a/apps/fo/app/store/flowRate/index.tsx +++ b/apps/fo/app/store/flowRate/index.tsx @@ -91,7 +91,6 @@ export type FlowRateModalProps = { */ // 리스트 (행정별) const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2}, { id: "A", title: "행정명", row: 0 ,colSpan: 2 }, { id: "sido", title: "시도", upName:"upChangeRe", row:1 }, { id: "gugun", title: "시군구", upName:"upChangeRe", row:1 }, @@ -107,7 +106,6 @@ const upHeadList1:TableUpperProps[] = [ // 리스트 (권역별) const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2}, { id: "A", title: "유역", row: 0, colSpan: 3}, { id: "rvbsn", title: "권역", upName:"upChangeRe", row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe", row:1 }, @@ -124,7 +122,6 @@ const upHeadList2:TableUpperProps[] = [ // 리스트 (환경청별별) const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row: 0, colSpan: 3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe1", row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", row:1 }, @@ -157,7 +154,7 @@ const upHeadListFlowRateModal:TableUpperProps[] = [ { id: "sumFlow", title: "연계처리량(m³/일)", align:"right"}, { id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"}, { id: "efflAmnt", title: "방류량(m³/일)", align:"right"}, - /* { id: "infoRnDay", title: "강우량(mm/일)", align:"right"} */ + { id: "infoRnDay", title: "강우량(mm/일)", align:"right"} ] const modalFlowRateListYear:FlowRateModalProps[] = [ @@ -233,12 +230,12 @@ export type FlowRateSearchStore = FlowRateSearchType & FlowRateSearchActions; const dataFormSetting=(item : flowRateListProps | FlowRateModalProps)=> { DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record }); - DataForm({ type: 'float1', column: 'totInflAmnt', item: item as unknown as Record}); - DataForm({ type: 'float1', column: 'inflAmnt', item: item as unknown as Record}); - DataForm({ type: 'float1', column: 'sumFlow', item: item as unknown as Record}); - DataForm({ type: 'float1', column: 'efflWqltBod', item: item as unknown as Record}); - DataForm({ type: 'float1', column: 'returnAmnt', item: item as unknown as Record}); - DataForm({ type: 'float1', column: 'efflAmnt', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'totInflAmnt', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'inflAmnt', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'sumFlow', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'efflWqltBod', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'returnAmnt', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'efflAmnt', item: item as unknown as Record}); } export const flowRateSearchInitState:FlowRateSearchType = { @@ -351,12 +348,12 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow for(let idx = 0; idx < data.length; idx++) { if(data[idx] && data[idx]?.rainEffectDay === 1) { if(idx > 1){ - if(data[idx-1]?.infoRnDay && Number(data[idx-1]?.infoRnDay.toString().trim()) >= 3) { + if(data[idx-1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-1]?.infoRnDay.toString().trim()) >= 3) { data[idx]!.infoRnDay = data[idx-1]!.infoRnDay; } } if(idx > 2){ - if(data[idx-2]?.infoRnDay && Number(data[idx-2]?.infoRnDay.toString().trim()) >= 3) { + if(data[idx-2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-2]?.infoRnDay.toString().trim()) >= 3) { data[idx]!.infoRnDay = data[idx-2]!.infoRnDay; } } diff --git a/apps/fo/app/store/planSewage/index.tsx b/apps/fo/app/store/planSewage/index.tsx index 112070300..885949e3b 100644 --- a/apps/fo/app/store/planSewage/index.tsx +++ b/apps/fo/app/store/planSewage/index.tsx @@ -44,7 +44,6 @@ const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시 // 리스트 헤더 const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "행정구역", upSequnce:1, row:0, colSpan:2 }, { id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 }, @@ -52,12 +51,11 @@ const upHeadList1:TableUpperProps[] = [ { id: "daddr", title: "주소", row:0, rowSpan:2 }, { id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' }, { id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' }, - { id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' }, + { id: "variableLoadPercent", title: "변동부하율", row:0, rowSpan:2, align:'right' }, { id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' }, ]; const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1}, { id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1}, @@ -66,12 +64,11 @@ const upHeadList2:TableUpperProps[] = [ { id: "daddr", title: "주소", row:0, rowSpan:2 }, { id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' }, { id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' }, - { id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' }, + { id: "variableLoadPercent", title: "변동부하율", row:0, rowSpan:2, align:'right' }, { id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' }, ]; const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1}, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1}, @@ -84,7 +81,7 @@ const upHeadList3:TableUpperProps[] = [ { id: "daddr", title: "주소", row:0, rowSpan:2 }, { id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' }, { id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' }, - { id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' }, + { id: "variableLoadPercent", title: "변동부하율", row:0, rowSpan:2, align:'right' }, { id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' }, ]; @@ -122,9 +119,9 @@ export type ExcelType = { } const dataFormSetting=(item : PlanSewageProps)=> { - DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record }); - DataForm({ type: 'float1', column: 'spareCapacity', item: item as unknown as Record}); - DataForm({ type: 'float1', column: 'variableLoadPercent', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'spareCapacity', item: item as unknown as Record}); + DataForm({ type: 'float2', column: 'variableLoadPercent', item: item as unknown as Record}); DataForm({ type: 'float1', column: 'classifiedSewerPercent', item: item as unknown as Record}); }; diff --git a/apps/fo/app/store/processArea/index.tsx b/apps/fo/app/store/processArea/index.tsx index 049341d23..dc3826fef 100644 --- a/apps/fo/app/store/processArea/index.tsx +++ b/apps/fo/app/store/processArea/index.tsx @@ -19,12 +19,12 @@ export type ProcessAreaProps = { location : string, fctCpc : number | '', ruse : boolean, - yrDt4 : string, - yr : string, - yrDc1 : string, - yrDc2 : string, - yrDc3 : string, - yrDc4 : string, + planYrDt4 : string, + planYr : string, + planYrDc1 : string, + planYrDc2 : string, + planYrDc3 : string, + planYrDc4 : string, /** * 증설계획 */ @@ -128,7 +128,6 @@ const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용 // 리스트 헤더(행정별) const upHeadList1:TableUpperProps[] = [ - { id: "swtrmtFcltCd", title: "시설코드", rowSpan:3, row:0}, { id: "A", title: "행정구역", colSpan:2, rowSpan:2, row:0}, { id: "sido", title: "시도", upName:"upChangeRe", row:2 }, { id: "sigungo", title: "시군구", upName:"upChangeRe", row:2 }, @@ -137,26 +136,26 @@ const upHeadList1:TableUpperProps[] = [ { id: "fctCpc", title: "시설용량(m³/일)", align:"right", rowSpan:3, row:0}, { id: "B", title: "시설계획", colSpan:19, row:0}, { id: "C", title: "증설계획(m³/일)", colSpan:6, row:1}, - { id: "planSwageQntSumDt4", title: "기승인\n4단계", row:2}, - { id: "planSwageQntSum", title: "현재", row:2}, - { id: "planSwageQntSumDc1", title: "1단계", row:2}, - { id: "planSwageQntSumDc2", title: "2단계", row:2}, - { id: "planSwageQntSumDc3", title: "3단계", row:2}, - { id: "planSwageQntSumDc4", title: "4단계", row:2}, + { id: "planSwageQntSumDt4", title: "기승인\n4단계", row:2, align:"right"}, + { id: "planSwageQntSum", title: "현재", row:2, align:"right"}, + { id: "planSwageQntSumDc1", title: "1단계", row:2, align:"right"}, + { id: "planSwageQntSumDc2", title: "2단계", row:2, align:"right"}, + { id: "planSwageQntSumDc3", title: "3단계", row:2, align:"right"}, + { id: "planSwageQntSumDc4", title: "4단계", row:2, align:"right"}, { id: "D", title: "처리인구(인)", colSpan:6, row:1}, - { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계", row:2}, - { id: "swtrmtPpltnCnt", title: "현재", row:2}, - { id: "swtrmtPpltnCntDc1", title: "1단계", row:2}, - { id: "swtrmtPpltnCntDc2", title: "2단계", row:2}, - { id: "swtrmtPpltnCntDc3", title: "3단계", row:2}, - { id: "swtrmtPpltnCntDc4", title: "4단계", row:2}, + { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계", row:2, align:"right"}, + { id: "swtrmtPpltnCnt", title: "현재", row:2, align:"right"}, + { id: "swtrmtPpltnCntDc1", title: "1단계", row:2, align:"right"}, + { id: "swtrmtPpltnCntDc2", title: "2단계", row:2, align:"right"}, + { id: "swtrmtPpltnCntDc3", title: "3단계", row:2, align:"right"}, + { id: "swtrmtPpltnCntDc4", title: "4단계", row:2, align:"right"}, { id: "E", title: "처리구역(km²)", colSpan:7, row:1}, - { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계", row:2}, - { id: "swtrmtZoneArea", title: "현재", row:2}, - { id: "swtrmtZoneAreaDc1", title: "1단계", row:2}, - { id: "swtrmtZoneAreaDc2", title: "2단계", row:2}, - { id: "swtrmtZoneAreaDc3", title: "3단계", row:2}, - { id: "swtrmtZoneAreaDc4", title: "4단계", row:2}, + { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계", row:2, align:"right"}, + { id: "swtrmtZoneArea", title: "현재", row:2, align:"right"}, + { id: "swtrmtZoneAreaDc1", title: "1단계", row:2, align:"right"}, + { id: "swtrmtZoneAreaDc2", title: "2단계", row:2, align:"right"}, + { id: "swtrmtZoneAreaDc3", title: "3단계", row:2, align:"right"}, + { id: "swtrmtZoneAreaDc4", title: "4단계", row:2, align:"right"}, { id: "trtDstr", title: "처리구역", row:2} ]; @@ -168,13 +167,13 @@ const processAreaList:ProcessAreaProps[] = [ */ // 시설증설 계획 (헤더) const upHeadListFcltyImproveInfo:TableUpperProps[] = [ - { id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan:2, row:0}, + { id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan:2, row:0, align:"right"}, { id: "A", title: "증설계획(m³/일)", colSpan:5, row:0}, - { id: "planSwageQntSum", title: "현재", row:1}, - { id: "planSwageQntSumDc1", title: "1단계", row:1}, - { id: "planSwageQntSumDc2", title: "2단계", row:1}, - { id: "planSwageQntSumDc3", title: "3단계", row:1}, - { id: "planSwageQntSumDc4", title: "4단계", row:1}, + { id: "planSwageQntSum", title: "현재", row:1, align:"right"}, + { id: "planSwageQntSumDc1", title: "1단계", row:1, align:"right"}, + { id: "planSwageQntSumDc2", title: "2단계", row:1, align:"right"}, + { id: "planSwageQntSumDc3", title: "3단계", row:1, align:"right"}, + { id: "planSwageQntSumDc4", title: "4단계", row:1, align:"right"}, ]; // 시설증설 계획 (리스트) @@ -183,13 +182,13 @@ const gridListFcltyImproveInfo:FcltyImproveInfoProps[] = [ // 처리인구 계획 (헤더) const upHeadListProcessPopulation:TableUpperProps[] = [ - { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan:2, row:0}, + { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan:2, row:0, align:"right"}, { id: "A", title: "처리인구(인)", colSpan:5, row:0}, - { id: "swtrmtPpltnCnt", title: "현재", row:1}, - { id: "swtrmtPpltnCntDc1", title: "1단계", row:1}, - { id: "swtrmtPpltnCntDc2", title: "2단계", row:1}, - { id: "swtrmtPpltnCntDc3", title: "3단계", row:1}, - { id: "swtrmtPpltnCntDc4", title: "4단계", row:1}, + { id: "swtrmtPpltnCnt", title: "현재", row:1, align:"right"}, + { id: "swtrmtPpltnCntDc1", title: "1단계", row:1, align:"right"}, + { id: "swtrmtPpltnCntDc2", title: "2단계", row:1, align:"right"}, + { id: "swtrmtPpltnCntDc3", title: "3단계", row:1, align:"right"}, + { id: "swtrmtPpltnCntDc4", title: "4단계", row:1, align:"right"}, ]; // 처리인구 계획 (리스트) @@ -198,13 +197,13 @@ const gridListProcessPopulation:ProcessPopulationProps[] = [ // 처리구역 계획 (헤더) const upHeadListProcessAreaPlan:TableUpperProps[] = [ - { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계(km²)", rowSpan:2, row:0}, - { id: "A", title: "처리면적(km²)", colSpan:6, row:0}, - { id: "swtrmtZoneArea", title: "현재", row:1}, - { id: "swtrmtZoneAreaDc1", title: "1단계", row:1}, - { id: "swtrmtZoneAreaDc2", title: "2단계", row:1}, - { id: "swtrmtZoneAreaDc3", title: "3단계", row:1}, - { id: "swtrmtZoneAreaDc4", title: "4단계", row:1}, + { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계(km²)", rowSpan:2, row:0, align:"right"}, + { id: "A", title: "처리면적(km²)", colSpan:6, row:0, align:"right"}, + { id: "swtrmtZoneArea", title: "현재", row:1, align:"right"}, + { id: "swtrmtZoneAreaDc1", title: "1단계", row:1, align:"right"}, + { id: "swtrmtZoneAreaDc2", title: "2단계", row:1, align:"right"}, + { id: "swtrmtZoneAreaDc3", title: "3단계", row:1, align:"right"}, + { id: "swtrmtZoneAreaDc4", title: "4단계", row:1, align:"right"}, { id: "trtDstr", title: "처리구역", row:1} ]; @@ -236,17 +235,17 @@ const fetchProcessAreaList = async (pageNo : number, pageSize : number, ctpvNm : res.map(async (item: ProcessAreaProps) => { upHeadList1.forEach((head:TableUpperProps) => { /* if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = '현재\n(' + item.yr + '년)'; + head.title = '현재\n(' + item.planYr + '년)'; } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = '1단계\n(' + item.yrDc1 + '년)'; + head.title = '1단계\n(' + item.planYrDc1 + '년)'; } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = '2단계\n(' + item.yrDc2 + '년)'; + head.title = '2단계\n(' + item.planYrDc2 + '년)'; } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = '3단계\n(' + item.yrDc3 + '년)'; + head.title = '3단계\n(' + item.planYrDc3 + '년)'; } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = '4단계\n(' + item.yrDc4 + '년)'; + head.title = '4단계\n(' + item.planYrDc4 + '년)'; } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = '기승인\n4단계(' + item.yrDt4 + '년)'; + head.title = '기승인\n4단계(' + item.planYrDt4 + '년)'; } */ if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { head.title = '현재'; @@ -432,51 +431,51 @@ export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: set((state: ProcessAreaType) => { const rtnData = state.ProcessArea.processAreaList.find((item: ProcessAreaProps) => item.swtrmtFcltCd === swtrmtFcltCd); if (rtnData) { - upHeadListFcltyImproveInfo.forEach((head:TableUpperProps) => { - if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = rtnData.yr? '현재\n(' + rtnData.yr + '년)':head.title = '현재'; - } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = rtnData.yrDc1? '1단계\n(' + rtnData.yrDc1 + '년)':head.title = '1단계'; - } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = rtnData.yrDc2? '2단계\n(' + rtnData.yrDc2 + '년)':head.title = '2단계'; - } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = rtnData.yrDc3? '3단계\n(' + rtnData.yrDc3 + '년)':head.title = '3단계'; - } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = rtnData.yrDc4? '4단계\n(' + rtnData.yrDc4 + '년)':head.title = '4단계'; - } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = rtnData.yrDt4? '기승인\n4단계(' + rtnData.yrDt4 + '년)':head.title = '기승인\n4단계'; - } - }); + upHeadListFcltyImproveInfo.forEach((head:TableUpperProps) => { + if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = rtnData.planYr? '현재\n(' + rtnData.planYr + '년)':head.title = '현재'; + } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = rtnData.planYrDc1? '1단계\n(' + rtnData.planYrDc1 + '년)':head.title = '1단계'; + } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = rtnData.planYrDc2? '2단계\n(' + rtnData.planYrDc2 + '년)':head.title = '2단계'; + } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = rtnData.planYrDc3? '3단계\n(' + rtnData.planYrDc3 + '년)':head.title = '3단계'; + } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = rtnData.planYrDc4? '4단계\n(' + rtnData.planYrDc4 + '년)':head.title = '4단계'; + } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = rtnData.planYrDt4? '기승인\n4단계(' + rtnData.planYrDt4 + '년)':head.title = '기승인\n4단계'; + } + }); upHeadListProcessAreaPlan.forEach((head:TableUpperProps) => { if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = rtnData.yr? '현재\n(' + rtnData.yr + '년)':head.title = '현재'; + head.title = rtnData.planYr? '현재\n(' + rtnData.planYr + '년)':head.title = '현재'; } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = rtnData.yrDc1?'1단계\n(' + rtnData.yrDc1 + '년)':head.title = '1단계'; + head.title = rtnData.planYrDc1?'1단계\n(' + rtnData.planYrDc1 + '년)':head.title = '1단계'; } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = rtnData.yrDc2?'2단계\n(' + rtnData.yrDc2 + '년)':head.title = '2단계'; + head.title = rtnData.planYrDc2?'2단계\n(' + rtnData.planYrDc2 + '년)':head.title = '2단계'; } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = rtnData.yrDc3? '3단계\n(' + rtnData.yrDc3 + '년)':head.title = '3단계'; + head.title = rtnData.planYrDc3? '3단계\n(' + rtnData.planYrDc3 + '년)':head.title = '3단계'; } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = rtnData.yrDc4?'4단계\n(' + rtnData.yrDc4 + '년)':head.title = '4단계'; + head.title = rtnData.planYrDc4?'4단계\n(' + rtnData.planYrDc4 + '년)':head.title = '4단계'; } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = rtnData.yrDt4?'기승인\n4단계(' + rtnData.yrDt4 + '년)':head.title = '기승인\n4단계'; + head.title = rtnData.planYrDt4?'기승인\n4단계(' + rtnData.planYrDt4 + '년)':head.title = '기승인\n4단계'; } }); upHeadListProcessPopulation.forEach((head:TableUpperProps) => { if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = rtnData.yr?'현재\n(' + rtnData.yr + '년)':head.title = '현재'; + head.title = rtnData.planYr?'현재\n(' + rtnData.planYr + '년)':head.title = '현재'; } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = rtnData.yrDc1?'1단계\n(' + rtnData.yrDc1 + '년)':head.title = '1단계'; + head.title = rtnData.planYrDc1?'1단계\n(' + rtnData.planYrDc1 + '년)':head.title = '1단계'; } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = rtnData.yrDc2?'2단계\n(' + rtnData.yrDc2 + '년)':head.title = '2단계'; + head.title = rtnData.planYrDc2?'2단계\n(' + rtnData.planYrDc2 + '년)':head.title = '2단계'; } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = rtnData.yrDc3?'3단계\n(' + rtnData.yrDc3 + '년)':head.title = '3단계'; + head.title = rtnData.planYrDc3?'3단계\n(' + rtnData.planYrDc3 + '년)':head.title = '3단계'; } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = rtnData.yrDc4?'4단계\n(' + rtnData.yrDc4 + '년)':head.title = '4단계'; + head.title = rtnData.planYrDc4?'4단계\n(' + rtnData.planYrDc4 + '년)':head.title = '4단계'; } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = rtnData.yrDt4?'기승인\n4단계(' + rtnData.yrDt4 + '년)':head.title = '기승인\n4단계'; + head.title = rtnData.planYrDt4?'기승인\n4단계(' + rtnData.planYrDt4 + '년)':head.title = '기승인\n4단계'; } }); diff --git a/apps/fo/app/store/processFacility/index.tsx b/apps/fo/app/store/processFacility/index.tsx index 022c28c3e..9a2572916 100644 --- a/apps/fo/app/store/processFacility/index.tsx +++ b/apps/fo/app/store/processFacility/index.tsx @@ -117,7 +117,8 @@ export type fcltyHistInfoProps = { rnDt : string, sitArea : number, trtDstrArea : number, - swrTrtPopl : number + swrTrtPopl : number, + fcltCpc : number | '' } // 시설 수질정보 @@ -161,7 +162,6 @@ const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용 // 리스트 헤더 const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드"}, { id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1 }, { id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2 }, { id: "bscFctNm", title: "시설명"}, @@ -177,7 +177,6 @@ const upHeadList1:TableUpperProps[] = [ // 처리시설 리스트 (유역) const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드"}, { id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1}, { id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2}, { id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3}, @@ -194,7 +193,6 @@ const upHeadList2:TableUpperProps[] = [ // 처리시설 리스트 (환경청청) const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드"}, { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1}, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2}, { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3}, @@ -330,6 +328,7 @@ const upHeadListFcltyHistInfo:TableUpperProps[] = [ { id: "cmplDt", title: "준공일"}, { id: "trlrnCmplDt", title: "시운전완료일"}, { id: "rnDt", title: "가동개시일"}, + { id: "fctCpc", title: "시설용량(m³/일)", align: 'right' }, ]; // 시설 이력관리 (리스트) @@ -346,6 +345,7 @@ const gridListFcltyHistInfo:fcltyHistInfoProps[] = [ sitArea : 0, trtDstrArea : 0, swrTrtPopl : 0, + fcltCpc : 860000 } ]; @@ -601,7 +601,7 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio res.data.forEach((item:facilityListProps) => { DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record}); DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record}); - DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record}); + DataForm({ type: 'float0', column: 'fctCpc', item:item as unknown as Record}); }) res.data.forEach(item => { @@ -613,7 +613,6 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio if(pageSize>5000){ res.data.forEach(item => { - item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; excelDownList.push({ 시설코드: item.bscFctCd, 권역: item.rvbsn, @@ -633,10 +632,6 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio 관리대행업자: item.mnstr, }) }) - }else{ - res.data.forEach(item => { - item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; - }) } set((state:ProcessFacilityType) => ({ ...state, @@ -676,7 +671,6 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio item.plInflWqltBod = item.plInflWqltBod ? Number(item.plInflWqltBod).toFixed(1) : ''; item.dsInflWqltBod = item.dsInflWqltBod ? Number(item.dsInflWqltBod).toFixed(1) : ''; }) */ - console.log(res.data); set((state:ProcessFacilityType) => ({ ...state, ProcessFacilityList: { @@ -783,6 +777,29 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio } }); + sortedWaterQltyInfo.forEach(item => { + DataForm({ type: 'float1', column: 'plInflWqltBod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'plInflWqltToc', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'plInflWqltCod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'plInflWqltSs', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'plInflWqltTn', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'plInflWqltTp', item: item as unknown as Record }); + + DataForm({ type: 'float1', column: 'dsInflWqltBod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'dsInflWqltToc', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'dsInflWqltCod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'dsInflWqltSs', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'dsInflWqltTn', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'dsInflWqltTp', item: item as unknown as Record }); + + DataForm({ type: 'float1', column: 'plEfflWqltBod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'plEfflWqltToc', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'plEfflWqltCod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'plEfflWqltSs', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'plEfflWqltTn', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'plEfflWqltTp', item: item as unknown as Record }); + }); + // 법적수질정보 초기화 및 push gridListLegalWaterQlty = []; @@ -795,6 +812,14 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio totClfgrpcnt : Number(facilityWaterQltyInfo[0]?.totClfgrpcnt ?? 0), }); + gridListLegalWaterQlty.forEach(item => { + DataForm({ type: 'float1', column: 'bod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'cod', item: item as unknown as Record }); + DataForm({ type: 'float1', column: 'ss', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'tn', item: item as unknown as Record }); + DataForm({ type: 'float3', column: 'tp', item: item as unknown as Record }); + }); + // 마지막 수질 데이터의 값을 gridListPlanInfo에 업데이트 if (sortedWaterQltyInfo.length > 0) { const lastWaterQltyInfo = sortedWaterQltyInfo[sortedWaterQltyInfo.length - 1]; @@ -818,7 +843,7 @@ export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActio plInflWqltSs: lastWaterQltyInfo?.plInflWqltSs === 'NULL' || lastWaterQltyInfo?.plInflWqltSs === undefined ? '' : lastWaterQltyInfo.plInflWqltSs, plInflWqltTn: lastWaterQltyInfo?.plInflWqltTn === 'NULL' || lastWaterQltyInfo?.plInflWqltTn === undefined ? '' : lastWaterQltyInfo.plInflWqltTn, plInflWqltTp: lastWaterQltyInfo?.plInflWqltTp === 'NULL' || lastWaterQltyInfo?.plInflWqltTp === undefined ? '' : lastWaterQltyInfo.plInflWqltTp - }] + }], } })); } else { diff --git a/apps/fo/app/store/rdi/index.tsx b/apps/fo/app/store/rdi/index.tsx index fd9ca52b6..6fe400ce9 100644 --- a/apps/fo/app/store/rdi/index.tsx +++ b/apps/fo/app/store/rdi/index.tsx @@ -1,6 +1,8 @@ +import { getFetch } from "@/app/components/api"; import DataForm from "@/app/components/common/DataForm"; import type { TableUpperProps } from "../dashboard"; -import { getFetch } from "@/app/components/api"; +import { fetchFacilityWaterQltyListHist } from "../facilityWaterQlty"; +import { calRainEffectDay } from "../underWaterInput"; export type { TableUpperProps }; /**==================================== * Store - 타입 @@ -19,7 +21,7 @@ export type RdiProps = { daddr : string, fctCpc : number, summerMinBodValue : number, - inflAmnt : number, + inflAmnt : number | string | '', winterAvgBodLowRainfall : number, rdiiValue : number, infoRnDay : number, @@ -27,6 +29,7 @@ export type RdiProps = { rvbsn : string, lgbsn : string, mdmbsn : string, + rainEffectDay : number, // 강우영향일 } export interface RdiInterface { @@ -78,7 +81,7 @@ export interface ExcelType { 주소: string, 시설용량: number, 하절기강우일최저유입BOD: number | '', - 하절기최저유입BOD유입유량: number | '', + 하절기최저유입BOD유입유량: number | string | '', 동절기청천시유입BOD: number | '', RDII추정 : number | '', RDII추정시일강수량 : number | '', @@ -86,7 +89,6 @@ export interface ExcelType { // 리스트 헤더 const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "행정구역", row:0, colSpan:2 }, { id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "sigungo", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 }, @@ -95,15 +97,14 @@ const upHeadList1:TableUpperProps[] = [ { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row:0, rowSpan:2 }, { id: "B", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 }, { id: "summerMinBodValue", title: "3mm 이상 강우일 중 최저 유입BOD\n(mg/L)", align:"right", row:1 }, - { id: "inflAmnt", title: "하절기최저유입BOD유입유량\n(m³/일)", row:1 }, + { id: "inflAmnt", title: "하절기최저유입BOD유입유량\n(m³/일)", row:1, align:"right" }, { id: "C", title: "동절기 운영현황(1~2월)", row:0 }, - { id: "winterAvgBodLowRainfall", title: "청천시 유입BOD\n(mg/L)", row:1 }, - { id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 }, - { id: "infoRnDay", title: "RDII추정시일강수량\n(mm)", row:0, rowSpan:2 }, + { id: "winterAvgBodLowRainfall", title: "청천시 유입BOD\n(mg/L)", row:1, align:"right" }, + { id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2, align:"right" }, + { id: "infoRnDay", title: "RDII추정시일강수량\n(mm)", row:0, rowSpan:2, align:"right" }, ]; const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 }, @@ -113,15 +114,14 @@ const upHeadList2:TableUpperProps[] = [ { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, { id: "B", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 }, { id: "summerMinBodValue", title: "하절기강우일최저유입BOD", align:"right", row:1 }, - { id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1 }, + { id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1, align:"right" }, { id: "D", title: "동절기 운영현황(1~2월)", row:0 }, - { id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1 }, - { id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 }, - { id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2 }, + { id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1, align:"right" }, + { id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2, align:"right" }, + { id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2, align:"right" }, ]; const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 }, @@ -135,11 +135,11 @@ const upHeadList3:TableUpperProps[] = [ { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, { id: "C", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 }, { id: "summerMinBodValue", title: "하절기강우일최저유입BOD", align:"right", row:1 }, - { id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1 }, + { id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1, align:"right" }, { id: "D", title: "동절기 운영현황(1~2월)", row:0 }, - { id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1 }, - { id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 }, - { id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2 }, + { id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1, align:"right" }, + { id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2, align:"right" }, + { id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2, align:"right" }, ]; const rdiList:RdiProps[] = [ @@ -206,11 +206,12 @@ export const rdiInitState: RdiType = { } const dataFormSetting=(item : RdiProps)=> { - DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record }); DataForm({ type: 'float1', column: 'summerMinBodValue', item: item as unknown as Record}); DataForm({ type: 'float1', column: 'winterAvgBodLowRainfall', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'rdiiValue', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'infoRnDay', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'rdiiValue', item: item as unknown as Record}); + DataForm({ type: 'float1', column: 'infoRnDay', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'inflAmnt', item: item as unknown as Record}); } // Add type definitions for state setter function @@ -238,10 +239,45 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio ...item, sido: item.lclgvrnmntNm.split(' ')[0]?.toString() || '', sigungo: item.lclgvrnmntNm.split(' ')[1]?.toString() || '', - inflAmnt: item.inflAmnt?item.inflAmnt.toFixed(1) : '' + inflAmnt: item.inflAmnt?Number(item.inflAmnt).toFixed(1): '' } }) + // 청천히 동절기 유입bod를 새로 구하기 위해서 재 호출 + for (const item of rtnData) { + const fcltyCd = item.bscFctCd; + let winterInflBod = 0; + + const res: any = await fetchFacilityWaterQltyListHist(searchYear, fcltyCd); + + if (res?.data) { + const rtnWqltData = res.data.wqltObjs; + rtnWqltData.forEach((dataItem: any) => { + dataItem.rainEffectDay = 0; + }); + + calRainEffectDay(rtnWqltData, 'SUNNY'); + + const rtnWqltWinterData = rtnWqltData.filter((dataItem: any) => + (dataItem.msrDt.substring(4, 6) === '01' || + dataItem.msrDt.substring(4, 6) === '02') && + dataItem.rainEffectDay == 0 + ); + + rtnWqltWinterData.forEach((dataItem: any) => { + winterInflBod += dataItem.inflAssctBfrWqltBod + ? Number(dataItem.inflAssctBfrWqltBod) + : 0; + }); + + if (rtnWqltWinterData.length > 0) { + winterInflBod = winterInflBod / rtnWqltWinterData.length; + } + } + + item.winterAvgBodLowRainfall = winterInflBod; + } + rtnData.forEach((item: RdiProps) => { dataFormSetting(item) }) diff --git a/apps/fo/app/store/sewageOccur/index.tsx b/apps/fo/app/store/sewageOccur/index.tsx index 7eec53913..4a6d055f0 100644 --- a/apps/fo/app/store/sewageOccur/index.tsx +++ b/apps/fo/app/store/sewageOccur/index.tsx @@ -1,3 +1,4 @@ +import DataForm from "@/app/components/common/DataForm"; import type { TableUpperProps } from "../dashboard"; import { getFetch } from "@/app/components/api"; export type { TableUpperProps }; @@ -59,7 +60,6 @@ export interface ExcelType { // 리스트 헤더 const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "행정구역", row:0, colSpan:2 }, { id: "sidoNm", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "sggNm", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 }, @@ -74,7 +74,6 @@ const upHeadList1:TableUpperProps[] = [ ]; const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 }, @@ -90,7 +89,6 @@ const upHeadList2:TableUpperProps[] = [ ]; const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 }, @@ -111,9 +109,9 @@ const upHeadList3:TableUpperProps[] = [ const upHeadListSewageOccurDetail:TableUpperProps[] = [ { id: "msrDt", title: "년월" }, - { id: "waterUsage", title: "물사용량(m³/일)" }, - { id: "swageChangePercent", title: "오수전환율(%)" }, - { id: "estimationSewage", title: "오수발생량 추정(m³/일)", row:0, rowSpan:2 }, + { id: "waterUsage", title: "물사용량(m³/일)", align:"right" }, + { id: "swageChangePercent", title: "오수전환율(%)", align:"right" }, + { id: "estimationSewage", title: "오수발생량 추정(m³/일)", row:0, rowSpan:2, align:"right" }, ]; const sewageOccurList:SewageOccurProps[] = [] @@ -129,7 +127,6 @@ const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); - console.log("res", res.data); return { data: res.data, count: res.count } /* @@ -229,7 +226,7 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - item.swageChangePercent = 80; + item.swageChangePercent = 90; const bscFctCd = item.bscFctCd; const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse; @@ -237,10 +234,7 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { let sumChungcunDay = 0; let chungcunSumFlow = 0; let totWaterUsage = 0; - - if(bscFctCd === '11200PB001R'){ - console.log('res2.data : ',res2.data); - } + // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) for(let idx = 0; idx < res2.data.length; idx++) { const item = res2.data[idx]; @@ -270,21 +264,17 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { for(let idx = 0; idx < res2.data.length; idx++) { if(res2.data[idx] && res2.data[idx]?.rainEffectDay === 1) { if(idx > 1){ - if(res2.data[idx-1]?.infoRnDay && Number(res2.data[idx-1]?.infoRnDay.toString().trim()) >= 3) { + if(res2.data[idx-1]?.infoRnDay && Number(res2.data[idx]?.infoRnDay) < 3 && Number(res2.data[idx-1]?.infoRnDay.toString().trim()) >= 3) { res2.data[idx]!.infoRnDay = res2.data[idx-1]!.infoRnDay; } } if(idx > 2){ - if(res2.data[idx-2]?.infoRnDay && Number(res2.data[idx-2]?.infoRnDay.toString().trim()) >= 3) { + if(res2.data[idx-2]?.infoRnDay && Number(res2.data[idx]?.infoRnDay) < 3 && Number(res2.data[idx-2]?.infoRnDay.toString().trim()) >= 3) { res2.data[idx]!.infoRnDay = res2.data[idx-2]!.infoRnDay; } } } } - - if(bscFctCd === '11200PB001R'){ - console.log('calData : ',res2.data); - } res2.data.forEach((item: FlowDataItem) => { @@ -303,6 +293,15 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { return item; })); + calData.forEach((item: SewageOccurProps) => { + DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'waterUsage', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'swageChangePercent', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'estimationSewage', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'sewageInputYear', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'sewageInputChungchun', item: item as unknown as Record }); + }); + if(pageSize>5000){ calData.forEach((item: SewageOccurProps) => { excelDownList.push({ @@ -312,8 +311,8 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { 중권역: item.mdmbsn, 환경청: item.rgnNm, 시설명: item.bscFctNm, - 시도: item.sidoNm, // sido ?? 타입에러 나서 수정함 - 시군구: item.sggNm, //sgg ?? + 시도: item.sidoNm, + 시군구: item.sggNm, 주소: item.daddr, 시설용량: item.fctCpc, 물사용량: item.waterUsage, @@ -345,7 +344,6 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { const monthData: SewageOccurDetailProps[] = []; let tot = 0, cnt= 0; - console.log('startYearMonth : ', startYearMonth, 'endYearMonth : ', endYearMonth); if(startYearMonth) { res.data = res.data.filter((item: FlowDataItem) => { const month = item.msrDt?.substring(0, 6); @@ -373,7 +371,7 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { tot += Number(item.inflAmnt) cnt++ }) - console.log('tot', tot, cnt, tot/cnt, tot/cnt*0.65); + const tableData:modalTableType[] = [] let maxWaterUsage = 0; @@ -383,9 +381,9 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { item.usege = item.sum/item.cnt *0.65 tableData.push({ msrDt: item.yr+'-'+(index+1>=10?String(index+1):'0'+(index+1)), - waterUsage: Math.round(item.usege*100) /100, - swageChangePercent: (Math.round((item.usege/item.usege*0.8) * 100) / 100) * 100, - estimationSewage: Math.round(item.usege*0.8 * 100) / 100 + waterUsage: Math.round(item.usege*1) /1, + swageChangePercent: (Math.round((item.usege/item.usege*0.9) * 100) / 100) * 100, + estimationSewage: Math.round(item.usege*0.9 * 1) / 1 }) if (item.usege > maxWaterUsage) { maxWaterUsage = item.usege; @@ -401,21 +399,21 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { const newData= [ { msrDt: '평균', - waterUsage: Math.round(avgWaterUsage*100) /100, - swageChangePercent: 80, - estimationSewage: Math.round(avgWaterUsage*0.8*100) /100, + waterUsage: Math.round(avgWaterUsage*1) /1, + swageChangePercent: 90, + estimationSewage: Math.round(avgWaterUsage*0.9*1) /1, }, { msrDt: '최대', waterUsage: Math.round(maxWaterUsage*100) /100, - swageChangePercent: 80, - estimationSewage: Math.round(maxWaterUsage*0.8*100) /100, + swageChangePercent: 90, + estimationSewage: Math.round(maxWaterUsage*0.9*1) /1, }, { msrDt: '최소', waterUsage: Math.round(minWaterUsage*100) /100, - swageChangePercent: 80, - estimationSewage: Math.round(minWaterUsage*0.8*100) /100, + swageChangePercent: 90, + estimationSewage: Math.round(minWaterUsage*0.9*1) /1, }, ...tableData ] diff --git a/apps/fo/app/store/underWaterInput/index.tsx b/apps/fo/app/store/underWaterInput/index.tsx index 589c35962..e9c664b99 100644 --- a/apps/fo/app/store/underWaterInput/index.tsx +++ b/apps/fo/app/store/underWaterInput/index.tsx @@ -1,6 +1,6 @@ +import { getFetch } from "@/app/components/api"; import DataForm from "@/app/components/common/DataForm"; import type { TableUpperProps } from "../dashboard"; -import { getFetch } from "@/app/components/api"; import type { ExcelType, UnderWaterInputModalProps, UnderWaterProps } from "./types"; export type { TableUpperProps }; @@ -32,7 +32,6 @@ const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시 // 리스트 헤더 const upHeadList1:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "행정구역", row:0, colSpan:2 }, { id: "sidoNm", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "sggNm", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 }, @@ -46,7 +45,6 @@ const upHeadList1:TableUpperProps[] = [ ]; const upHeadList2:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 }, @@ -61,7 +59,6 @@ const upHeadList2:TableUpperProps[] = [ ]; const upHeadList3:TableUpperProps[] = [ - { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 }, { id: "A", title: "유역", row:0, colSpan:3 }, { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 }, { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 }, @@ -82,9 +79,9 @@ const upHeadList3:TableUpperProps[] = [ // 모달 const upHeadListUnderWaterDetail:TableUpperProps[] = [ { id: "msrDt", title: "날짜" }, - { id: "realSewageInput", title: "청천시 실제 하수유입량\n(m³/일)" }, - { id: "sewageOccur", title: "오수발생량\n(m³/일)" }, - { id: "underWaterInput", title: "지하수 유입량\n(m³/일)" }, + { id: "realSewageInput", title: "청천시 실제 하수유입량\n(m³/일)", align:"right" }, + { id: "sewageOccur", title: "오수발생량\n(m³/일)", align:"right" }, + { id: "underWaterInput", title: "지하수 유입량\n(m³/일)", align:"right" }, ]; const underWaterList:UnderWaterProps[] = [ @@ -201,7 +198,7 @@ const fetchUnderWaterList = async (pageNo: number, pageSize: number, searchYear: ====================================*/ export type UnderWaterActions = { searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; - unserWaterModal:(bscFctCd:string, searchYear:string)=>void + unserWaterModal:(bscFctCd:string, searchYear:string, startYm:string, endYm:string)=>void } export type UnderWaterType = { @@ -253,15 +250,21 @@ export const underWaterInitState: UnderWaterType = { } const dataFormSetting=(item : UnderWaterProps)=> { - DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record }); - DataForm({ type: 'float1', column: 'underWaterInputYear', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'underWaterInputSummer', item: item as unknown as Record}); - DataForm({ type: 'float2', column: 'underWaterInputWinter', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'underWaterInputYear', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'underWaterInputSummer', item: item as unknown as Record}); + DataForm({ type: 'float0', column: 'underWaterInputWinter', item: item as unknown as Record}); } export type UnderWaterStore = UnderWaterType & UnderWaterActions; -export const calRainEffectDay = (data: FlowDataItem[], weather: string) => { +/** + * + * @param data + * @param weather ('ALL' | 'SUNNY' | 'RAINY') + * @returns + */ +export const calRainEffectDay = (data: any, weather: string) => { // 강우영향일 계산 (강우량이 3 이상이면 무조건 강우영향일) for(let idx = 0; idx < data.length; idx++) { const item = data[idx]; @@ -291,12 +294,12 @@ export const calRainEffectDay = (data: FlowDataItem[], weather: string) => { for(let idx = 0; idx < data.length; idx++) { if(data[idx] && data[idx]?.rainEffectDay === 1) { if(idx > 1){ - if(data[idx-1]?.infoRnDay && Number(data[idx-1]?.infoRnDay.toString().trim()) >= 3) { + if(data[idx-1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-1]?.infoRnDay.toString().trim()) >= 3) { data[idx]!.infoRnDay = data[idx-1]!.infoRnDay; } } if(idx > 2){ - if(data[idx-2]?.infoRnDay && Number(data[idx-2]?.infoRnDay.toString().trim()) >= 3) { + if(data[idx-2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-2]?.infoRnDay.toString().trim()) >= 3) { data[idx]!.infoRnDay = data[idx-2]!.infoRnDay; } } @@ -359,9 +362,14 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy const summerUsage = sumSummerChungcunFlow / sumSummerChungcunDay; const winterUsage = sumWinterChungcunFlow / sumWinterChungcunDay; - item.underWaterInputYear = Math.round((usage-(usage*0.65*0.8)) * 100) / 100; - item.underWaterInputSummer = Math.round((summerUsage-(summerUsage*0.65*0.8)) * 100) / 100; - item.underWaterInputWinter = Math.round((winterUsage-(winterUsage*0.65*0.8)) * 100) / 100; + if(bscFctCd === '11200PB001R') { + console.log('calData.length : ', calData.length); + console.log('usage : ', usage, 'summerUsage : ', summerUsage, 'winterUsage : ', winterUsage); + } + + item.underWaterInputYear = Math.round((usage-(usage*0.65*0.9)) * 1) / 1; + item.underWaterInputSummer = Math.round((summerUsage-(summerUsage*0.65*0.9)) * 1) / 1; + item.underWaterInputWinter = Math.round((winterUsage-(winterUsage*0.65*0.9)) * 1) / 1; return item; })); @@ -412,25 +420,19 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy }, })) }, - unserWaterModal:async (bscFctCd, searchYear)=>{ - console.log('searchYear : ', searchYear); + + unserWaterModal:async (bscFctCd, searchYear, startYm, endYm)=>{ const res = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear); const monthData: SewageOccurDetailProps[] = []; const calData = calRainEffectDay(res.data, 'SUNNY'); - let tot = 0, cnt= 0; - - let tmpTot = 0; - - calData.forEach((item: FlowDataItem) => { - tmpTot += Number(item.inflAmnt); - }) - console.log('tmpTot : ', tmpTot/calData.length); + let tot = 0; + let cnt = 0; calData.forEach((item: FlowDataItem) => { const month = item.msrDt?.substring(4, 6); const monthIdx = month ? Number(month) - 1 : -1; - if (monthIdx >= 0) { + if (monthIdx >= 0 && (monthIdx>=Number(startYm.substring(4,6))-1 && monthIdx <= Number(endYm.substring(4, 6))-1)) { if (typeof monthData[monthIdx] === 'object') { monthData[monthIdx].sum += Number(item.inflAmnt); monthData[monthIdx].cnt++; @@ -438,9 +440,12 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy monthData[monthIdx] = {cnt:1, sum:Number(item.inflAmnt), yr:item.msrDt.substring(0,4), usege:0}; } } - tot += Number(item.inflAmnt) - cnt++ - }) + + if(month >= startYm.substring(4, 6) && month <= endYm.substring(4, 6)){ + cnt++; + tot += item.inflAmnt && item.inflAmnt !== 'x' ? Number(item.inflAmnt.toString().trim()) : 0; + } + }); const tableData:UnderWaterInputModalProps[] = [] let maxWaterUsage = 0; @@ -450,9 +455,9 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy item.usege = item.sum/item.cnt tableData.push({ msrDt: item.yr+'-'+(index+1>=10?String(index+1):'0'+(index+1)), - realSewageInput:Math.round(item.usege*100) /100, - sewageOccur:Math.round(item.usege*0.65*0.8 * 100) / 100, - underWaterInput:Math.round((item.usege-(item.usege*0.65*0.8)) * 100) / 100, + realSewageInput:Math.round(item.usege*1) /1, + sewageOccur:Math.round(item.usege*0.65*0.9 * 1) / 1, + underWaterInput:Math.round((item.usege-(item.usege*0.65*0.9)) * 1) / 1, }) if (item.usege > maxWaterUsage) { maxWaterUsage = item.usege; @@ -463,25 +468,28 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy } }); - avgWaterUsage = tot/cnt + console.log('tot', tot, 'calData.length', calData.length); + avgWaterUsage = tot/cnt; + console.log(avgWaterUsage); const newData= [ { msrDt: '평균', - realSewageInput:Math.round(avgWaterUsage*100) /100, - sewageOccur:Math.round(avgWaterUsage*0.65*0.8 * 100) / 100, - underWaterInput:Math.round((avgWaterUsage-(avgWaterUsage*0.65*0.8)) * 100) / 100, + realSewageInput:Math.round(avgWaterUsage*1) /1, + sewageOccur:Math.round(avgWaterUsage*0.65*0.9 * 1) / 1, + underWaterInput: Math.round((avgWaterUsage-(avgWaterUsage*0.65*0.9)) * 1) / 1 + //underWaterInput:Math.round((avgWaterUsage-(avgWaterUsage*0.65*0.8)) * 100) / 100, }, { msrDt: '최대', - realSewageInput: Math.round(maxWaterUsage*100) /100, - sewageOccur: Math.round(maxWaterUsage*0.65*0.8 * 100) / 100, - underWaterInput: Math.round((maxWaterUsage-(maxWaterUsage*0.65*0.8)) * 100) / 100, + realSewageInput: Math.round(maxWaterUsage*1) /1, + sewageOccur: Math.round(maxWaterUsage*0.65*0.9 * 1) / 1, + underWaterInput: Math.round((maxWaterUsage-(maxWaterUsage*0.65*0.9)) * 1) / 1, }, { msrDt: '최소', - realSewageInput: Math.round(minWaterUsage*100) /100, - sewageOccur: Math.round(minWaterUsage*0.65*0.8 * 100) / 100, - underWaterInput: Math.round((minWaterUsage-(minWaterUsage*0.65*0.8)) * 100) / 100, + realSewageInput: Math.round(minWaterUsage*1) /1, + sewageOccur: Math.round(minWaterUsage*0.65*0.9 * 1) / 1, + underWaterInput: Math.round((minWaterUsage-(minWaterUsage*0.65*0.9)) * 1) / 1, }, ...tableData ] diff --git a/apps/fo/eslint.config.mjs b/apps/fo/eslint.config.mjs index 29e63f653..f477f55e1 100644 --- a/apps/fo/eslint.config.mjs +++ b/apps/fo/eslint.config.mjs @@ -4,6 +4,7 @@ import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const isProd = process.env.NODE_ENV === "production"; const compat = new FlatCompat({ baseDirectory: __dirname, @@ -14,6 +15,7 @@ const eslintConfig = [ {rules:{ // other rules.. "@typescript-eslint/no-explicit-any": "off", + ...(isProd ? { "react-hooks/exhaustive-deps": "off" } : {}), }}, ]; diff --git a/apps/sn/.env.development b/apps/sn/.env.development index 0fffc4f89..d137c4f9f 100644 --- a/apps/sn/.env.development +++ b/apps/sn/.env.development @@ -1,15 +1,13 @@ NEXT_PUBLIC_SITE_ID = SN -NEXT_PUBLIC_PORT = 11302 -NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11302 -NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082 -NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 -NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver -# NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver -NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver -NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com NEXT_PUBLIC_CLAIM_NAME = Authorization NEXT_PUBLIC_AUTH_USER_ID = token-id NEXT_PUBLIC_ACCESS_TOKEN = access-token NEXT_PUBLIC_REFRESH_TOKEN = refresh-token +NEXT_PUBLIC_PORT = 11302 +NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11302 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 +NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver +NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver +NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api \ No newline at end of file diff --git a/apps/sn/.env.local b/apps/sn/.env.local index 35701bd81..375f25fd5 100644 --- a/apps/sn/.env.local +++ b/apps/sn/.env.local @@ -1,16 +1,14 @@ NEXT_PUBLIC_SITE_ID = SN -NEXT_PUBLIC_PORT = 3002 -NEXT_PUBLIC_PROXY_HOST = http://localhost:3002 -NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082 -NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 -NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver -# NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver -NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver -NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com NEXT_PUBLIC_CLAIM_NAME = Authorization NEXT_PUBLIC_AUTH_USER_ID = token-id NEXT_PUBLIC_ACCESS_TOKEN = access-token NEXT_PUBLIC_REFRESH_TOKEN = refresh-token +NEXT_PUBLIC_PORT = 3002 +NEXT_PUBLIC_PROXY_HOST = http://localhost:3002 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 +NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver +NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver +NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api #NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api diff --git a/apps/sn/.env.production b/apps/sn/.env.production index 3fb05262e..805498398 100644 --- a/apps/sn/.env.production +++ b/apps/sn/.env.production @@ -6,10 +6,8 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token NEXT_PUBLIC_PORT = 3000 NEXT_PUBLIC_PROXY_HOST = https://sn.hasuon.or.kr NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000 -NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082 -NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver -# NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver +NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver NEXT_PUBLIC_LX_GEOSERVER_URL = http://172.29.143.87:8080/geoserver_swl -NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com +NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000 NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC -NEXT_PUBLIC_SEWERAGE_API_URL = http://con.mind-one.co.kr:11083/sewerage-service/api +NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api 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/codeMapper.ts b/apps/sn/app/(map-layout)/map/components/common/codeMapper.ts index 61a11c8a8..d1e777242 100644 --- a/apps/sn/app/(map-layout)/map/components/common/codeMapper.ts +++ b/apps/sn/app/(map-layout)/map/components/common/codeMapper.ts @@ -43,7 +43,7 @@ export const codeMapper = ({ layerName, data }: CodeMapperProps): any => { // usg_cd 값 변환 (용도코드) if (processedData.usg_cd) { const usageCodeItem = MS_SWAGE_MHL_USE_CODES.find( - code => code.val === processedData.usg_cd.trim() + code => code.val.trim() === processedData.usg_cd.trim() ); if (usageCodeItem) { processedData.usg_cd = usageCodeItem.text; 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..a16e68013 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: '지형지물부호코드' }, @@ -51,6 +55,21 @@ export const MsSwagePplCol2 = [ // { id: 'usgCd', title: '용도코드' }, // { id: 'lyrTypeNm', title: '레이어타입명' }, ] +export const MsSwagePplExcelCol = { + 'fcltMngNo': '시설 관리번호', + 'instMngNo': '기관 관리번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'swtrmt_zone_nm': '처리구역', + 'instlYmd': '설치일자', + 'usgCd': '용도', + 'pdmtVl': '관경(mm)', + 'prlgVl': '연장(m)', + 'tmptDpth': '시점 깊이(m)', + 'tmptMhlMngNo': '시점맨홀 관리번호', + 'scendDpth': '종점 깊이(m)', + 'scendMhlMngNo': '종점맨홀 관리번호', +}; // 하수관로 용도 코드표 export const MS_SWAGE_PPL_USE_CODES = [ { val: "SBA000", text: "미분류" }, @@ -335,6 +354,18 @@ export const SwtrmtFcltCol2 = [ // { id: 'bsnNm', title: '유역명' }, // { id: 'obsrvnSptCd', title: '관측지점코드' }, ]; +export const SwtrmtFcltExcelCol = { + 'swtrmtFcltNm': '하수처리시설명', + 'fcltCpct': '시설 용량(㎥/d)', + 'rgnExpln': '지역설명', + 'oprtngStrtYmd': '가동개시 일자', + 'laeNm': '지자체명', + 'sareaNm': '권역명', + 'rvrNm': '하천명', + 'dschgWtstNm': '방류수계명', + 'swtrmtPpltnCnt': '하수 처리 인구수(인)', + 'rprsMsrcCn': '대표 공법 내용' +}; // 하수처리구역 export const SwtrmtPrcsZoneCol = [ @@ -358,6 +389,12 @@ export const SwtrmtPrcsZoneCol2 = [ { id: 'sggNm', title: '시군구' }, { id: 'swtrmtFcltNm', title: '하수처리시설명' }, ]; +export const SwtrmtPrcsZoneExcelCol = { + 'swtrmtZoneNm': '하수처리구역명', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'swtrmtFcltNm': '하수처리시설명' +}; // 하수맨홀 export const MsSwageMhlCol = [ @@ -388,16 +425,18 @@ 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: '관리기관코드' }, { id: 'instlYmd', title: '설치일자' }, // { id: 'lastDrdgnYmd', title: '최종준설일자' }, - // { id: 'usgCd', title: '용도코드' }, + { id: 'usgCd', title: '용도' }, // { id: 'fcltyShpCd', title: '시설물형태코드' }, // { id: 'mhlKndCd', title: '맨홀종류코드' }, { id: 'swageMhlDiamVl', title: '하수맨홀구경(mm)' }, - { id: 'mqltCd', title: '재질코드' }, + // { id: 'mqltCd', title: '재질코드' }, { id: 'wdthLen', title: '가로길이(m)' }, { id: 'vrtcLen', title: '세로길이(m)' }, // { id: 'angDir', title: '표고' }, @@ -405,6 +444,18 @@ export const MsSwageMhlCol2 = [ { id: 'dpthlVl', title: '심도(m)' }, // { id: 'instMngNo', title: '기관관리번호' } ]; +export const MsSwageMhlExcelCol = { + 'fcltMngNo': '시설관리번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'instlYmd': '설치일자', + 'usgCd': '용도', + 'swageMhlDiamVl': '하수맨홀구경(mm)', + 'wdthLen': '가로길이(m)', + 'vrtcLen': '세로길이(m)', + 'altdVl': '표고(EL.m)', + 'dpthlVl': '심도(m)' +}; // 하수맨홀 뚜껑재질 코드표 export const MS_SWAGE_MHL_CODES = [ { val: "SBC000", text: "미분류" }, @@ -442,31 +493,31 @@ export const MS_SWAGE_MHL_CODES = [ ]; // 하수맨홀 용도 코드표 export const MS_SWAGE_MHL_USE_CODES = [ - { val: "SMU000", text: "미분류" }, - { val: "SMU001", text: "우수" }, - { val: "SMU002", text: "오수" }, - { val: "SMU003", text: "집수" }, - { val: "SMU004", text: "합류" }, - { val: "SMU005", text: "차집" }, - { val: "SMU999", text: "기타" }, - { val: "SMU900", text: "1호" }, - { val: "SMU901", text: "2호" }, - { val: "SMU902", text: "3호" }, - { val: "SMU903", text: "4호" }, - { val: "SMU904", text: "5호" }, - { val: "SMU905", text: "6호" }, - { val: "SMU906", text: "7호" }, - { val: "SMU907", text: "8호" }, - { val: "SMU908", text: "구거용맨홀" }, - { val: "SMU909", text: "부관부착맨홀" }, - { val: "SMU910", text: "조립식맨홀" }, - { val: "SMU911", text: "특1호" }, - { val: "SMU912", text: "특2호" }, - { val: "SMU913", text: "특3호" }, - { val: "SMU914", text: "특4호" }, - { val: "SMU915", text: "특5호" }, - { val: "SMU916", text: "하수맨홀용도" }, - { val: "SMU917", text: "현장타설관거용" } + { val: "SMU000 ", text: "미분류" }, + { val: "SMU001 ", text: "우수" }, + { val: "SMU002 ", text: "오수" }, + { val: "SMU003 ", text: "집수" }, + { val: "SMU004 ", text: "합류" }, + { val: "SMU005 ", text: "차집" }, + { val: "SMU999 ", text: "기타" }, + { val: "SMU900 ", text: "1호" }, + { val: "SMU901 ", text: "2호" }, + { val: "SMU902 ", text: "3호" }, + { val: "SMU903 ", text: "4호" }, + { val: "SMU904 ", text: "5호" }, + { val: "SMU905 ", text: "6호" }, + { val: "SMU906 ", text: "7호" }, + { val: "SMU907 ", text: "8호" }, + { val: "SMU908 ", text: "구거용맨홀" }, + { val: "SMU909 ", text: "부관부착맨홀" }, + { val: "SMU910 ", text: "조립식맨홀" }, + { val: "SMU911 ", text: "특1호" }, + { val: "SMU912 ", text: "특2호" }, + { val: "SMU913 ", text: "특3호" }, + { val: "SMU914 ", text: "특4호" }, + { val: "SMU915 ", text: "특5호" }, + { val: "SMU916 ", text: "하수맨홀용도" }, + { val: "SMU917 ", text: "현장타설관거용" } ]; // 우수펌프장 @@ -496,12 +547,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: '일일처리용량(㎥)' }, @@ -511,6 +564,18 @@ export const ExclnSewagePumpCol2 = [ { id: 'exclnWpmpAbltVl', title: '우수양수능력(m²/d)' }, // { id: 'instMngNo', title: '기관관리번호' }, ] +export const ExclnSewagePumpExcelCol = { + 'fcltMngNo': '시설관리번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'swagePmstnNm': '하수펌프장명', + 'instlYmd': '설치일자', + 'dailyPrcsCpct': '일일처리용량(㎥)', + 'maxImpndCpct': '최대저수용량(㎥)', + 'altdVl': '표고(EL.m)', + 'wtlvVl': '수위(m)', + 'exclnWpmpAbltVl': '우수양수능력(m²/d)' +}; // 오수펌프장 export const DrtwSewagePumpCol = [ // { id: 'fid', title: 'FID' }, @@ -539,11 +604,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 +652,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: '시설물형태코드' }, @@ -594,6 +663,16 @@ export const stofcbrCol2 = [ { id: 'swageMhlMngNo', title: '하수맨홀관리번호' }, { id: 'drtnAngl', title: '방향각도(°)' }, ] +export const stofcbrExcelCol = { + 'fcltMngNo': '시설관리번호', + 'mapshtNo': '도엽번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'instlYmd': '설치일자', + 'worflwWallHgt': '월류벽높이(m)', + 'swageMhlMngNo': '하수맨홀관리번호', + 'drtnAngl': '방향각도(°)' +}; // 중점우수토실 측정지점 export const untrtSwgRainCol1 = [ @@ -657,7 +736,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/components/popupComponent/featureClickEventPopup.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx index 85f614918..24f9a3f8d 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx @@ -275,7 +275,6 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, // API에서 상세 데이터 가져오기 if (apiEndpoint) { try { - console.log(`API 호출: ${apiEndpoint}`); const result = await getData(apiEndpoint); if (result) { @@ -386,7 +385,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, const ContentComponent = layerNm ? getLayerContentComponent(layerNm) : null; return ( -
@@ -672,12 +1131,15 @@ const SearchModal: React.FC = ({ } tableData={detailTableData} headlist={dynamicHeadlist2} - pageSize={5} + pageSize={OPERATION_TABLE_SIZE} total={detailTableCount} cellClick={undefined} onPageChange={setDetailCurPage} resetPageNo={isResetDetailPage} resetSelection={isResetDetailSelection} + headButton={ + + } />
@@ -687,20 +1149,23 @@ const SearchModal: React.FC = ({ ); return ( - + + body={renderBody()} + useDirectRender={true} + /> + + ); }; diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts b/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts index 4a5df24a1..33d9e3fed 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { getDcData } from '@/components/lib/api'; - +import { create } from 'zustand'; export type Folder = { clsfSystmSn: string; @@ -16,13 +16,14 @@ export type SelectDataType = { val: string; sido?: string; // 시도 코드 (선택 사항) } -type ReportDataFile = { +export type ReportDataFile = { no: number; fileTitle: string; regDt: string; fileSn: string; filePath: string; } + const isYear = (value: string): boolean => { return /^[0-9]{4}년$/.test(value); }; @@ -107,23 +108,53 @@ export const useDcTree = (clsfSystmNm: string) => { return data; }; -export const getDcFile = async (clsfSystmSn: string) => { +export const getDcFiles = async (clsfSystmSn: string) : Promise => { + if (!clsfSystmSn) { + console.error("clsfSystmSn is required to get files."); + return []; + } const queryString = 'clsfSystmSn=' + clsfSystmSn; const result = await getDcData(`/api/v1/dc/file${queryString ? `?${queryString}` : ''}`); return result; }; + +export const useDcFiles = (clsfSystmNm: string) => { + const [data, setData] = useState([]); + + useEffect(() => { + const fetch = async () => { + const result = await getDcFiles(clsfSystmNm); + if (result) { + setData(result); + } + console.log("useDcFiles 호출"); + console.log("clsfSystmNm:", clsfSystmNm, "data:", result); + }; + fetch(); + }, [clsfSystmNm]); + + return data; +}; + +// 파일 이름 추가 export const addFileName = async (data: FlattenedFolder[]) => { const fileReportData = await Promise.all( data.map(async (item) => { - const filenameArr: ReportDataFile[] = await getDcFile(String(item.clsfSystmSn)); - const f = filenameArr[0]; - const result = { ...item, fileName: f?.fileTitle, regDate: f?.regDt, fileSn: f?.fileSn, filePath: f?.filePath }; - + const fileArr: ReportDataFile[] = await getDcFiles(String(item.clsfSystmSn)); + let result:FlattenedFolder; + if (fileArr.length > 0) { + const f = fileArr[0]; + const fCount = fileArr.length; + result = { ...item, fileName: f?.fileTitle+" 외 "+fCount+"건", regDate: f?.regDt, fileSn: f?.fileSn, filePath: f?.filePath }; + } else { + result = { ...item, fileName: "", regDate: "", fileSn: "", filePath: "" }; + } return result; }) ); return fileReportData; }; + export const getDcFileprcs = async (fileSn: string) => { const queryString = 'fileSn=' + fileSn; const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`); @@ -157,3 +188,11 @@ export const getFileDownload = async (fileSn: string, fileName: string, filePath fileDownload(blobData, fileName); } +interface ModalStore { + isOpen: boolean; + setIsOpen: (open: boolean) => void; +} +export const useReportFileModalStore = create((set) => ({ + isOpen: false, + setIsOpen: (open: boolean) => set({ isOpen: open }), +})); 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..1c578a3e6 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} + /> {/* 설치년도 */} = ({ {/* 용도 */} { setVar1(String(val)); diff --git a/apps/sn/app/(map-layout)/map/searchModal/swagePmstn.tsx b/apps/sn/app/(map-layout)/map/searchModal/swagePmstn.tsx index ee3886e90..6f54f5793 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/swagePmstn.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/swagePmstn.tsx @@ -3,17 +3,22 @@ import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox'; import { Button, Input, Label } from '@common/components'; import React from 'react'; +import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; interface SwagePmstnProps { start1: string; start2: string; end1: string; end2: string; + city: string; + sgg: string; yearFilter: { start: string; end: string }; setStart1: (value: string) => 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..e501a9153 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} + /> {/* 설치년도 */} = ({ {/* 용도 */} { setVar1(String(val)); @@ -73,7 +91,7 @@ const SwagePpl: React.FC = ({ 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..6af066e4d 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 = true; return ( <> @@ -393,6 +408,15 @@ const IrSetting = () => { startYear={1900} // 시작 연도 설정 /> + + 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/YearRangeSelectBox.tsx b/apps/sn/app/components/ui/YearRangeSelectBox.tsx index 83b21cba5..8450a2b60 100644 --- a/apps/sn/app/components/ui/YearRangeSelectBox.tsx +++ b/apps/sn/app/components/ui/YearRangeSelectBox.tsx @@ -72,7 +72,7 @@ export function YearRangeSelectBox({ (maxYear - i).toString() ).filter(y => !end || parseInt(y) <= parseInt(end)); return [ - { val: "0", text: "년" }, + { val: "0", text: "전체" }, ...years.map(y => ({ val: y, text: y })), ]; }; @@ -82,7 +82,7 @@ export function YearRangeSelectBox({ (maxYear - i).toString() ).filter(y => !start || parseInt(y) >= parseInt(start)); return [ - { val: "9999", text: "년" }, + { val: "9999", text: "전체" }, ...years.map(y => ({ val: y, text: y })), ]; }; diff --git a/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx b/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx index a7472d4c0..1a800fb35 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,11 +43,13 @@ 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(""); @@ -109,18 +111,14 @@ 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]" @@ -130,17 +128,11 @@ export function CtpvSggPrcsFcltSelectBox({ className="w-48" selectClass="w-48 rounded-lg bg-white truncate" label="" - selectArray={ - [defaultOption, - ...sgg - .filter(opt => 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 +147,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/components/ui/verticalTable.tsx b/apps/sn/app/components/ui/verticalTable.tsx index ee13405fb..14c61bec8 100644 --- a/apps/sn/app/components/ui/verticalTable.tsx +++ b/apps/sn/app/components/ui/verticalTable.tsx @@ -1,6 +1,8 @@ import UiPageNations from "@common/business_components/ui/table/uiPageNations"; import { Table, TableBody, TableCell, TableRow, Button } from "@common/components"; import { useEffect, useState } from "react"; +import { confirmToast, showConfirmToast } from '@/components/ui/toast' +import { toast, Toaster } from "react-hot-toast"; type UiTableProps = { headName: React.ReactNode | undefined; @@ -38,6 +40,9 @@ type UiHeadProps = { align?: string; type?: string; options?: string[]; + placeholder?: string; + isNumber?: boolean; + length?: number; }; export function VerticalTable({ @@ -62,6 +67,8 @@ export function VerticalTable({ }: UiTableProps) { const [cur, setCur] = useState(1); const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({}); + const [errors, setErrors] = useState<{ [key: string]: string }>({}); + useEffect(() => { if (resetPageNo) { setCur(1); @@ -71,32 +78,44 @@ export function VerticalTable({ useEffect(() => { if (tableData.length > 0 && tableData[0]) { - // 깊은 복사를 통해 새로운 객체 생성 const newData = JSON.parse(JSON.stringify(tableData[0])); setFormData(newData); - console.log("Form data initialized:", newData); // 디버깅용 + setErrors({}); } }, [tableData]); const handleInputChange = (id: string, value: string | number | boolean) => { - console.log(`Input change: ${id} = ${value}`); // 디버깅용 const newFormData = { ...formData, [id]: value, }; - console.log("New form data:", newFormData); // 디버깅용 setFormData(newFormData); - // 폼 데이터가 변경될 때마다 부모에게 알림 + if (errors[id]) { + setErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[id]; + return newErrors; + }); + } + if (onFormDataChange) { onFormDataChange(newFormData); } }; + const handlePageChange = (page: number) => { setCur(page); onPageChange(page); }; + const showError = (fieldId: string, message: string) => { + setErrors(prev => ({ + ...prev, + [fieldId]: message + })); + }; + return ( <> {headName ? ( @@ -141,13 +160,55 @@ export function VerticalTable({ ))} ) : head.type === "input" ? ( - handleInputChange(head.id, e.target.value)} - className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" - /> +
+ { + if (head.isNumber) { + const newValue = e.target.value; + + if (newValue === "") { + handleInputChange(head.id, newValue); + return; + } + + const isValidNumber = /^[0-9]+$/.test(newValue); + if (!isValidNumber) { + showError(head.id, `${head.title}는 숫자만 입력 가능합니다.`); + return; + } + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + const numericValue = parseInt(newValue); + if (isNaN(numericValue) || numericValue < 0) return; + + handleInputChange(head.id, numericValue); + } else { + const newValue = e.target.value; + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + handleInputChange(head.id, e.target.value); + } + }} + className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" + placeholder={head.placeholder} + /> + {errors[head.id] && ( +
+ {errors[head.id]} +
+ )} +
) : ( {formData[head.id] ?? "-"} ) @@ -188,13 +249,55 @@ export function VerticalTable({ ))} ) : head.type === "input" ? ( - handleInputChange(head.id, e.target.value)} - className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" - /> +
+ { + if (head.isNumber) { + const newValue = e.target.value; + + if (newValue === "") { + handleInputChange(head.id, newValue); + return; + } + + const isValidNumber = /^[0-9]+$/.test(newValue); + if (!isValidNumber) { + showError(head.id, `${head.title}는 숫자만 입력 가능합니다.`); + return; + } + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + const numericValue = parseInt(newValue); + if (isNaN(numericValue) || numericValue < 0) return; + + handleInputChange(head.id, numericValue); + } else { + const newValue = e.target.value; + + if (head.length && newValue.length > head.length) { + showError(head.id, `${head.title}는 최대 ${head.length}자까지 입력 가능합니다.`); + return; + } + + handleInputChange(head.id, e.target.value); + } + }} + className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" + placeholder={head.placeholder} + /> + {errors[head.id] && ( +
+ {errors[head.id]} +
+ )} +
) : ( {formData[head.id] ?? "-"} ) @@ -216,6 +319,7 @@ export function VerticalTable({ /> )}
+ ); } \ No newline at end of file diff --git a/apps/sn/app/store/index.ts b/apps/sn/app/store/index.ts index 40e094480..28afbd570 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,69 @@ 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:any, b:any) => { + // 커스텀 순서 정의 + const customOrder = [ + "서울특별시", + "부산광역시", + "대구광역시", + "인천광역시", + "광주광역시", + "대전광역시", + "울산광역시", + "세종특별자치시", + "경기도", + "강원특별자치도", // 경기도 다음에 배치 + "충청북도", + "충청남도", + "전북특별자치도", // 충청남도 다음에 배치 + "전라남도", + "경상북도", + "경상남도", + "제주특별자치도" + ]; - set({ ctpv: ctpvList, sgg: sggList }); - } catch (e: any) { - // alert("시도, 시군구 데이터 로드 실패: " + e.message); - console.error("CTPV 데이터 로드 실패:", e); - } + const indexA = customOrder.indexOf(a.text); + const indexB = customOrder.indexOf(b.text); + + // 둘 다 커스텀 순서에 있으면 해당 순서로 + if (indexA !== -1 && indexB !== -1) { + return indexA - indexB; + } + + // 하나만 커스텀 순서에 있으면 그것을 앞으로 + if (indexA !== -1) return -1; + if (indexB !== -1) return 1; + + // 둘 다 커스텀 순서에 없으면 기본 알파벳 순서 + return a.text.localeCompare(b.text, 'ko'); + }); + + 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:any, b:any) => a.text.localeCompare(b.text, 'ko')); + + set({ sgg: list }); }; export const fetchBIZ_SE_CD = async (set: (state: any) => void) => { @@ -48,7 +87,10 @@ export const fetchBIZ_SE_CD = async (set: (state: any) => void) => { export const fetchBIZ_TY_CD = async (set: (state: any) => void) => { try { const res = await getCmsData(`/api/v1/code/cache/BIZTYCD`); - set({ BIZ_TY_CD: res }); + const filtered: BIZ_CD_TYPE[] = Array.isArray(res) + ? (res as BIZ_CD_TYPE[]).filter(item => item?.lwpoCd !== "AQAAA") + : []; + set({ BIZ_TY_CD: filtered }); } catch (e: any) { // alert("BIZ_TY_CD 데이터 로드 실패: " + e.message); console.error("BIZ_TY_CD 데이터 로드 실패:", e); 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/apps/sn/eslint.config.mjs b/apps/sn/eslint.config.mjs index c85fb67c4..5524ee038 100644 --- a/apps/sn/eslint.config.mjs +++ b/apps/sn/eslint.config.mjs @@ -4,6 +4,7 @@ import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const isProd = process.env.NODE_ENV === "production"; const compat = new FlatCompat({ baseDirectory: __dirname, @@ -11,6 +12,7 @@ const compat = new FlatCompat({ const eslintConfig = [ ...compat.extends("next/core-web-vitals", "next/typescript"), + ...(isProd ? { "react-hooks/exhaustive-deps": "off" } : {}), ]; export default eslintConfig; diff --git a/apps/sn/package.json b/apps/sn/package.json index 3dd7ac0c4..493a4302a 100644 --- a/apps/sn/package.json +++ b/apps/sn/package.json @@ -34,7 +34,10 @@ "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", "zod": "^3.24.2", - "zustand": "^5.0.3" + "zustand": "^5.0.3", + "ol": "^7.3.0", + "ol-ext": "^4.0.11", + "proj4": "^2.9.0" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/apps/wr/app/(map-layout)/map/mainMap/page.tsx b/apps/wr/app/(map-layout)/map/mainMap/page.tsx index 0006631d3..f75604d86 100644 --- a/apps/wr/app/(map-layout)/map/mainMap/page.tsx +++ b/apps/wr/app/(map-layout)/map/mainMap/page.tsx @@ -143,6 +143,7 @@ const Map = () => { } select.selectFeature(foundFeature, true); } else { + // toast.error('위치정보를 찾을수 없습니다') console.warn("OpenLayers Feature를 찾을 수 없습니다:"); console.log("foundFeature :", foundFeature ); console.warn("featureId : ", featureId); @@ -275,6 +276,8 @@ const Map = () => { onOpenPopup={(data: any) => { onOpenPopup(data); }} + defaultZoom={7.5} + defaultLonLat={[127.75, 36]} />
diff --git a/apps/wr/app/(popUp-layout)/mapDrgtPopUp/page.tsx b/apps/wr/app/(popUp-layout)/mapDrgtPopUp/page.tsx index a2c843351..e9315a4c9 100644 --- a/apps/wr/app/(popUp-layout)/mapDrgtPopUp/page.tsx +++ b/apps/wr/app/(popUp-layout)/mapDrgtPopUp/page.tsx @@ -82,6 +82,8 @@ export default function MapDrgtPopUp() { checkInterval={2500} height={`h-[100vh]`} droughtDates={{ startYmd: fromDate, endYmd: toDate }} + defaultZoom={7.5} + defaultLonLat={[127.75, 36]} />
); diff --git a/apps/wr/app/(popUp-layout)/mapDrgtPopUp/searchBar.tsx b/apps/wr/app/(popUp-layout)/mapDrgtPopUp/searchBar.tsx index 21a98d696..75a938672 100644 --- a/apps/wr/app/(popUp-layout)/mapDrgtPopUp/searchBar.tsx +++ b/apps/wr/app/(popUp-layout)/mapDrgtPopUp/searchBar.tsx @@ -46,7 +46,7 @@ export function SearchBar({ gisMaps, ctpvCode, sggCode }: CtpvSggPrcsFcltSelectB async function fetchCtpvData() { try { const ctpvData = await fetch(`/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo%3Atb_ms_ctpv&outputFormat=application%2Fjson&cql_filter=mctpv_cd=%27${ctpvCd.trim()}%27&srsName=EPSG:5179`) - .then(res => res.json()); + .then(res => res.json()); console.log('시도 데이터', ctpvData.features[0]); const format = new GeoJSON(); @@ -110,10 +110,48 @@ export function SearchBar({ gisMaps, ctpvCode, sggCode }: CtpvSggPrcsFcltSelectB 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) => { + // 커스텀 순서 정의 + const customOrder = [ + "서울특별시", + "부산광역시", + "대구광역시", + "인천광역시", + "광주광역시", + "대전광역시", + "울산광역시", + "세종특별자치시", + "경기도", + "강원특별자치도", // 경기도 다음에 배치 + "충청북도", + "충청남도", + "전북특별자치도", // 충청남도 다음에 배치 + "전라남도", + "경상북도", + "경상남도", + "제주특별자치도" + ]; + + const indexA = customOrder.indexOf(a.text); + const indexB = customOrder.indexOf(b.text); + + // 둘 다 커스텀 순서에 있으면 해당 순서로 + if (indexA !== -1 && indexB !== -1) { + return indexA - indexB; + } + + // 하나만 커스텀 순서에 있으면 그것을 앞으로 + if (indexA !== -1) return -1; + if (indexB !== -1) return 1; + + // 둘 다 커스텀 순서에 없으면 기본 알파벳 순서 + return a.text.localeCompare(b.text, 'ko'); + }); console.log('시도 데이터', list); setCtpv(list); @@ -126,9 +164,8 @@ export function SearchBar({ gisMaps, ctpvCode, sggCode }: CtpvSggPrcsFcltSelectB 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); setSgg(list); }; diff --git a/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx b/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx index a660e5e6b..2b8a32342 100644 --- a/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx +++ b/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx @@ -82,7 +82,7 @@ export default function MapFeaturePopup() { return ( <> {isDetail ? ( -
+
{layerNm === "tb_wr_bdst_ruse_fclt" ? ( ) : layerNm === "tb_il_swage_prcs_fclt" ? ( diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 688fb836c..219d5cfbe 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -220,6 +220,7 @@ export default function PublicReuseFaciltyInsert() { setSelectRow(pbnstHasuRes?.[val]); if (selectRow) { pbnstRegClick(selectRow); + setIndexMng(0) } else { console.error("선택된 행이 없습니다."); } @@ -236,6 +237,7 @@ export default function PublicReuseFaciltyInsert() { }, [indexMng]); useEffect(() => { + if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) { form.reset({ items: pbnst.pbnstRes }); } else { @@ -426,6 +428,7 @@ export default function PublicReuseFaciltyInsert() { } }} onInvalid={onInvalid} + newIndexMng={(formWatch?.length -1)} > } bodyClass="w-[68vw] max-w-none h-[90vh]" diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx index 21f72f449..bbab4d9ce 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx @@ -237,11 +237,13 @@ export default function PbnstSttn() { /> +
+ +
} - bodyClass="w-[68vw] max-w-none h-[90vh]" + bodyClass="w-[68vw] max-w-none h-[90vh] overflow-hidden flex flex-col min-h-0" open={pbnDetaileData.isModal} onOpenChange={(open) => { //TODO:OPEN 상태 확인필요 diff --git a/apps/wr/app/(with-layout)/stats/rswtStat/page.tsx b/apps/wr/app/(with-layout)/stats/rswtStat/page.tsx index 99a8bc4f3..21ca5112a 100644 --- a/apps/wr/app/(with-layout)/stats/rswtStat/page.tsx +++ b/apps/wr/app/(with-layout)/stats/rswtStat/page.tsx @@ -132,7 +132,7 @@ export default function RswtStat() { } return item; }); - + // 시도별 합계 계산 transformedRswtDetailList.forEach((item: RswtStatProps[]) => { const sidoText = item[0]?.toString(); @@ -144,18 +144,21 @@ export default function RswtStat() { sidoData[i] = '합계'; } else if (i == 3) { // 해당 시도의 데이터 개수를 계산 - const sidoDataCount = rswtDetailList.filter((data: unknown[]) => - Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText - ).length; - sidoData[i] = sidoDataCount; + if (typeof sidoData[i] === "number") { + sidoData[i] = (sidoData[i] as number) + 1; + } else if ((sidoData[i] === undefined || isNaN(Number(sidoData[i])))) { + sidoData[i] = 1; + } } else if (i == 4) { sidoData[i] = ''; } else if (i == 6) { // 법정 대상 건축물 개수 (인덱스 6이 'Y'인 경우) - const bumCnt = rswtDetailList.filter((data: unknown[]) => - Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText - ).filter((data: unknown[]) => String(data[6]) === '대상').length; - sidoData[i] = bumCnt; + + if (item[i]?.toString() == '대상'&&typeof sidoData[i] === "number") { + sidoData[i] = (sidoData[i] as number) + 1; + } else if (item[i]?.toString() == '대상'&&(sidoData[i] === undefined || isNaN(Number(sidoData[i])))) { + sidoData[i] = 1; + } } else if (i == 9) { const value = Number(item[9]) || 0; sidoData[i] = (Number(sidoData[i]) + value) as string | number; @@ -256,14 +259,14 @@ export default function RswtStat() { diff --git a/apps/wr/app/store/map/htdflMap/index.tsx b/apps/wr/app/store/map/htdflMap/index.tsx index 03289a06d..a8b9eb7c1 100644 --- a/apps/wr/app/store/map/htdflMap/index.tsx +++ b/apps/wr/app/store/map/htdflMap/index.tsx @@ -2,12 +2,15 @@ import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; import type { CommonTableProps } from "../../common/type/header"; import type { customHtdflBdst } from "../../ruseFcltMng/fcltReg/htdflReg"; import { htdflSttHeadListExport, htdflSttNodeListExport } from "../../ruseFcltMng/fcltSttn/htdflSttn/headList"; +import type { SelectDataType } from "@common/business_components/ui"; +import { excelHtdflSttnParsing } from "../../ruseFcltMng/fcltSttn/htdflSttn/excel"; export const htdfMapSize = 15; export type htdfMapState = { htdfMap: { htdfMapRes: customHtdflBdst[]; htdfMapCount: number; + htdflMapExcelList:[] isInit: boolean; htdfSttHeadList: CommonTableProps[]; htdfSttNodeList: CommonTableProps[]; @@ -16,7 +19,9 @@ export type htdfMapState = { type htdfMapActions = { htdfMapInit: (page: number, total: number, cityOrSgg: string) => void; - htdfMapSearch: (page: number, total: number, cityOrSgg:string, searchTxt?:string) =>void + htdfMapSearch: (isExcel: boolean, page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; htdfMapDetile:(featureCd:string) => Promise; }; @@ -27,6 +32,7 @@ export const htdfMapStateExport: htdfMapState = { htdfMap: { isInit: false, htdfMapRes: [], + htdflMapExcelList:[], htdfMapCount: 1, htdfSttHeadList: htdflSttHeadListExport, htdfSttNodeList: htdflSttNodeListExport, @@ -62,32 +68,44 @@ export const htdfMapActionsExport: (set: any) => htdfMapActions = ( console.log(e); } }, - htdfMapSearch: async ( - page: number, - total: number, - cityOrSgg: string, - searchTxt?: string + htdfMapSearch: async (isExcel: boolean, page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[] ) => { try { const htdfMapRes = await getFetchWithCount( fetchType.WR, `/drain/facilityList` + - (page ? `/${page}` : "/1") + - (total ? `/${total}` : "/20") + - (cityOrSgg ? `/${cityOrSgg}` : "/0") + - (searchTxt ? `/${searchTxt}` : "") + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); + if (isExcel) { + const htdflMapExcelList = excelHtdflSttnParsing(htdfMapRes, sidoCode, sggCode); - set((state: htdfMapState) => { - return { - ...state, - htdfMap: { - ...state.htdfMap, - htdfMapRes: htdfMapRes.data, - htdfMapCount: htdfMapRes.count, - }, - }; - }); + set((state: htdfMapState) => { + return { + ...state, + htdfMap: { + ...state.htdfMap, + htdflMapExcelList: htdflMapExcelList + }, + }; + }); + }else{ + + set((state: htdfMapState) => { + return { + ...state, + htdfMap: { + ...state.htdfMap, + htdfMapRes: htdfMapRes.data, + htdfMapCount: htdfMapRes.count, + }, + }; + }); + } } catch (e) { console.log(e); } diff --git a/apps/wr/app/store/map/pbnstMap/excel.ts b/apps/wr/app/store/map/pbnstMap/excel.ts new file mode 100644 index 000000000..63484b12f --- /dev/null +++ b/apps/wr/app/store/map/pbnstMap/excel.ts @@ -0,0 +1,21 @@ + +export const excelHasuMapParsing = ( + excel:{data:any[]} +): string[][] => { + const excelExportData: string[][] = []; + + excel.data.forEach((item) => { + + excelExportData.push([ + item.sido??'', + item.gugun??'', + item.bscFctNm ?? "", + String(item.addr??""), + item.efflWqltStndrdNm ?? "", + String(item.fctCpc ?? ""), + String(item.ruse ?? ""), + String(item.rswtrYn ?? ""), + ]); + }); + return excelExportData; +}; \ No newline at end of file diff --git a/apps/wr/app/store/map/pbnstMap/index.tsx b/apps/wr/app/store/map/pbnstMap/index.tsx index c093431ff..4cd118caa 100644 --- a/apps/wr/app/store/map/pbnstMap/index.tsx +++ b/apps/wr/app/store/map/pbnstMap/index.tsx @@ -1,9 +1,10 @@ import { fetchType, postFectch } from "@/components/lib/api"; import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; import { checkWrRegList } from "../../ruseFcltMng/fcltReg/pbnstReg"; -import type { ReturnType } from "@common/business_components/ui"; +import type { ReturnType} from "@common/business_components/ui"; import { pubFacHeadListExport } from "../../ruseFcltMng/fcltReg/pbnstReg/headList"; import type { TableUpperProps } from "../../common/type/header"; +import { excelHasuMapParsing } from "./excel"; export const pbnMapTableSize = 20; @@ -61,12 +62,23 @@ export type pbnMapState = { pbnMapRes: pubHasuType[]; pbnMapCount: number; pbnMapHeadList: TableUpperProps[]; + hasuMapExcelList:[] }; }; type pbnMapActions = { pbnMapInit: (page: number, pageTotal: number, reurnValue: ReturnType) => void; - pbnMapDetaile: (featureCd: string, data: string) => Promise; + pbnMapSearch: ( + isExcel: boolean, + page: number, + pageTotal: number, + reurnValue: ReturnType, + txt: string, + ) => void; + pbnMapDetaile: ( + featureCd: string, + data: string + ) => Promise; }; export type pbnMapType = pbnMapState & pbnMapActions; @@ -77,6 +89,7 @@ export const pbnMapStateExport: pbnMapState = { pbnMapRes: [], pbnMapCount: 1, pbnMapHeadList: pubFacHeadListExport, + hasuMapExcelList:[] }, }; @@ -124,5 +137,44 @@ export const pbnMapActionsExport: (set: any) => pbnMapActions = (set: any) => { throw error; // 호출부에서 catch 가능하도록 } }, + pbnMapSearch: async ( + isExcel: boolean, + page: number, + pageTotal: number, + reurnValue: ReturnType, + txt: string, + ) => { + try { + const hasuMapRes = await hasuListFetch(page, pageTotal, reurnValue, txt); + if (isExcel) { + const hasuMapExcelList = excelHasuMapParsing( + hasuMapRes + ); + set((state: pbnMapState) => { + return { + ...state, + pbnMap: { + ...state.pbnMap, + hasuMapExcelList: hasuMapExcelList + }, + }; + }); + } else { + set((state: pbnMapState) => { + return { + ...state, + pbnMap: { + ...state.pbnMap, + pbnMapRes: hasuMapRes.data, + pbnMapCount: hasuMapRes.count, + isInit: false, + }, + }; + }); + } + } catch (e) { + console.log(e); + } + }, }; }; diff --git a/apps/wr/app/store/map/rainMap/index.tsx b/apps/wr/app/store/map/rainMap/index.tsx index a8d8ecb04..f80c27cc7 100644 --- a/apps/wr/app/store/map/rainMap/index.tsx +++ b/apps/wr/app/store/map/rainMap/index.tsx @@ -5,12 +5,15 @@ import { rainSttNodeListExport, } from "../../ruseFcltMng/fcltSttn/rainSttn/headList"; import type { customRainBdst } from "../../ruseFcltMng/fcltReg/rainReg"; +import type { SelectDataType } from "@common/business_components/ui"; +import { excelRainSttnParsing } from "../../ruseFcltMng/fcltSttn/rainSttn/excel"; export const rainMapSize = 15 export type rainMapState = { rainMap: { rainMapRes: customRainBdst[]; + rainMapExcelList: []; rainMapCount: number; isInit: boolean; rainSttHeadList: CommonTableProps[]; @@ -20,7 +23,9 @@ export type rainMapState = { type rainMapActions = { rainMapInit: (page: number, total: number, cityOrSgg:string) => void; - rainMapSearch: (page: number, total: number, cityOrSgg:string, searchTxt?:string) =>void + rainMapSearch: (isExcel: boolean, page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; rainMapDetile:(featureCd:string) => Promise; }; @@ -30,6 +35,7 @@ export const rainMapStateExport: rainMapState = { rainMap: { isInit: false, rainMapRes: [], + rainMapExcelList:[], rainMapCount: 1, rainSttHeadList: rainSttHeadListExport, rainSttNodeList: rainSttNodeListExport, @@ -65,17 +71,31 @@ export const rainMapActionsExport: (set: any) => rainMapActions = ( console.log(e); } }, - rainMapSearch: async (page: number, total: number, cityOrSgg:string, searchTxt?:string) => { + rainMapSearch: async (isExcel: boolean, page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { try { const rainMapRes = await getFetchWithCount( fetchType.WR, `/reuseFc/rainyInfoList` + - (page ? `/${page}` : "/1") + - (total ? `/${total}` : "/20") + - (cityOrSgg ? `/${cityOrSgg}` : "/0") + - (searchTxt ? `/${searchTxt}` : "") + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); - + if (isExcel) { + const rainMapExcelList = excelRainSttnParsing(rainMapRes, sidoCode, sggCode); + + set((state: rainMapState) => { + return { + ...state, + rainMap: { + ...state.rainMap, + rainMapExcelList: rainMapExcelList, + }, + }; + }); + }else{ set((state: rainMapState) => { return { ...state, @@ -86,6 +106,8 @@ export const rainMapActionsExport: (set: any) => rainMapActions = ( }, }; }); + + } } catch (e) { console.log(e); } diff --git a/apps/wr/app/store/map/rswtMap/index.tsx b/apps/wr/app/store/map/rswtMap/index.tsx index e788bedc1..4acc0e321 100644 --- a/apps/wr/app/store/map/rswtMap/index.tsx +++ b/apps/wr/app/store/map/rswtMap/index.tsx @@ -5,12 +5,15 @@ import { rswtSttHeadListExport, rswtSttNodeListExport, } from "../../ruseFcltMng/fcltSttn/rswtSttn/headList"; +import type { SelectDataType } from "@common/business_components/ui"; +import { excelRSwtSttnParsing } from "../../ruseFcltMng/fcltSttn/rswtSttn/excel"; export const rswtMapSize = 15; export type rswtMapState = { rswtMap: { rswtMapRes: customRswtBdst[]; rswtMapCount: number; + rswtMapExcelList:[] isInit: boolean; rswtSttHeadList: CommonTableProps[]; rswtSttNodeList: CommonTableProps[]; @@ -20,10 +23,9 @@ export type rswtMapState = { type rswtMapActions = { rswtMapInit: (page: number, total: number, cityOrSgg: string) => void; rswtMapSearch: ( - page: number, - total: number, - cityOrSgg: string, - searchTxt?: string + isExcel: boolean, page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[] ) => void; rswtMapDetile: (featureCd: string) => Promise; }; @@ -34,6 +36,7 @@ export const rswtMapStateExport: rswtMapState = { rswtMap: { isInit: false, rswtMapRes: [], + rswtMapExcelList:[], rswtMapCount: 1, rswtSttHeadList: rswtSttHeadListExport, rswtSttNodeList: rswtSttNodeListExport, @@ -69,32 +72,43 @@ export const rswtMapActionsExport: (set: any) => rswtMapActions = ( console.log(e); } }, - rswtMapSearch: async ( - page: number, - total: number, - cityOrSgg: string, - searchTxt?: string + rswtMapSearch: async (isExcel: boolean, page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[] ) => { try { const rswtMapRes = await getFetchWithCount( fetchType.WR, `/reuseFc/waterreuseFc` + - (page ? `/${page}` : "/1") + - (total ? `/${total}` : "/20") + - (cityOrSgg ? `/${cityOrSgg}` : "/0") + - (searchTxt ? `/${searchTxt}` : "") + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); + if (isExcel) { + const rswtMapExcelList = excelRSwtSttnParsing(rswtMapRes, sidoCode, sggCode); + set((state: rswtMapState) => { + return { + ...state, + rswtMap: { + ...state.rswtMap, + rswtMapExcelList:rswtMapExcelList + }, + }; + }); + }else{ - set((state: rswtMapState) => { - return { - ...state, - rswtMap: { - ...state.rswtMap, - rswtMapRes: rswtMapRes.data, - rswtMapCount: rswtMapRes.count, - }, - }; - }); + set((state: rswtMapState) => { + return { + ...state, + rswtMap: { + ...state.rswtMap, + rswtMapRes: rswtMapRes.data, + rswtMapCount: rswtMapRes.count, + }, + }; + }); + } } catch (e) { console.log(e); } diff --git a/apps/wr/app/store/map/ruseMap/index.tsx b/apps/wr/app/store/map/ruseMap/index.tsx index 6333846b4..5e07a9b72 100644 --- a/apps/wr/app/store/map/ruseMap/index.tsx +++ b/apps/wr/app/store/map/ruseMap/index.tsx @@ -8,6 +8,7 @@ import { import type { ReturnType } from "@common/business_components/ui"; import { hasuListFetch } from "../../ruseFcltMng/fcltSttn/pbnstSttn"; import type { customPbnst } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; +import { excelPbnstSttnParsing } from "../../ruseFcltMng/fcltSttn/pbnstSttn/excel"; export const ruseMapSize = 15; export type ruseMapState = { @@ -16,6 +17,7 @@ export type ruseMapState = { ruseMapCount: number; isInit: boolean; fclts: string[]; + ruseExcelList: []; ruseDetailList: customPbnst[]; ruseMapHeadList: CommonTableProps[]; ruseMapNodeList: CommonTableProps[]; @@ -31,11 +33,19 @@ type ruseMapActions = { year: string, fclts: string[] ) => void; + ruseMapSearch: ( + isExcel: boolean, + page: number, + total: number, + reurnValue: ReturnType, + year: string, + fclts: string[], + fcltNm: string + ) => void; ruseMapDetail: (ruseFcltId: string) => Promise; getRuseMaplList: (bscFctCd: string) => void; }; - export type ruseMapType = ruseMapState & ruseMapActions; export const ruseMapStateExport: ruseMapState = { @@ -45,6 +55,7 @@ export const ruseMapStateExport: ruseMapState = { ruseDetailList: [], fclts: [], ruseMapCount: 1, + ruseExcelList: [], ruseMapHeadList: pbnSttHeadListExport, ruseMapNodeList: pbnstSttNodeListExport, }, @@ -80,7 +91,6 @@ export const ruseMapActionsExport: (set: any) => ruseMapActions = ( year, fclts ); - set((state: ruseMapState) => { return { ...state, @@ -148,5 +158,51 @@ export const ruseMapActionsExport: (set: any) => ruseMapActions = ( throw errorHasu; // 호출부에서 catch 가능하도록 } }, + ruseMapSearch: async ( + isExcel: boolean, + page: number, + total: number, + reurnValue: ReturnType, + year: string, + fclts: string[], + fcltNm: string + ) => { + try { + const ruseMapRes = await hasuListFetch( + String(page), + String(total), + reurnValue, + fcltNm, + year, + fclts + ); + if (isExcel) { + const ruseMapExcel = excelPbnstSttnParsing(ruseMapRes.data); + set((state: ruseMapState) => { + return { + ...state, + ruseMap: { + ...state.ruseMap, + ruseExcelList: ruseMapExcel, + }, + }; + }); + } else { + set((state: ruseMapState) => { + return { + ...state, + ruseMap: { + ...state.ruseMap, + ruseMapRes: ruseMapRes.data, + ruseMapCount: ruseMapRes.count, + isInit: false, + }, + }; + }); + } + } catch (e) { + console.log(e); + } + }, }; }; diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx index bb0c18d6a..cd320cd06 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx @@ -61,49 +61,49 @@ export const reUseCaseNames: nameMap = { "사업비 정산_국고비율(백만원)": "clclnEcqrRt" } -interface PbnstSttnItem { - rvbsn: string; - lgbsn: number; - mdmbsn?: string; - rgnNm?: string; - sido?: number; - gugun?: number; - bscFctNm?: string; - efflWqltStndrdNm?: string; - fctCpc?: number; - tel?: string; - ruseUtztnFcltNm?: string; - fcltCpct?: number; - splyExtraCpct?: number; - ruseCpct?: number; - usageTotal?: number; - inUsage?: number; - outUsage?: number; -} +// interface PbnstSttnItem { +// rvbsn: string; +// lgbsn: number; +// mdmbsn?: string; +// rgnNm?: string; +// sido?: number; +// gugun?: number; +// bscFctNm?: string; +// efflWqltStndrdNm?: string; +// fctCpc?: number; +// tel?: string; +// ruseUtztnFcltNm?: string; +// fcltCpct?: number; +// splyExtraCpct?: number; +// ruseCpct?: number; +// usageTotal?: number; +// inUsage?: number; +// outUsage?: number; +// } -export const excelPbnstSttnParsing = (data: PbnstSttnItem[]): string[][] => { - const excelExportData: string[][] = []; +// export const excelPbnstSttnParsing = (data: PbnstSttnItem[]): string[][] => { +// const excelExportData: string[][] = []; - data.forEach((item) => { - excelExportData.push([ - item.rvbsn, - String(item.lgbsn), - item.mdmbsn ?? "", - item.rgnNm ?? "", - String(item.sido ?? ""), - String(item.gugun ?? ""), - String(item.bscFctNm ?? ""), - String(item.efflWqltStndrdNm ?? ""), - String(item.fctCpc ?? ""), - String(item.tel ?? ""), - String(item.ruseUtztnFcltNm ?? ""), - String(item.fcltCpct ?? ""), - String(item.splyExtraCpct ?? ""), - String(item.ruseCpct ?? ""), - String(item.usageTotal ?? ""), - String(item.inUsage ?? ""), - String(item.outUsage ?? ""), - ]) - }) - return excelExportData; -}; \ No newline at end of file +// data.forEach((item) => { +// excelExportData.push([ +// item.rvbsn, +// String(item.lgbsn), +// item.mdmbsn ?? "", +// item.rgnNm ?? "", +// String(item.sido ?? ""), +// String(item.gugun ?? ""), +// String(item.bscFctNm ?? ""), +// String(item.efflWqltStndrdNm ?? ""), +// String(item.fctCpc ?? ""), +// String(item.tel ?? ""), +// String(item.ruseUtztnFcltNm ?? ""), +// String(item.fcltCpct ?? ""), +// String(item.splyExtraCpct ?? ""), +// String(item.ruseCpct ?? ""), +// String(item.usageTotal ?? ""), +// String(item.inUsage ?? ""), +// String(item.outUsage ?? ""), +// ]) +// }) +// return excelExportData; +// }; \ No newline at end of file diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx index 8e18317a8..38e2cab7f 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx @@ -172,9 +172,16 @@ console.log("rainChkgdData", rainChkgdData); ` 연간운영비(원/년): 숫자 형식 아님 /입력값:${rain.anlOperCst}` ); } - if (rain.oilqMeasMthd.length > 1 && !chkMngType.includes(rain.oilqMeasMthd)) { + + if (rain.operMngCd.length > 1 && !chkMngType.includes(rain.operMngCd)) { issues.push( - defaultMessage + `유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rain.oilqMeasMthd}` + defaultMessage + `운영관리 형태: 위탁 혹은 자체 만 허용) /입력값:${rain.oilqMeasMthd}` + ); + } + + if (rain.oilqMeasMthd.length > 1 && !chkSpwtPositnType.includes(rain.oilqMeasMthd)) { + issues.push( + defaultMessage + `유량측정방법: 유량계, 수위계, 펌프적산전력계, 기타) /입력값:${rain.oilqMeasMthd}` ); } diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx index d9016410d..a3372639b 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx @@ -2,12 +2,12 @@ import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { isInvalidDate, isInvalidNumber, + parseCommaRawValue, parseRawValue, } from "@/components/lib/func/excelFunc"; import type { SelectDataType } from "@common/business_components/ui"; import type { rswtChkZodType } from "."; import { - chkSpwtPositnType, useTypes, type Buildingtype } from "../rainChkgdn/excel"; @@ -137,7 +137,6 @@ export const rswtMakeExcelSample = ( export const excelToRswtGuideData = ( excelData: any, year: string, - issues: string[] ) => { const rswtChkgdData: rswtChkZodType[] = excelData .map((item: any) => { @@ -160,7 +159,7 @@ export const excelToRswtGuideData = ( return { bdstRuseFcltSn: Number(item["NO"]), operMngCd: item["운영관리형태"], - anlOperCst: parseRawValue(item[`연간운영비(원/년)`]), + anlOperCst: parseCommaRawValue(item["연간운영비(원/년)"]), prcsCpct: parseRawValue(item[`처리용량(m³)`]), spwtPositn: item["채수지점"], chckYmd: checkYmd, @@ -174,18 +173,15 @@ export const excelToRswtGuideData = ( }) .filter((el: any) => el !== undefined); - if (rswtChkgdData.length > 0) { - rswtExcelDataCheck(rswtChkgdData, issues); - } - if (issues.length > 0) { - return { data: [], issues }; - } - - return { data: rswtChkgdData, issues: [] }; + return rswtChkgdData }; -export const excelToRswtPolution = (excelData: any, issues: string[]) => { + + + +export const excelToRswtPolution = (excelData: any) => { + const rswtChkgdData: rswtChkZodType[] = excelData .map((item: any) => { if (item["점검일"] !== undefined) { @@ -203,11 +199,11 @@ export const excelToRswtPolution = (excelData: any, issues: string[]) => { const combResdch = parseRawValue(item[`${quarter}분기_결합잔류염소`]); const trbt = parseRawValue(item[`${quarter}분기_탁도`]); const bod = parseRawValue(item[`${quarter}분기_BOD`]); - const smll = parseRawValue(item[`${quarter}분기_냄새`]); + const smll = String(item[`${quarter}분기_냄새`]); const crmty = parseRawValue(item[`${quarter}분기_색도`]); const tph = parseRawValue(item[`${quarter}분기_T-P`]); const tn = parseRawValue(item[`${quarter}분기_T-N`]); - const ph = parseRawValue(item[`${quarter}분기_PH`]); + const ph = String(item[`${quarter}분기_PH`]) ; const clIonQnt = parseRawValue(item[`${quarter}분기_염화물`]); return { node: quarter, @@ -226,12 +222,13 @@ export const excelToRswtPolution = (excelData: any, issues: string[]) => { mdfr: TEST_UUID, }; }); + return { bdstRuseFcltSn: Number(item["NO"]), chckYmd: checkYmd, utztnUsgCd: item["이용용도"], operMngCd: item["운영관리형태"], - anlOperCst: parseRawValue(item["연간운영비(원/년)"]), + anlOperCst: parseCommaRawValue(item["연간운영비(원/년)"]), prcsCpct: parseRawValue(item["처리용량(m³)"]), spwtPositn: item["채수지점"], rswtrPollutions: makePoluttion, @@ -251,21 +248,22 @@ export const excelToRswtPolution = (excelData: any, issues: string[]) => { } }) .filter((el: any) => el !== undefined); - if (rswtChkgdData.length > 0) { - rswtExcelDataCheck(rswtChkgdData, issues); - } + // if (rswtChkgdData.length > 0) { + // rswtExcelDataCheck(rswtChkgdData, issues); + // } - if (issues.length > 0) { - return { data: [], issues }; - } + // if (issues.length > 0) { + // return { data: [], issues }; + // } - return { data: rswtChkgdData, issues: [] }; + return rswtChkgdData }; -const rswtExcelDataCheck = ( +export const rswtExcelDataCheck = ( rswtChkgdData: rswtChkZodType[], issues: string[] ) => { + rswtChkgdData.forEach((rswt: any) => { const defaultMessage = `*중수도 수질 [NO: ${rswt.bdstRuseFcltSn}, 점검일: ${rswt.chckYmd}, 이용용도: ${rswt.utztnUsgCd}] 항목에서 `; @@ -273,6 +271,7 @@ const rswtExcelDataCheck = ( issues.push(defaultMessage + `점검일: 날짜 형식 아님 (${rswt.chckYmd})`); } if (rswt.anlOperCst.length > 2 && isInvalidNumber(rswt.anlOperCst)) { + issues.push( defaultMessage + ` 연간운영비(원/년): 숫자 형식 아님 (${rswt.anlOperCst})` @@ -283,15 +282,15 @@ const rswtExcelDataCheck = ( defaultMessage + ` 처리용량(m³): 숫자 형식 아님 (${rswt.prcsCpct})` ); } - if ( - rswt.spwtPositn.length > 1 && - !chkSpwtPositnType.includes(rswt.spwtPositn) - ) { - issues.push( - defaultMessage + - `측정위치: 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rswt.spwtPositn}` - ); - } + // if ( + // rswt.spwtPositn.length > 1 && + // !chkSpwtPositnType.includes(rswt.spwtPositn) + // ) { + // issues.push( + // defaultMessage + + // `채수지점 : 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rswt.spwtPositn}` + // ); + // } if (rswt?.rswtrPollutions && rswt?.rswtrPollutions.length > 0) { rswt.rswtrPollutions.forEach((pol) => { @@ -318,12 +317,6 @@ const rswtExcelDataCheck = ( defaultMessage + `${pol.node}분기_BOD: 숫자 형식 아님 (${pol.bod})` ); } - if (isInvalidNumber(pol.smll)) { - issues.push( - defaultMessage + - `${pol.node}분기_냄새: 숫자 형식 아님 (${pol.smll})` - ); - } if (isInvalidNumber(pol.crmty)) { issues.push( defaultMessage + @@ -340,20 +333,16 @@ const rswtExcelDataCheck = ( defaultMessage + `${pol.node}분기_T-N: 숫자 형식 아님 (${pol.tn})` ); } - if (isInvalidNumber(pol.ph)) { - issues.push( - defaultMessage + `${pol.node}분기_PH: 숫자 형식 아님 (${pol.ph})` - ); if (isInvalidNumber(pol.clIonQnt)) { issues.push( defaultMessage + - `${pol.node}분기_PH: 숫자 형식 아님 (${pol.clIonQnt})` + `${pol.node}분기_염소이온량: 숫자 형식 아님 (${pol.clIonQnt})` ); - } } }); } + if (rswt?.rswtrDetails && rswt?.rswtrDetails.length > 0) { rswt?.rswtrDetails.forEach((key) => { if (key.levyAmt?.length > 1 && isInvalidNumber(key.levyAmt)) { diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 5f72998d1..afba2f813 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -285,6 +285,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( ...state.rswtChkgdn, rswtChkRes: rswtChkRes.data, rswtChkCount: rswtChkRes.count, + }, })); } catch (error) { @@ -361,7 +362,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index 3712b4dd1..423c3c113 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -232,16 +232,18 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { if( response.status === 200) { toast.success(`${pbnstRes.ruseUtztnFcltNm} 하수재이용 시설이 등록되었습니다.`); } - const pbnstHasuRes = await hasuListFetch('1', '0'); + + const newPbnstRes = await initFetch(pbnstRes.pbnstRuseFcltId.slice(0, 11)); return set((state: pubFacState) => ({ ...state, pbnst: { ...state.pbnst, - pbnstHasuRes: pbnstHasuRes.data, - pbnstHasuResCount:pbnstHasuRes.count, - isInit: false, + pbnstRes: newPbnstRes, + // isInit: false, }, })); + + } catch (error) { console.error("Error updating pbnst:", error); } diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx index b57e0edde..65f02e26b 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx @@ -22,13 +22,13 @@ export const excelRainSttnParsing = ( String(item.chgBuilding.length > 0 ? "Y" : "N"), String(item.reuseBuilding?.[0]?.lawTrgtYn ?? ""), String(item.reuseBuilding?.[0]?.instlCmptnDt ?? ""), - String(item.reuseBuilding?.[0]?.instlCst ?? ""), + String(item.reuseBuilding?.[0]?.instlCst === -1 ? "확인불가" : String(item.reuseBuilding?.[0]?.instlCst) ?? ""), item.reuseBuilding?.[0]?.rainyObj?.pntfColKnd ?? "", - String(item.reuseBuilding?.[0]?.rainyObj?.pntfColArea ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.pntfColArea === -1 ? "확인불가" : String(item.reuseBuilding?.[0]?.rainyObj?.pntfColArea) ?? ""), String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctYn ?? ""), - String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctCpct ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctCpct === -1 ? "확인불가" : String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctCpct) ?? ""), String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctPrs ?? ""), - String(item.reuseBuilding?.[0]?.rainyObj?.dtfcltCpct ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.dtfcltCpct === -1 ? "확인불가" : String(item.reuseBuilding?.[0]?.rainyObj?.dtfcltCpct) ?? ""), String(item.reuseBuilding?.[0]?.instlCstSprtYn ?? ""), String(item.reuseBuilding?.[0]?.instlCstSprtAmt === -1 ? "확인불가" : String(item.reuseBuilding?.[0]?.instlCstSprtAmt) ?? ""), String(item.reuseBuilding?.[0]?.grntchBdstCertYn ?? ""), diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx index 803dc2d83..b3d3835c2 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx @@ -25,7 +25,7 @@ export const excelRSwtSttnParsing = ( String(item.reuseBuilding?.[0]?.instlCmptnDt ?? ""), //설치완료일시 String(item.reuseBuilding?.[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding?.[0]?.instlCst ?? ""), //설치비용 item.reuseBuilding?.[0]?.rswtrObj?.orgwtKndCd ?? "", //원수종류 - String(item.reuseBuilding?.[0]?.rswtrObj.filtrTrtmfctCpct ?? ""), //처리시설용량 + String(item.reuseBuilding?.[0]?.rswtrObj.filtrTrtmfctCpct === -1 ? "확인불가" : item.reuseBuilding?.[0]?.rswtrObj.filtrTrtmfctCpct ?? ""), //처리시설용량 String(item.reuseBuilding?.[0]?.rswtrObj?.filtrTrtmfctProcs ?? ""), //주요처리공정 String(item.reuseBuilding?.[0]?.rswtrObj?.filtrTrtmfctUsgCd ?? ""), //이용용도 String(item.reuseBuilding?.[0]?.instlCstSprtYn ?? ""), //설치지원비용여부 diff --git a/apps/wr/app/store/stats/rainStat/index.tsx b/apps/wr/app/store/stats/rainStat/index.tsx index ec46c2b4e..80787ae43 100644 --- a/apps/wr/app/store/stats/rainStat/index.tsx +++ b/apps/wr/app/store/stats/rainStat/index.tsx @@ -50,7 +50,13 @@ const excelDownList: ExcelType[] = []; const fetchRainStatList = async (year: string) => { const res = await getFetch(fetchType.WR, `/reuseChk/rainy/stat/${year}`); - return res; + + + return res.map((item) => ({ + ...item, + instlCst:item.instlCst<0?0:item.instlCst, + dtfcltCpct:item.dtfcltCpct<0?0:item.dtfcltCpct + })); }; type rainStatActions = { @@ -83,7 +89,8 @@ export const rainStatActionsExport = ( searchRainStatList: async (year: string, city: SelectDataType[]) => { const res = await fetchRainStatList(year); const cityArr: any[] = []; - + + console.log('res ', res) res.filter((resFilter: RainStatProps) => city.some((cityCd) => @@ -209,6 +216,7 @@ export const rainStatActionsExport = ( utztnUsgCd: utztnUsgArray.join(", "), }); }); + const excelDetailData = excelRainStat(rainDetailList); /* const rainStatHeadList = data.map((item:RainStatProps) => ({ ...item, @@ -230,8 +238,6 @@ export const rainStatActionsExport = ( yearMoney:item.yearMoney, })) */ - console.log(" cityArr ", cityArr); - console.log(" excelDownList ", excelDownList); cityArr.unshift(totalObj); set((state: rainStatState) => ({ diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index 0733f1bee..ef4baa67b 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -173,7 +173,6 @@ export const getCityOrSgg = (ref: RefObject) => { export const safeDate = (value: any): Date | undefined => { const date = new Date(value); - console.log(' date ', date) return isNaN(date.getTime()) ? undefined : date; }; @@ -181,8 +180,8 @@ const currentYear = new Date().getFullYear(); export const yearOptions: { val: string; text: string }[] = Array.from( { length: currentYear - 1969 }, (_, i) => ({ - val: String(1970 + i), - text: `${1970 + i}년`, + val: String(currentYear - i), + text: `${currentYear - i}년`, }) ); diff --git a/apps/wr/components/lib/func/excelFunc.tsx b/apps/wr/components/lib/func/excelFunc.tsx index 2383bdbb3..7a1cc1d2e 100644 --- a/apps/wr/components/lib/func/excelFunc.tsx +++ b/apps/wr/components/lib/func/excelFunc.tsx @@ -12,6 +12,23 @@ export const isInvalidNumber = (val: any) => { if (val === "" || val === undefined || val === null) return 0; if (typeof val === "string" && ["보류", "N/A"].includes(val.trim())) return val; - return !isNaN(val) && val !== " " ? Number(val) : val; + return !isNaN(val) && val !== " " ? Number(val.replaceAll(',','')) : val; }; - \ No newline at end of file + + + export const parseCommaRawValue = (val: any): number | string => { + if (val === "" || val === undefined || val === null) return 0; + + // 문자열이고 "보류"나 "N/A"면 그대로 반환 + if (typeof val === "string" && ["보류", "N/A"].includes(val.trim())) { + return val; + } + + // 문자열에 쉼표가 있으면 제거 + if (typeof val === "string" && val.includes(",")) { + val = val.replace(/,/g, ""); + } + + // 숫자로 변환 가능하면 숫자로, 아니면 그대로 + return !isNaN(val) && val !== " " ? Number(val) : val; + }; \ No newline at end of file diff --git a/apps/wr/components/lib/func/mapUtil.ts b/apps/wr/components/lib/func/mapUtil.ts index 78aa71427..a23b48484 100644 --- a/apps/wr/components/lib/func/mapUtil.ts +++ b/apps/wr/components/lib/func/mapUtil.ts @@ -9,7 +9,7 @@ const wrLayerList = [ ]; const wrFeaturePK = [ { text: "bscFctCd", val: "tb_il_swage_prcs_fclt" }, - { text: "pbnstRuseFcltId", val: "tb_wr_bdst_ruse_fclt" }, + { text: "searchPk", val: "tb_wr_bdst_ruse_fclt" }, { text: "bdstInfoSn", val: "tb_wr_rnwtr_utztn_fclt" }, { text: "bdstInfoSn", val: "tb_wr_rswtr_fclt" }, { text: "bdstInfoSn", val: "tb_wr_htdfl_fclt" }, @@ -26,11 +26,12 @@ const wrFeaturePK = [ // { text: "잠재수요처", val: "tb_wr_tmpr_dmd_co" }, export const getFeatureCd = (featurePropertise: any, layerNm: string) => { + + console.log('featurePropertise ', featurePropertise) const featureCd = layerNm === "tb_wr_bdst_ruse_fclt" ? featurePropertise?.swtrmt_fclt_cd : [ - "tb_wr_ruse_fclt_dmd_co", "tb_wr_rswtr_fclt", "tb_wr_htdfl_fclt", "tb_wr_rnwtr_utztn_fclt", @@ -39,6 +40,8 @@ export const getFeatureCd = (featurePropertise: any, layerNm: string) => { ? featurePropertise?.bdst_nm : layerNm === "tb_il_swage_prcs_fclt" ? featurePropertise?.swtrmt_fclt_nm + : layerNm === "tb_wr_ruse_fclt_dmd_co"? + featurePropertise?.bdst_info_sn : null; return featureCd; @@ -125,15 +128,18 @@ export function featurePopUpOpen(layerNm:string,featureCd:string,popupDetailRef) const url = `/mapFeaturePopup?layerNm=${encodeURIComponent(String(layerNm))}&featureCd=${encodeURIComponent(featureCd)} `; const width = String(layerNm) === "tb_wr_bdst_ruse_fclt" - ? Math.floor(window.innerWidth * 0.8) + ? Math.floor(window.innerWidth * 0.6) : Math.floor(window.innerWidth * 0.4); // 화면 폭의 40% const height = String(layerNm) === "tb_wr_bdst_ruse_fclt" - ? Math.floor(window.innerHeight - 150) - : Math.floor(window.innerHeight - 450); // 150px 여백 확보; // calc(100% - 70px) + ? Math.floor(window.innerHeight - 100) + : String(layerNm) === "tb_wr_ruse_fclt_dmd_co" + ?Math.floor(window.innerHeight - 430) + : Math.floor(window.innerHeight - 300); // 150px 여백 확보; // calc(100% - 70px) const left = (window.screen.availWidth - width) / 2; const top = (window.screen.availHeight - height) / 2; - const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`; + const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes `; + popupDetailRef.current = window.open( url, "지도 시설 상세정보 팝업", diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index 7f87fe1fd..1ad310465 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -234,9 +234,6 @@ export default function ConCostForm({ } }; - useEffect(() => { - console.log(" form ", form.formState.errors); - }, [form.formState]); useEffect(() => { const origin = window.location.origin; @@ -580,9 +577,6 @@ export default function ConCostForm({ +
- + */}
-
+
0) { - htdfMapSearch( - page, - rainMapSize, - String(con), - seartchTxt ?? "" - ); - } else { - htdfMapSearch(page, rainMapSize, String(con)); - } + htdfMapSearch( + false, + page, + String(htdfMapSize), + String(con), + seartchTxt||'', + code.cityCd, + code.sggCd + ); setIsReset(false); } }} diff --git a/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx index e1a917e8f..80bb8d7ec 100644 --- a/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx @@ -1,7 +1,7 @@ import { useWrStore } from "@/app/store"; import { rainMapSize } from "@/app/store/map/rainMap"; import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import { getCity, tableData } from "@/components/lib/func/commom"; +import { excelDownload, getCity, tableData } from "@/components/lib/func/commom"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; import { ContentsDiv, @@ -17,6 +17,8 @@ import { import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import { Button } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; export function RainFacility({ rtnFeature, @@ -26,7 +28,7 @@ export function RainFacility({ const { code, rainMap, rainMapInit, rainMapSearch } = useWrStore( (state) => state ); - const { rainMapRes, rainMapCount, rainSttHeadList, rainSttNodeList } = + const { rainMapRes, rainMapExcelList,rainMapCount, rainSttHeadList, rainSttNodeList } = rainMap; const [rainTable, setRainTable] = useState(); const [isReset, setIsReset] = useState(false); @@ -35,6 +37,7 @@ export function RainFacility({ sgg: "", city: "", }); + const { t } = useTranslation('base'); const [seartchTxt, setSearchTxt] = useState(); @@ -116,7 +119,18 @@ export function RainFacility({ .filter((item: any) => item.rainyObj !== undefined); setRainTable(chData); }, [code, rainMapRes]); - + useEffect(() => { + if (rainMapExcelList.length > 0) { + excelDownload( + rainMapExcelList, + "빗물재이용시설 현황", + "sample_rainSttn.xlsx", + "A1:W3" + ); + } else { + toast.error(t("excelDownErrMsg")); + } + }, [rainMapExcelList]); return ( <> @@ -146,17 +160,50 @@ export function RainFacility({ /> { setSearchTxt(e); }} /> + +
- + */}
@@ -187,16 +243,16 @@ export function RainFacility({ ? objSearch.sgg : objSearch.city; if (con) { - if (seartchTxt && seartchTxt?.length > 0) { - rainMapSearch( - page, - rainMapSize, - String(con), - seartchTxt ?? "" - ); - } else { - rainMapSearch(page, rainMapSize, String(con)); - } + rainMapSearch( + false, + page, + String(rainMapSize), + String(con), + seartchTxt ?? "", + code.cityCd, + code.sggCd + ); + setIsReset(false); } }} diff --git a/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx b/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx index 18a2b2c1d..becd76872 100644 --- a/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx @@ -1,57 +1,71 @@ import { useWrStore } from "@/app/store"; import { rainMapSize } from "@/app/store/map/rainMap"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; -import { getCity, tableData } from "@/components/lib/func/commom"; +import { excelDownload, getCity, tableData } from "@/components/lib/func/commom"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; -import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import { Button } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; export function RswtFacility({ rtnFeature, }: { rtnFeature: (feature: any) => void; }) { - const { code, rswtMap, rswtMapInit,rswtMapSearch } = useWrStore((state) => state); - const { rswtMapRes, rswtMapCount, rswtSttHeadList, rswtSttNodeList } = + const { code, rswtMap, rswtMapInit, rswtMapSearch } = useWrStore( + (state) => state + ); + const { rswtMapRes, rswtMapExcelList, rswtMapCount, rswtSttHeadList, rswtSttNodeList } = rswtMap; const [rswtrTable, setRswtrTable] = useState(); const [isReset, setIsReset] = useState(false); - const [selectSgg, setSelectSgg] = useState(); - const [objSearch, setObjSearch] = useState({ - sgg: "", - city: "", - }); - - const [seartchTxt, setSearchTxt] = useState(); - - const getSelCity = useCallback( - (val: string | number) => { - const cityValue = String(val); - const data = getCity(cityValue, code.sggCd); - setSelectSgg(data); - setObjSearch({ city: cityValue, sgg: "0" }); - }, - [code.sggCd] - ); - - const getSgg = useCallback((val: string | number) => { - setObjSearch((prev) => ({ ...prev, sgg: String(val) })); - }, []); - - useEffect(() => { - // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 - if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { - const firstCityVal = code.cityCd[0]?.val; - if (firstCityVal) { - // 첫 번째 시/도 값으로 상태를 초기화합니다. - getSelCity(firstCityVal); - } + const [objSearch, setObjSearch] = useState({ + sgg: "", + city: "", + }); + const { t } = useTranslation('base'); + + const [seartchTxt, setSearchTxt] = useState(); + + const getSelCity = useCallback( + (val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: "0" }); + }, + [code.sggCd] + ); + + const getSgg = useCallback((val: string | number) => { + setObjSearch((prev) => ({ ...prev, sgg: String(val) })); + }, []); + + useEffect(() => { + // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 + if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { + const firstCityVal = code.cityCd[0]?.val; + if (firstCityVal) { + // 첫 번째 시/도 값으로 상태를 초기화합니다. + getSelCity(firstCityVal); } - }, [code.cityCd, objSearch.city, getSelCity]); + } + }, [code.cityCd, objSearch.city, getSelCity]); const rswtCellClick = (index: number | undefined, headId: string) => { if ( headId !== "chBuildingYn" && @@ -100,6 +114,19 @@ export function RswtFacility({ ); setRswtrTable(chData); }, [code, rswtMapRes]); + + useEffect(() => { + if (rswtMapExcelList.length > 0) { + excelDownload( + rswtMapExcelList, + "중수도시설 현황", + "sample_rswtSttn.xlsx", + "A1:W3", + ); + } else { + toast.error(t('excelDownErrMsg')); + } + }, [rswtMapExcelList]); return ( <> @@ -129,18 +156,14 @@ export function RswtFacility({ /> { setSearchTxt(e); }} /> -
- - -
+ +
+ + +
+ {/* */}
@@ -166,22 +226,23 @@ export function RswtFacility({ }} onPageChange={(page) => { const con = - objSearch.sgg && objSearch.sgg !== "0" - ? objSearch.sgg - : objSearch.city; - if (con) { - if (seartchTxt && seartchTxt?.length > 0) { + objSearch.sgg && objSearch.sgg !== "0" + ? objSearch.sgg + : objSearch.city; + if (con) { rswtMapSearch( + false, page, - rainMapSize, + String(rainMapSize), String(con), - seartchTxt ?? "" + seartchTxt ?? "", + code.cityCd, + code.sggCd ); - } else { - rswtMapSearch(page, rainMapSize, String(con)); + + setIsReset(false); } - setIsReset(false); - }}} + }} resetPageNo={isReset} > diff --git a/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx index 4fd9c684f..2c7a5295c 100644 --- a/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx +++ b/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx @@ -2,7 +2,7 @@ import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import { ruseMapSize } from "@/app/store/map/ruseMap"; import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; -import { yearOptions } from "@/components/lib/func/commom"; +import { excelDownload, yearOptions } from "@/components/lib/func/commom"; import { ContentsDiv, SearchBaseForm, @@ -14,17 +14,20 @@ import { } from "@common/business_components/ui"; import { Label } from "@common/components/ui"; import { useEffect, useRef, useState } from "react"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; export function RuseFacility({ rtnFeature, }: { rtnFeature: (feature: any) => void; }) { - const { code, ruseMap, ruseMapInit, getMapFlcts } = useWrStore( + const { code, ruseMap, ruseMapInit, getMapFlcts,ruseMapSearch } = useWrStore( (state) => state ); + const { t } = useTranslation("base"); - const { ruseMapCount, fclts, ruseMapRes,ruseMapHeadList } = ruseMap; + const { ruseMapCount, ruseExcelList,fclts, ruseMapRes,ruseMapHeadList } = ruseMap; const [ruseTable, setRuseTable] = useState(); const [isReset, setIsReset] = useState(false); @@ -51,6 +54,23 @@ export function RuseFacility({ ruseMapInit(1,ruseMapSize, ref.current, searchYear, fclts); }, [fclts]); + + useEffect(()=>{ + if(ruseExcelList&&ruseExcelList.length>0){ + if (ruseExcelList.length > 0) { + excelDownload( + ruseExcelList, + "하수재이용시설 현황", + "sample_pbnstSttn.xlsx", + "A1:U2", + 3 + ); + } else { + toast.error(t('excelDownErrMsg')); + } + } + + },[ruseExcelList]) useEffect(() => { if (ruseMapRes && ruseMapRes.length > 0) { setRuseTable(ruseMapRes); @@ -113,7 +133,7 @@ export function RuseFacility({ ref={ref} isAdmin={false} excelClick={function (returnType: ReturnType): void { - console.log("pbnstSttnSearch ", returnType); + ruseMapSearch(true,1,1000000 , returnType, searchYear, fclts,pbnRuseNm??'') }} />
@@ -127,7 +147,12 @@ export function RuseFacility({ headName={""} cellClick={ruseCellClick} onPageChange={(page) => { - ruseMapInit(page,ruseMapSize , ref.current, searchYear, fclts); + if(pbnRuseNm&&pbnRuseNm.length>0){ + ruseMapSearch(false,page,ruseMapSize , ref.current, searchYear, fclts,pbnRuseNm) + }else{ + ruseMapInit(page,ruseMapSize , ref.current, searchYear, fclts); + } + setIsReset(false); }} resetPageNo={isReset} diff --git a/apps/wr/components/ui/map/detailPopup/ruseDetailPopup.tsx b/apps/wr/components/ui/map/detailPopup/ruseDetailPopup.tsx index 9f0a114f8..7e4c3b603 100644 --- a/apps/wr/components/ui/map/detailPopup/ruseDetailPopup.tsx +++ b/apps/wr/components/ui/map/detailPopup/ruseDetailPopup.tsx @@ -13,7 +13,7 @@ export function RuseDetailPopup({ bscFctCd }: { bscFctCd: string }) { const { ruseMap, getRuseMaplList } = useWrStore((state) => state); const { ruseDetailList } = ruseMap; const [fcltTab, setFcltTab] = useState(""); - const [isDetail, setIsDetail] = useState({} as customPbnst); + const [, setIsDetail] = useState({} as customPbnst); const initRef = useRef(false); useEffect(() => { @@ -38,9 +38,9 @@ export function RuseDetailPopup({ bscFctCd }: { bscFctCd: string }) { if (!(ruseDetailList && ruseDetailList[0])) return; return ( -
+
- + {ruseDetailList.map((item, index) => { return ( - - - + {ruseDetailList.map((item) => ( + +
+ +
+
+ ))}
); diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx index 46e6c5d30..405061390 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -6,6 +6,7 @@ import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn import { excelToRswtGuideData, excelToRswtPolution, + rswtExcelDataCheck, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel"; import { showConfirmToast } from "@/components/lib/func/commom"; import { @@ -110,30 +111,23 @@ export default function ChkgdExcel({ }); } else if (type === "RST") { filePickedArray(file, 0).then((res) => { - let issues: string[] = []; + if (res && res?.length > 1) { const guideData = excelToRswtGuideData( res[0], form.getValues("chckYr"), - issues ); - const polutionData = excelToRswtPolution(res[1], issues); + const polutionData = excelToRswtPolution(res[1]); - issues = [...guideData.issues, ...polutionData.issues]; + - if (issues && issues.length > 0) { - issues.some((err, index) => { - showConfirmToast(err); - return index === 6; // true 반환 시 반복 종료 - }); - clearFile(); - } else { + const keyGenerator = (item: rswtChkZodType) => `${item.bdstRuseFcltSn}|${item.chckYmd}|${item.utztnUsgCd}`; - const totalData: rswtChkZodType[] = guideData.data.map( + const totalData: rswtChkZodType[] = guideData.map( (guide) => { - const fusion = polutionData.data.find( + const fusion = polutionData.find( (polution) => guide.bdstRuseFcltSn === polution.bdstRuseFcltSn && guide.chckYmd === polution.chckYmd && @@ -153,8 +147,8 @@ export default function ChkgdExcel({ } } ); - const guideKey = new Set(guideData.data.map(keyGenerator)); - const onlypolution = polutionData.data.filter( + const guideKey = new Set(guideData.map(keyGenerator)); + const onlypolution = polutionData.filter( (item) => !guideKey.has(keyGenerator(item)) ); @@ -162,9 +156,20 @@ export default function ChkgdExcel({ ...totalData, ...onlypolution, ]; + const issues: string[] = []; - setExcelRswtData(listTotalData); - } + console.log('listTotalData ', listTotalData) + rswtExcelDataCheck(listTotalData,issues) + + if (issues && issues.length > 0) { + issues.some((err, index) => { + showConfirmToast(err); + return index === 6; // true 반환 시 반복 종료 + }); + clearFile(); + } else { + setExcelRswtData(listTotalData); + } } }); } diff --git a/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx index a809c88f6..911dd2ed2 100644 --- a/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx +++ b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx @@ -79,7 +79,6 @@ export default function RuseCaseMngExcel() { }; Object.keys(reUseCaseNames).forEach((key: string) => { - console.log("reUseCaseNames", reUseCaseNames) if (item && reUseCaseNames[key] !== undefined) { const mappedKey = reUseCaseNames[key]; if (mappedKey && object.hasOwnProperty(mappedKey)) { diff --git a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx index cc38b1f30..7762e96dc 100644 --- a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -128,23 +128,10 @@ export default function FcOrgwtsRegModal({ }> 조회 - +
- {/*
-
- - setInputVal(e.target.value)} - /> - -
-
-
*/} {/* 테이블 영역 */}
diff --git a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx index c0cbdda30..ec79dd42b 100644 --- a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -25,18 +25,20 @@ export function PbnstRegModal({ form, setPbnstInsert, onInvalid, + newIndexMng }: { form: UseFormReturn<{ items: PbnstRegArrrayType }>; setPbnstInsert: (data: customPbnst) => void; onInvalid: ( errors: FieldErrors<{ items: z.infer[] }> ) => void; + newIndexMng:number }) { const { t } = useTranslation("base"); //TODO: 유즈 스테이트에 set만해도 되나? const [, setCurrentTab] = useState("mng"); - const [indexMng, setIndexMng] = useState(0); + const [indexMng, setIndexMng] = useState(newIndexMng); const [indexBiz, setIndexBiz] = useState(0); const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { @@ -59,12 +61,8 @@ export function PbnstRegModal({ console.log("form errors:", form.formState.errors); }, [form.formState.errors]); - useEffect(() => { - const firstForm = form.getValues("items"); - if (firstForm) { - setIndexMng(firstForm.length - 1); - } - }, [form]); + + return ( <>

diff --git a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx index d7ad10a76..4c06f5722 100644 --- a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx +++ b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx @@ -169,7 +169,7 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" }; const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" }; - if (prfList) { + if (prfList&&prfList.length>0) { prfList?.forEach((prfItem) => { const month = Number(prfItem?.ym?.slice(4)); // "MM" const monthKey = `${month}월`; @@ -220,12 +220,16 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; outPrf: outUsageData, totalPrf: tableData, }); + }else{ + setPrfData(undefined); } }; useEffect(() => { if(isMap){ if(data.prfList &&data.prfList.length>0){ getTablePrfData(data.prfList); + }else{ + getTablePrfData([]); } }else{ @@ -250,16 +254,17 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; } }, [data, form]) + const Wrapper: React.ElementType = isMap ?"div": ScrollArea; return ( <> -
+
{titleVisible && ( <> </> )} - <ScrollArea className={ isMap==true? "h-[100vh]":"h-[70vh]" }> + <Wrapper className={ "h-[70vh]" }> <PbnstMngForm form={form} formType={"sttn"} @@ -289,7 +294,7 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; 재이용 수요처 현황 </TabsTrigger> </TabsList> - <TabsContent className="max-h-full" value={fcltTab}> + <TabsContent className="min-h-0" value={fcltTab}> <UiTable headName={undefined} tableData={ruseTableData || []} @@ -322,10 +327,10 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; 장외용수 </TabsTrigger> </TabsList> - <TabsContent className="max-h-full" value={ruseTab}> + <TabsContent className="min-h-0" value={ruseTab}> {prfData && ( <ListDiv className="col-span-8 pr-6 "> - <ScrollArea className="h-[60vh]"> + {/* <ScrollArea className="h-[60vh]"> */} <div className="card-header"> <h3 className="text-2xl font-medium text-label"> {"하수처리수재이용 실적(m³) 등록결과"} @@ -339,13 +344,13 @@ export function PbnstSttnModal({ data, titleVisible,isMap}: { data: customPbnst; {InputTableBody(prfData[ruseTab as keyof typeof prfData] || [], pbnstPrfmncSttn.monthSttnHeadList, setPrfData as any)} </TableBody> </Table> - </ScrollArea> + {/* </ScrollArea> */} </ListDiv> )} </TabsContent> </Tabs> </div> - </ScrollArea> + </Wrapper> </div> </> ); diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx index dfe88413b..2edb47844 100644 --- a/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx @@ -5,7 +5,7 @@ import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; import { rainChkTableData } from "@/components/lib/func/TableData"; -import { ScrollArea, Title, UiTable } from "@common/business_components/ui"; +import { Title, UiTable } from "@common/business_components/ui"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import { useEffect, useState } from "react"; import HtdflRegForm from "../../../form/fcltReg/htdflReg/htdflRegForm"; @@ -89,13 +89,13 @@ export default function SttnDetailModal({ : "온배수시설 상세정보" } > - 0 ? "h-[80vh]" : "h-[70vh]" } - > + > */} {type === "RNB" ? ( )} - + {/* */}
); } diff --git a/apps/wr/eslint.config.mjs b/apps/wr/eslint.config.mjs index 73d6b7492..40c800c78 100644 --- a/apps/wr/eslint.config.mjs +++ b/apps/wr/eslint.config.mjs @@ -4,6 +4,7 @@ import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const isProd = process.env.NODE_ENV === "production"; const compat = new FlatCompat({ baseDirectory: __dirname, @@ -14,6 +15,7 @@ const eslintConfig = [ {rules:{ // other rules.. "@typescript-eslint/no-explicit-any": "off", + ...(isProd ? { "react-hooks/exhaustive-deps": "off" } : {}), }}, ]; diff --git a/apps/wr/public/sample_hasuMap.xlsx b/apps/wr/public/sample_hasuMap.xlsx new file mode 100644 index 000000000..70fe3a0d1 Binary files /dev/null and b/apps/wr/public/sample_hasuMap.xlsx differ diff --git a/apps/wr/public/sample_rainy.xlsx b/apps/wr/public/sample_rainy.xlsx index ec7577c51..3ab349569 100644 Binary files a/apps/wr/public/sample_rainy.xlsx and b/apps/wr/public/sample_rainy.xlsx differ diff --git a/packages/common/business_components/src/ui/dialog_ui/index.tsx b/packages/common/business_components/src/ui/dialog_ui/index.tsx index 0858a66b8..4201cbdfa 100644 --- a/packages/common/business_components/src/ui/dialog_ui/index.tsx +++ b/packages/common/business_components/src/ui/dialog_ui/index.tsx @@ -41,7 +41,7 @@ export function UiModal({ {(open && body) ? useDirectRender ? body - :
{body}
+ : <>{body} : <> } {noFooterYn === 'N' && ( diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 71f9fccf3..276733475 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -21,6 +21,7 @@ import GisToolBar from "./GisToolBar"; import GisTree from "./GisTree"; import "./lib/css/gis.common.css"; import GisApp from "./lib/gis.core"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; export const GisContext = createContext({ gisCore: null, @@ -124,7 +125,9 @@ function Gis({ checkInterval = 3000, height: height = "h-[calc(100vh-5rem)]", onOpenPopup = (data) => { }, - droughtDates = { startYmd: '', endYmd: '' } + droughtDates = { startYmd: '', endYmd: '' }, + defaultZoom = 15, + defaultLonLat = [127.4875, 37.4916], }) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); @@ -149,6 +152,7 @@ function Gis({ // 체크된 레이어 상태 관리 const [checkedLayers, setCheckedLayers] = useState([]); const [checkedLayersData, setCheckedLayersData] = useState([]); + const [isLoading, setIsLoading] = useState(true) //로딩 상태 let gisMap = null; const closePopup = () => { @@ -195,9 +199,9 @@ function Gis({ }).extend([]), view: new View({ projection: GisApp.TO_PROJ, - zoom: gisCore.defaults.zoom, + zoom: defaultZoom ?? gisCore.defaults.zoom, center: olProj.transform( - [127.4875, 37.4916], + defaultLonLat, GisApp.FROM_PROJ, GisApp.TO_PROJ, ), @@ -206,12 +210,16 @@ function Gis({ }), }); } else { - // 📌 재마운트 시 target 없으면 다시 지정 + // 재마운트 시 target 없으면 다시 지정 if (!map.getTarget()) { map.setTarget(mapRef.current); } } + map.on('loadstart', function () { + setIsLoading(false) + }); + const select = GisApp.Module.select; if (select) { if (select.selectInteraction) { @@ -322,6 +330,7 @@ function Gis({ return ( <> + {}, + setCheckedNodes = () => { }, visible, setVisible, onCheckedChange, onLayerIconClick, imageVisible = false, isPlaying = false, - setIsPlaying = () => {}, + setIsPlaying = () => { }, targetLayerTypes = [], orderedLayers = [], checkInterval = 3000, - onProgressChange = () => {}, - }) { + onProgressChange = () => { }, +}) { const { gisCore, treeData } = useGisContext(); + + // 체크/확장/노드/탭 상태 const [checked, setChecked] = useState([]); const [expanded, setExpanded] = useState([]); const [nodes, setNodes] = useState([]); const [activeTab, setActiveTab] = useState("layers"); - + + // 자동 애니메이션 관련 ref (실행 인덱스, 대상 노드 목록, 일시정지, 타이머) const currentAnimationIndexRef = useRef(0); const targetNodesRef = useRef([]); const isPausedRef = useRef(false); const timeoutIdRef = useRef(null); - - // ===== 유틸 ===== + + // ===================== + // 유틸리티: 트리 탐색 관련 함수들 + // ===================== + + /** + * 주어진 id의 모든 하위(자손) 노드 id를 평탄 배열로 반환합니다. + * @param {number|string} id 기준 노드 id + * @param {Array} all 전체 노드 배열 + * @returns {Array} 자손 노드 id 배열 + */ const getDescendants = (id, all) => { - const children = all.filter((n) => n.parent === id); - return children.reduce( - (acc, child) => acc.concat(child.id, ...getDescendants(child.id, all)), - [] - ); + const children = all.filter((n) => n.parent === id); + return children.reduce( + (acc, child) => acc.concat(child.id, ...getDescendants(child.id, all)), + [] + ); }; - + + /** + * 주어진 id의 모든 상위(조상) 노드 id를 상단부터 배열로 반환합니다. + * @param {number|string} id 기준 노드 id + * @param {Array} all 전체 노드 배열 + * @returns {Array} 조상 노드 id 배열 + */ const getAncestors = (id, all) => { - const node = all.find((n) => n.id === id); - if (!node || node.parent == null) return []; - return [node.parent, ...getAncestors(node.parent, all)]; + const node = all.find((n) => n.id === id); + if (!node || node.parent == null) return []; + return [node.parent, ...getAncestors(node.parent, all)]; }; - - const getChildren = (parentId, all) => - all.filter((n) => n.parent === parentId); - + + /** + * 특정 parentId의 직계 자식 노드 리스트를 반환합니다. + */ + const getChildren = (parentId, all) => all.filter((n) => n.parent === parentId); + + /** + * parentId의 모든 직계 자식이 checkedIds에 포함되어 있는지 확인합니다. + */ const areAllChildrenChecked = (parentId, all, checkedIds) => { - const children = getChildren(parentId, all); - return children.length > 0 && children.every((c) => checkedIds.includes(c.id)); + const children = getChildren(parentId, all); + return children.length > 0 && children.every((c) => checkedIds.includes(c.id)); }; - + + /** + * 어떤 노드의 체크 변경 이후, 상위 부모까지 올라가며 체크 상태를 전파/정리합니다. + * - 모든 자식이 체크되면 부모도 체크 + * - 일부라도 해제되면 부모 해제 + */ const updateParentCheckState = (nodeId, all, checkedIds) => { - const parent = all.find((n) => n.id === nodeId)?.parent; - if (parent == null) return checkedIds; - - if (areAllChildrenChecked(parent, all, checkedIds)) { - if (!checkedIds.includes(parent)) { - checkedIds.push(parent); - } - } else { - checkedIds = checkedIds.filter((id) => id !== parent); - } - return updateParentCheckState(parent, all, checkedIds); - }; - - const isEveryChildChecked = (parentId, all, checked) => { - const children = getChildren(parentId, all); - return children.length > 0 && children.every((c) => checked.includes(c.id)); - }; - - // ===== 기본 체크 노드 ===== - const getDefaultCheckedNodes = (nodes) => { - let checkedIds = nodes - .filter((n) => n.data.initEprssYn === "Y") - .map((n) => n.id); - - nodes.forEach((node) => { - checkedIds = updateParentCheckState(node.id, nodes, checkedIds); - }); - - return Array.from(new Set(checkedIds)); - }; - - // ===== 초기 로딩 ===== - useEffect(() => { - if (treeData && treeData.length > 0) { - const nodeData = setNodeValues(treeData); - setNodes(nodeData); - gisCore.setAllLayersOnMap(false); - } - }, [treeData]); - - useEffect(() => { - if (nodes.length > 0) { - const defaultChecked = getDefaultCheckedNodes(nodes); - setChecked(defaultChecked); - - const defaultExpanded = Array.from( - new Set([ - ...defaultChecked, - ...defaultChecked.flatMap((id) => getAncestors(id, nodes)), - ]) - ); - setExpanded(defaultExpanded); - - gisCore.setLayerOnMap(defaultChecked, true); - - const defaultCheckedNodesData = nodes.filter((node) => - defaultChecked.includes(node.id) - ); - onCheckedChange?.(defaultChecked, defaultCheckedNodesData); - } - }, [nodes]); - - // ===== 체크 이벤트 ===== - const handleCheck = (id) => { - setChecked((prevChecked) => { - let newChecked = [...prevChecked]; - const isChecked = prevChecked.includes(id); - - if (isChecked) { - const descendants = getDescendants(id, nodes); - newChecked = newChecked.filter( - (cid) => cid !== id && !descendants.includes(cid) - ); + const parent = all.find((n) => n.id === nodeId)?.parent; + if (parent == null) return checkedIds; + + if (areAllChildrenChecked(parent, all, checkedIds)) { + if (!checkedIds.includes(parent)) checkedIds.push(parent); } else { - const descendants = getDescendants(id, nodes); - newChecked = Array.from(new Set([...newChecked, id, ...descendants])); + checkedIds = checkedIds.filter((id) => id !== parent); } - - newChecked = updateParentCheckState(id, nodes, newChecked); - - const allIds = nodes.map((n) => n.id); - const unchecked = allIds.filter((nid) => !newChecked.includes(nid)); - gisCore.setLayerOnMap(newChecked, true); - gisCore.setLayerOnMap(unchecked, false); - - const checkedNodesData = nodes.filter((node) => - newChecked.includes(node.id) - ); - onCheckedChange?.(newChecked, checkedNodesData); - - return newChecked; - }); + return updateParentCheckState(parent, all, checkedIds); }; - - // ===== 특정 노드 체크/해제 ===== - const checkSpecificNode = useCallback( - (nodeId, shouldCheck = true) => { - setChecked((prevChecked) => { - let newChecked = [...prevChecked]; - const isCurrentlyChecked = prevChecked.includes(nodeId); - - if (shouldCheck && !isCurrentlyChecked) { - const descendants = getDescendants(nodeId, nodes); - newChecked = Array.from( - new Set([...newChecked, nodeId, ...descendants]) - ); - const ancestors = getAncestors(nodeId, nodes); - ancestors.forEach((p) => { - if (isEveryChildChecked(p, nodes, newChecked)) { - newChecked.push(p); - } - }); - } else if (!shouldCheck && isCurrentlyChecked) { - const descendants = getDescendants(nodeId, nodes); - newChecked = newChecked.filter( - (cid) => cid !== nodeId && !descendants.includes(cid) - ); - const ancestors = getAncestors(nodeId, nodes); - ancestors.forEach((p) => { - if (newChecked.includes(p)) { - newChecked = newChecked.filter((cid) => cid !== p); - } - }); - } - - const allIds = nodes.map((n) => n.id); - const unchecked = allIds.filter((nid) => !newChecked.includes(nid)); - gisCore.setLayerOnMap(newChecked, true); - gisCore.setLayerOnMap(unchecked, false); - - const checkedNodesData = nodes.filter((node) => - newChecked.includes(node.id) - ); - onCheckedChange?.(newChecked, checkedNodesData); - - return newChecked; + + /** + * parentId의 모든 직계 자식이 checked 배열에 포함되어 있는지 확인합니다. + */ + const isEveryChildChecked = (parentId, all, checked) => { + const children = getChildren(parentId, all); + return children.length > 0 && children.every((c) => checked.includes(c.id)); + }; + + // ===================== + // 기본 체크 노드 계산 + // ===================== + + /** + * 초기 렌더 시 기본으로 체크할 노드 id 목록을 반환합니다. + * - data.initEprssYn === 'Y' 인 노드 + 부모 전파 + */ + const getDefaultCheckedNodes = (nodes) => { + let checkedIds = nodes + .filter((n) => n.data.initEprssYn === "Y") + .map((n) => n.id); + + nodes.forEach((node) => { + checkedIds = updateParentCheckState(node.id, nodes, checkedIds); }); - }, - [nodes] + + return Array.from(new Set(checkedIds)); + }; + + // ===================== + // 초기 로딩: 트리 데이터 세팅 & 초기 체크/확장 & 지도 반영 + // ===================== + + /** + * treeData가 들어오면 노드 변환 후, 모든 레이어를 지도에서 잠시 비활성화합니다. + */ + useEffect(() => { + if (treeData && treeData.length > 0) { + const nodeData = setNodeValues(treeData); + setNodes(nodeData); + gisCore.setAllLayersOnMap(false); + } + }, [treeData]); + + /** + * nodes가 준비되면 기본 체크/확장 상태를 계산하고, 실제 지도 레이어 on/off 반영 및 콜백 호출. + */ + useEffect(() => { + if (nodes.length > 0) { + const defaultChecked = getDefaultCheckedNodes(nodes); + setChecked(defaultChecked); + + // 기본 확장 = 체크된 노드 + 그 조상들 + const defaultExpanded = Array.from( + new Set([ + ...defaultChecked, + ...defaultChecked.flatMap((id) => getAncestors(id, nodes)), + ]) + ); + setExpanded(defaultExpanded); + + // 지도 반영 + gisCore.setLayerOnMap(defaultChecked, true); + + const defaultCheckedNodesData = nodes.filter((node) => + defaultChecked.includes(node.id) + ); + onCheckedChange?.(defaultChecked, defaultCheckedNodesData); + } + }, [nodes]); + + // ===================== + // 체크 이벤트: 트리에서 체크 박스 클릭 시 처리 + // ===================== + + /** + * 단일 노드 체크/해제 처리 핸들러 + * - 체크 시: 자신 + 모든 자손 체크 + * - 해제 시: 자신 + 모든 자손 해제 + * - 이후 부모 상태 전파 및 지도 레이어 on/off 반영, 콜백 호출 + */ + const handleCheck = (id) => { + setChecked((prevChecked) => { + let newChecked = [...prevChecked]; + const isChecked = prevChecked.includes(id); + + if (isChecked) { + // 해제: 자신 + 자손 제거 + const descendants = getDescendants(id, nodes); + newChecked = newChecked.filter( + (cid) => cid !== id && !descendants.includes(cid) + ); + } else { + // 체크: 자신 + 자손 추가 + const descendants = getDescendants(id, nodes); + newChecked = Array.from(new Set([...newChecked, id, ...descendants])); + } + + // 부모 전파 (모든 자식 체크 시 부모 체크) + newChecked = updateParentCheckState(id, nodes, newChecked); + + // 지도 반영 + const allIds = nodes.map((n) => n.id); + const unchecked = allIds.filter((nid) => !newChecked.includes(nid)); + gisCore.setLayerOnMap(newChecked, true); + gisCore.setLayerOnMap(unchecked, false); + + // 콜백 + const checkedNodesData = nodes.filter((node) => newChecked.includes(node.id)); + onCheckedChange?.(newChecked, checkedNodesData); + + return newChecked; + }); + }; + + // ===================== + // 외부 호출용: 특정 노드 강제 체크/해제 + // ===================== + + /** + * 특정 nodeId를 강제로 체크/해제합니다. + * @param {number|string} nodeId 대상 노드 id + * @param {boolean} shouldCheck true면 체크, false면 해제 + * + * 내부적으로 handleCheck와 동일한 부모 전파/지도 반영/콜백 과정을 수행합니다. + */ + const checkSpecificNode = useCallback( + (nodeId, shouldCheck = true) => { + setChecked((prevChecked) => { + let newChecked = [...prevChecked]; + const isCurrentlyChecked = prevChecked.includes(nodeId); + + if (shouldCheck && !isCurrentlyChecked) { + // 체크: 자신 + 자손 추가, 부모 전파 + const descendants = getDescendants(nodeId, nodes); + newChecked = Array.from(new Set([...newChecked, nodeId, ...descendants])); + const ancestors = getAncestors(nodeId, nodes); + ancestors.forEach((p) => { + if (isEveryChildChecked(p, nodes, newChecked)) newChecked.push(p); + }); + } else if (!shouldCheck && isCurrentlyChecked) { + // 해제: 자신 + 자손 제거, 부모 해제 + const descendants = getDescendants(nodeId, nodes); + newChecked = newChecked.filter( + (cid) => cid !== nodeId && !descendants.includes(cid) + ); + const ancestors = getAncestors(nodeId, nodes); + ancestors.forEach((p) => { + if (newChecked.includes(p)) newChecked = newChecked.filter((cid) => cid !== p); + }); + } + + // 지도 반영 + const allIds = nodes.map((n) => n.id); + const unchecked = allIds.filter((nid) => !newChecked.includes(nid)); + gisCore.setLayerOnMap(newChecked, true); + gisCore.setLayerOnMap(unchecked, false); + + // 콜백 + const checkedNodesData = nodes.filter((node) => newChecked.includes(node.id)); + onCheckedChange?.(newChecked, checkedNodesData); + + return newChecked; + }); + }, + [nodes] ); - - // ===== 자동 애니메이션 ===== + + // ===================== + // 자동 애니메이션: 지정된 순서/간격으로 노드 체크 상태를 순차 변경 + // ===================== + + /** + * 레이어 자동 토글 애니메이션을 시작/중지/초기화합니다. + * @param {boolean} isEnabled 재생 여부 + * @param {string[]} layerTypes 대상 레이어 타입 목록 (node.data.lyrTyNm 기준) + * @param {number} interval 토글 간격(ms) + */ const handleAutoCheckAnimation = useCallback( - (isEnabled, layerTypes = [], interval = 3000) => { - if (timeoutIdRef.current) { - clearTimeout(timeoutIdRef.current); - timeoutIdRef.current = null; - } - - if (!isEnabled || layerTypes.length === 0) { - isPausedRef.current = false; - targetNodesRef.current = []; - currentAnimationIndexRef.current = 0; - onProgressChange(0, 0); - return; - } - - const getTargetNodesInOrder = () => { - const orderedNodes = []; - orderedLayers.forEach((lyrSn) => { - const node = nodes.find( - (n) => n.id === lyrSn && layerTypes.includes(n.data?.lyrTyNm) - ); - if (node) { - orderedNodes.push(node); + (isEnabled, layerTypes = [], interval = 3000) => { + // 기존 타이머 정리 + if (timeoutIdRef.current) { + clearTimeout(timeoutIdRef.current); + timeoutIdRef.current = null; } - }); - return orderedNodes; - }; - - const targetNodes = getTargetNodesInOrder(); - - if (targetNodes.length === 0) return; - - if (targetNodesRef.current.length === 0) { - targetNodesRef.current = targetNodes; - currentAnimationIndexRef.current = 0; - onProgressChange(targetNodes.length, 0); - targetNodes.forEach((node) => { - checkSpecificNode(node.id, false); - }); - } - - isPausedRef.current = false; - - const processNextLayer = () => { - if (isPausedRef.current || !isPlaying) return; - - const currentTargetNodes = targetNodesRef.current; - const currentIndex = currentAnimationIndexRef.current; - - if (currentIndex < currentTargetNodes.length) { - checkSpecificNode(currentTargetNodes[currentIndex].id, true); - if (currentIndex > 0) { - checkSpecificNode(currentTargetNodes[currentIndex - 1].id, false); + + // 중지/초기화 경로 + if (!isEnabled || layerTypes.length === 0) { + isPausedRef.current = false; + targetNodesRef.current = []; + currentAnimationIndexRef.current = 0; + onProgressChange(0, 0); + return; } - - currentAnimationIndexRef.current++; - onProgressChange( - currentTargetNodes.length, - currentAnimationIndexRef.current - ); - timeoutIdRef.current = setTimeout(processNextLayer, interval); - } else { + + // orderedLayers 순서를 우선 적용하면서, 타입 필터에 맞는 타겟 노드만 선별 + const getTargetNodesInOrder = () => { + const orderedNodes = []; + orderedLayers.forEach((lyrSn) => { + const node = nodes.find( + (n) => n.id === lyrSn && layerTypes.includes(n.data?.lyrTyNm) + ); + if (node) orderedNodes.push(node); + }); + return orderedNodes; + }; + + const targetNodes = getTargetNodesInOrder(); + if (targetNodes.length === 0) return; + + // 최초 진입 시 전체 해제 후 시작 + if (targetNodesRef.current.length === 0) { + targetNodesRef.current = targetNodes; + currentAnimationIndexRef.current = 0; + onProgressChange(targetNodes.length, 0); + targetNodes.forEach((node) => { + checkSpecificNode(node.id, false); + }); + } + isPausedRef.current = false; - targetNodesRef.current = []; - currentAnimationIndexRef.current = 0; - timeoutIdRef.current = null; - onProgressChange( - currentTargetNodes.length, - currentTargetNodes.length - ); - setTimeout(() => { - onProgressChange(0, 0); - }, 1000); - setIsPlaying?.(false); - } - }; - - processNextLayer(); - }, - [nodes, checkSpecificNode, setIsPlaying, isPlaying, onProgressChange] + + // 다음 레이어로 진행하는 내부 함수 + const processNextLayer = () => { + if (isPausedRef.current || !isPlaying) return; // 일시정지/정지 처리 + + const currentTargetNodes = targetNodesRef.current; + const currentIndex = currentAnimationIndexRef.current; + + if (currentIndex < currentTargetNodes.length) { + // 현재 타겟 on, 직전 타겟 off + checkSpecificNode(currentTargetNodes[currentIndex].id, true); + if (currentIndex > 0) { + checkSpecificNode(currentTargetNodes[currentIndex - 1].id, false); + } + + currentAnimationIndexRef.current++; + onProgressChange( + currentTargetNodes.length, + currentAnimationIndexRef.current + ); + timeoutIdRef.current = setTimeout(processNextLayer, interval); + } else { + // 종료 처리 및 상태 초기화 + isPausedRef.current = false; + targetNodesRef.current = []; + currentAnimationIndexRef.current = 0; + timeoutIdRef.current = null; + onProgressChange(currentTargetNodes.length, currentTargetNodes.length); + setTimeout(() => onProgressChange(0, 0), 1000); + setIsPlaying?.(false); + } + }; + + processNextLayer(); + }, + [nodes, checkSpecificNode, setIsPlaying, isPlaying, onProgressChange] ); - + + /** + * 자동 애니메이션 일시정지 + */ const pauseAnimation = useCallback(() => { - if (timeoutIdRef.current) { - clearTimeout(timeoutIdRef.current); - timeoutIdRef.current = null; - } - isPausedRef.current = true; - }, []); - - useEffect(() => { - if (isPlaying) - handleAutoCheckAnimation(true, targetLayerTypes, checkInterval); - else pauseAnimation(); - }, [ - isPlaying, - handleAutoCheckAnimation, - targetLayerTypes, - checkInterval, - pauseAnimation, - ]); - - useEffect(() => { - return () => { if (timeoutIdRef.current) { - clearTimeout(timeoutIdRef.current); + clearTimeout(timeoutIdRef.current); + timeoutIdRef.current = null; } - }; + isPausedRef.current = true; }, []); - - // ===== 노드 세팅 ===== + + /** + * 재생/정지/대상/간격 변경에 반응하여 애니메이션을 관리합니다. + */ + useEffect(() => { + if (isPlaying) + handleAutoCheckAnimation(true, targetLayerTypes, checkInterval); + else pauseAnimation(); + }, [ + isPlaying, + handleAutoCheckAnimation, + targetLayerTypes, + checkInterval, + pauseAnimation, + ]); + + /** + * 언마운트 시 타이머 정리. + */ + useEffect(() => { + return () => { + if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current); + }; + }, []); + + // ===================== + // 노드/아이콘 렌더링 데이터 준비 + // ===================== + + /** + * 서버에서 내려온 트리 데이터를 Tree 컴포넌트가 요구하는 구조로 변환합니다. + * - id, text, parent, droppable, data, icon + */ const setNodeValues = (result) => { return result.map((item) => { - const src = makeLegendSrc(item); - return { - id: item.lyrSn, // 반드시 고유값 - text: item.lyrNm, - parent: item.upperLyrSn || null, // 루트는 null - droppable: true, // children 여부 모를 땐 true로 둬야 트리 확장 가능 - data: item, - icon: src ? {item.lyrNm} : "", - }; + const src = makeLegendSrc(item); + return { + id: item.lyrSn, // 반드시 고유값 + text: item.lyrNm, + parent: item.upperLyrSn || null, // 루트는 null + droppable: true, // children 여부 모를 땐 true로 둬야 트리 확장 가능 + data: item, + icon: src ? {item.lyrNm} : "", + }; }); - }; - - + }; + + /** + * 레이어 정보로부터 범례 이미지 경로 또는 동적 생성 이미지를 반환합니다. + */ const makeLegendSrc = (layerinfo) => { - if (!layerinfo.gobjTy) return null; - if (layerinfo.iconNm) { - return GisApp.CONTEXT_LAYER_PATH + layerinfo.iconNm; - } else { - return makeLegendImage(layerinfo).src; - } + if (!layerinfo.gobjTy) return null; + if (layerinfo.iconNm) { + return GisApp.CONTEXT_LAYER_PATH + layerinfo.iconNm; + } else { + return makeLegendImage(layerinfo).src; + } }; - + + /** + * 레이어 스타일 정보를 기반으로 작은 캔버스에 간단한 범례 이미지를 그려 dataURL을 생성합니다. + */ const makeLegendImage = (layerinfo) => { - let image = document.createElement("img"); - let canvas = document.createElement("canvas"); - canvas.width = 16; - canvas.height = 16; - let ctx = canvas.getContext("2d"); - ctx.beginPath(); - - if (layerinfo.gobjTy === "lineLabelPoligon") { - ctx.moveTo(1, 1); - ctx.lineTo(15, 1); - ctx.lineTo(15, 15); - ctx.lineTo(1, 15); - ctx.lineTo(1, 1); - } else if (layerinfo.gobjTy === "line") { - ctx.moveTo(1, 15); - ctx.lineTo(5, 1); - ctx.lineTo(9, 15); - ctx.lineTo(15, 1); - } else { - ctx.moveTo(1, 1); - ctx.lineTo(15, 1); - ctx.lineTo(15, 15); - ctx.lineTo(1, 15); - ctx.lineTo(1, 1); - } - - if (layerinfo.gobjTy === "lineLabelPoligon") { - ctx.fillStyle = layerinfo.polgnColor; - ctx.fill(); - ctx.lineWidth = 1; - ctx.strokeStyle = layerinfo.lnColor; - ctx.stroke(); - } - if (layerinfo.gobjTy === "line") { - ctx.lineWidth = 1; - ctx.strokeStyle = layerinfo.lnColor; - ctx.stroke(); - } - - image.src = canvas.toDataURL("image/png"); - return image; + let image = document.createElement("img"); + let canvas = document.createElement("canvas"); + canvas.width = 16; + canvas.height = 16; + let ctx = canvas.getContext("2d"); + ctx.beginPath(); + + if (layerinfo.gobjTy === "lineLabelPoligon") { + ctx.moveTo(1, 1); + ctx.lineTo(15, 1); + ctx.lineTo(15, 15); + ctx.lineTo(1, 15); + ctx.lineTo(1, 1); + } else if (layerinfo.gobjTy === "line") { + ctx.moveTo(1, 15); + ctx.lineTo(5, 1); + ctx.lineTo(9, 15); + ctx.lineTo(15, 1); + } else { + ctx.moveTo(1, 1); + ctx.lineTo(15, 1); + ctx.lineTo(15, 15); + ctx.lineTo(1, 15); + ctx.lineTo(1, 1); + } + + if (layerinfo.gobjTy === "lineLabelPoligon") { + ctx.fillStyle = layerinfo.polgnColor; + ctx.fill(); + ctx.lineWidth = 1; + ctx.strokeStyle = layerinfo.lnColor; + ctx.stroke(); + } + if (layerinfo.gobjTy === "line") { + ctx.lineWidth = 1; + ctx.strokeStyle = layerinfo.lnColor; + ctx.stroke(); + } + + image.src = canvas.toDataURL("image/png"); + return image; }; - + + /** + * 각 레이어 행의 아이콘 클릭 시 호출. 선택되지 않은 레이어면 경고, 선택된 경우 콜백 전달. + */ const handleLayerImgClick = (nodeNm, nodeId) => { - if (!checked.includes(nodeId)) alert("시설을 선택해주세요"); - else onLayerIconClick(nodeNm); + if (!checked.includes(nodeId)) alert("시설을 선택해주세요"); + else onLayerIconClick(nodeNm); }; - + + // ===================== + // 렌더링 + // ===================== + if (!treeData || treeData.length === 0) return null; - + return ( - <> -
-
-

시설물

-
- - { - const isVisible = node.data.exprsYn === "Y"; - const hasVisibleChildren = nodes.some( - (child) => - child.parent === node.id && child.data.exprsYn === "Y" - ); - - return ( -
- {hasVisibleChildren ? ( - + {/* 사이드 패널 */} +
+
+

시설물

+
+ {/* DnD Tree */} + + { + const isVisible = node.data.exprsYn === "Y"; + const hasVisibleChildren = nodes.some( + (child) => + child.parent === node.id && child.data.exprsYn === "Y" + ); + + return ( +
+ {hasVisibleChildren ? ( + + ) : ( + + )} + handleCheck(node.id)} + className="cursor-pointer" + /> + {node.icon && {node.icon}} + + {node.text} + + {imageVisible && depth > 0 && ( + + + handleLayerImgClick( + node.data.lyrTyNm, + node.id + ) + } + alt={node.text} + /> + + )} +
+ ); + }} + sort={(a, b) => { + const aOrder = parseInt(a.data?.sortOrdr ?? "0"); + const bOrder = parseInt(b.data?.sortOrdr ?? "0"); + return aOrder - bOrder; + }} /> - ) : ( - - )} - handleCheck(node.id)} - className="cursor-pointer" - /> - {node.icon && {node.icon}} - - {node.text} - - {imageVisible && depth > 0 && ( - - - handleLayerImgClick( - node.data.lyrTyNm, - node.id - ) - } - alt={node.text} - /> - - )} -
- ); - }} - sort={(a, b) => { - const aOrder = parseInt(a.data?.sortOrdr ?? "0"); - const bOrder = parseInt(b.data?.sortOrdr ?? "0"); - return aOrder - bOrder; - }} - /> - -
- -
- {!visible && ( - - )} - +
+
+ +
+ {!visible && ( + + )} + ); - }); - \ No newline at end of file +}); diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index 5a6e705da..ad3db214a 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -650,7 +650,7 @@ GisApp.core.prototype = { */ getFeatureGeomProp: function (geometry, ftrIds, ftrCde) { let returnFeature; - this.map.getLayers().forEach(function (layer) { + this.map.getLayers().getArray().some(function (layer) { let type = layer.get('type'); if (type != 'base') { let source = layer.getSource(); @@ -673,6 +673,7 @@ GisApp.core.prototype = { featureFtrCde = properties['FTR_CDE']; if (ftrIds == featureFtrIds && ftrCde == featureFtrCde) { returnFeature = feature; + return true; } } } @@ -685,7 +686,7 @@ GisApp.core.prototype = { */ getFeatureByFtrIdn: function (typeName, ftrIdn) { let returnFeature; - this.map.getLayers().forEach(function (layer) { + this.map.getLayers().getArray().some(function (layer) { let layerTypeName = layer.get('typeName'); if (layerTypeName === typeName) { layer @@ -695,12 +696,16 @@ GisApp.core.prototype = { let featureFtrIdn = feature.get('ftrIdn'); if (featureFtrIdn === ftrIdn) { returnFeature = feature; + return true; } }); } }); return returnFeature; }, + getFeatureByFeatureId: function (featureId) { + return GisApp.Module.layer.getFeatureById(featureId); + }, /** * [사용]맵 이벤트 추가 */ diff --git a/packages/common/business_components/src/ui/map/lib/gis.layer.js b/packages/common/business_components/src/ui/map/lib/gis.layer.js index 6cbc054f8..f8fdb2a24 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.layer.js +++ b/packages/common/business_components/src/ui/map/lib/gis.layer.js @@ -33,6 +33,7 @@ const GisOlLayer = function (options) { GisOlLayer.prototype = { layers: [], cesiumLayers: [], + searchLayer: null, defaults: {}, storageNm: 'hasudo:', defaultLayerOptions: { @@ -59,6 +60,11 @@ GisOlLayer.prototype = { zIndex: options?.drawOrdr ? options?.drawOrdr : 1, }; let returnLayer = new VectorImageLayer(layerOptions); + + //스타일 추가 + returnLayer.setStyle(this.gisStyle.getLayerStyleFunction(returnLayer, options)); + returnLayer.set('id', options.id + ''); + this.layers.push(returnLayer); return returnLayer; }, @@ -76,7 +82,7 @@ GisOlLayer.prototype = { let toProj = GisApp.TO_PROJ; let lyrTyNm = options.lyrTyNm; let typename = this.storageNm + lyrTyNm; - + const params = {}; params['TYPENAME'] = typename; params['SERVICE'] = 'WFS'; @@ -92,11 +98,11 @@ GisOlLayer.prototype = { const [minX, minY, maxX, maxY] = extent; const bboxFilter = `BBOX(geom, ${minX}, ${minY}, ${maxX}, ${maxY}, '${toProj}')`; const filter = options?.flterSql && options.flterSql && options.flterSql.trim() !== '' - ? `(${options.flterSql}) AND ${bboxFilter}` - : bboxFilter; + ? `(${options.flterSql}) AND ${bboxFilter}` + : bboxFilter; params['CQL_FILTER'] = filter; - + const strParams = new URLSearchParams(params).toString(); return url + '?' + strParams; }, @@ -107,13 +113,13 @@ GisOlLayer.prototype = { /** * [사용]WFS 벡터 소스 반환 */ - getVectorTileSource: function (options) { + getVectorSourceAll: function (options) { let url = GisApp.GEOSERVER_URL; let toProj = GisApp.TO_PROJ; let lyrTyNm = options.lyrTyNm; let typename = this.storageNm + lyrTyNm; let filter = options?.flterSql && options.flterSql && options.flterSql.trim() !== ''; - + let params = {}; params['TYPENAME'] = typename; params['SERVICE'] = 'WFS'; @@ -150,19 +156,19 @@ GisOlLayer.prototype = { let cmncNm = option.cmncNm; try { - if(cmncNm == 'bbox') { + if (cmncNm == 'bbox') { source = this.getVectorSourceBbox(option); - }else { - source = this.getVectorTileSource(option); + } else { + source = this.getVectorSourceAll(option); } - }catch(error){ + } catch (error) { console.warn(option); } returnLayer.setSource(source); //스타일 추가 - returnLayer.setStyle(this.gisStyle.getLayerStyleFunction(returnLayer, option)); - returnLayer.set('id', option.id + ''); + // returnLayer.setStyle(this.gisStyle.getLayerStyleFunction(returnLayer, option)); + // returnLayer.set('id', option.id + ''); return returnLayer; }, /** @@ -548,7 +554,7 @@ GisOlLayer.prototype = { cesiumLayer['properties'] = properties; me.cesiumLayers.push(cesiumLayer); if (properties?.visible) { - const zoomLevel = getOpenLayersZoom(cesiumScene); + const zoomLevel = this.getOpenLayersZoom(cesiumScene); cesiumLayer.show = zoomLevel >= Number(properties?.minZoom); } else { cesiumLayer.show = false; @@ -557,7 +563,7 @@ GisOlLayer.prototype = { }); if (me.cesiumLayers.length > 0) { cesiumScene._moveEndListener = () => { - const zoomLevel = getOpenLayersZoom(cesiumScene); // 대략적인 줌 계산 + const zoomLevel = this.getOpenLayersZoom(cesiumScene); // 대략적인 줌 계산 me.cesiumLayers.forEach(cesiumLayer => { const properties = cesiumLayer.properties; if (properties?.visible) { @@ -569,22 +575,121 @@ GisOlLayer.prototype = { cesiumScene.camera.moveEnd.addEventListener(cesiumScene._moveEndListener); } }, - getFeatureById: function(featureId){ - this.layers - .getArray() - .forEach(layer => { - const source = layer.getSource(); - const feature = source.getFeatureById(String(featureId).trim()); - if (feature) return feature; - }); + getFeatureById: function (featureId) { + let returnFeature; + this.layers.getArray().some(function (layer) { + const source = layer.getSource(); + + if (source instanceof VectorSource) { + const feature = source.getFeatureById(featureId); + if (feature) { + returnFeature = feature; + return true; + } + } + }); + + return returnFeature; + }, + getOpenLayersZoom: function (cesiumScene) { + const cameraHeight = cesiumScene.camera.positionCartographic.height; + const maxResolution = 156543.03392804097; // OpenLayers 기본 해상도 (Google Mercator 기준) + const tileSize = 256; // 기본 타일 크기 + return Math.ceil(Math.log2(maxResolution / (cameraHeight / tileSize))); + }, + ensureSearchLayer: function () { + let layer = this.searchLayer; + + if (!layer) { + layer = new VectorLayer({ + source: new VectorSource(), + zIndex: 9999 + }); + let map = GisApp.Module.core.map; + map.addLayer(layer); + } + return layer; + }, + clearSearchLayer: function () { + const layer = this.ensureSearchLayer(); + layer.getSource().clear(true); + }, + showSearchResultsFromGeoJSON: function (lyrTyNm, featureId) { + const layer = this.ensureSearchLayer(); + + // 이전 검색결과는 깨끗이 + const prev = layer.getSource(); + if (prev) prev.clear(true); + + //레이어 타입 설정(point, line 등. 이걸 style에서 사용) + const layerCode = GisApp.layerCode; + const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm); + layer.set("gobjTy", matched.gobjTy); + + // 새 소스 만들기 + const source = this.getFeatureSourceFromGeoserver(lyrTyNm, featureId); + + // 1) 로드 완료시 extent 맞추기 + const onLoadEnd = () => { + const feats = source.getFeatures(); + if (feats && feats.length > 0) { + const select = GisApp.Module.select; + select.selectFeature(feats[0], false); + } else { + console.warn('[search-layer] GeoJSON에 피처가 없습니다.'); + } + // 한번만 쓰고 이벤트 정리 + source.un('featuresloadend', onLoadEnd); + source.un('featuresloaderror', onLoadError); + }; + + const onLoadError = (e) => { + console.error('[search-layer] GeoJSON 로드 실패:', e); + source.un('featuresloadend', onLoadEnd); + source.un('featuresloaderror', onLoadError); + }; + + // 이벤트를 먼저 걸고 → setSource + source.once('featuresloadend', onLoadEnd); + source.once('featuresloaderror', onLoadError); + + // 레이어에 소스 적용(이때부터 로딩 시작) + layer.setSource(source); + + // 필요시 명시적으로 재요청 + source.refresh(); + }, + /** + * 피쳐 ID로 지오서버에서 검색해서 피처의 벡터소스를 리턴 + * @param {} lyrTyNm + * @param {*} featureId + * @returns + */ + getFeatureSourceFromGeoserver: function (lyrTyNm, featureId) { + + const typeName = this.storageNm + lyrTyNm; + const srsName = GisApp.TO_PROJ; + const baseUrl = GisApp.GEOSERVER_URL; + const source = new VectorSource({ + format: new GeoJSON(), + url: () => { + const params = new URLSearchParams({ + SERVICE: 'WFS', + VERSION: '1.1.0', + REQUEST: 'GetFeature', + TYPENAME: typeName, + SRSNAME: srsName, + OUTPUTFORMAT: 'application/json', + FEATUREID: featureId, + }); + return `${baseUrl}?${params.toString()}`; + }, + strategy: all, + }); + + return source; } }; -function getOpenLayersZoom(cesiumScene) { - const cameraHeight = cesiumScene.camera.positionCartographic.height; - const maxResolution = 156543.03392804097; // OpenLayers 기본 해상도 (Google Mercator 기준) - const tileSize = 256; // 기본 타일 크기 - return Math.ceil(Math.log2(maxResolution / (cameraHeight / tileSize))); -} export default GisOlLayer; diff --git a/packages/common/business_components/src/ui/map/searchBar/comboSearchBar.tsx b/packages/common/business_components/src/ui/map/searchBar/comboSearchBar.tsx index 3289ae53f..e59fd762c 100644 --- a/packages/common/business_components/src/ui/map/searchBar/comboSearchBar.tsx +++ b/packages/common/business_components/src/ui/map/searchBar/comboSearchBar.tsx @@ -57,10 +57,47 @@ export const ComboSearchBar = ({ 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) => { + // 커스텀 순서 정의 + const customOrder = [ + "서울특별시", + "부산광역시", + "대구광역시", + "인천광역시", + "광주광역시", + "대전광역시", + "울산광역시", + "세종특별자치시", + "경기도", + "강원특별자치도", // 경기도 다음에 배치 + "충청북도", + "충청남도", + "전북특별자치도", // 충청남도 다음에 배치 + "전라남도", + "경상북도", + "경상남도", + "제주특별자치도" + ]; + + const indexA = customOrder.indexOf(a.text); + const indexB = customOrder.indexOf(b.text); + + // 둘 다 커스텀 순서에 있으면 해당 순서로 + if (indexA !== -1 && indexB !== -1) { + return indexA - indexB; + } + + // 하나만 커스텀 순서에 있으면 그것을 앞으로 + if (indexA !== -1) return -1; + if (indexB !== -1) return 1; + + // 둘 다 커스텀 순서에 없으면 기본 알파벳 순서 + return a.text.localeCompare(b.text, 'ko'); + }); setSidoData([{ text: "선택", val: "0" }, ...list]); }; @@ -68,10 +105,12 @@ export const ComboSearchBar = ({ 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); }; 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..465471715 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,48 @@ 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) => { + // 커스텀 순서 정의 + const customOrder = [ + "서울특별시", + "부산광역시", + "대구광역시", + "인천광역시", + "광주광역시", + "대전광역시", + "울산광역시", + "세종특별자치시", + "경기도", + "강원특별자치도", // 경기도 다음에 배치 + "충청북도", + "충청남도", + "전북특별자치도", // 충청남도 다음에 배치 + "전라남도", + "경상북도", + "경상남도", + "제주특별자치도" + ]; + + const indexA = customOrder.indexOf(a.text); + const indexB = customOrder.indexOf(b.text); + + // 둘 다 커스텀 순서에 있으면 해당 순서로 + if (indexA !== -1 && indexB !== -1) { + return indexA - indexB; + } + + // 하나만 커스텀 순서에 있으면 그것을 앞으로 + if (indexA !== -1) return -1; + if (indexB !== -1) return 1; + + // 둘 다 커스텀 순서에 없으면 기본 알파벳 순서 + return a.text.localeCompare(b.text, 'ko'); + }); setSidoData([{ text: "시도", val: "0" }, ...list]); }; @@ -54,10 +92,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 +130,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 +186,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"); }; diff --git a/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx b/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx index 623758ec3..305c0afe1 100644 --- a/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx +++ b/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx @@ -60,10 +60,48 @@ export const SearchBar = ({ const json = await res.json(); const sidoList = json .filter((item: any) => item.lwpoCd?.length === 2) - .map((item: any) => ({ val: item.lwpoCd, text: item.lwpoCdNm })); + .map((item: any) => ({ val: item.lwpoCd, text: item.lwpoCdNm })) + .sort((a, b) => { + // 커스텀 순서 정의 + const customOrder = [ + "서울특별시", + "부산광역시", + "대구광역시", + "인천광역시", + "광주광역시", + "대전광역시", + "울산광역시", + "세종특별자치시", + "경기도", + "강원특별자치도", // 경기도 다음에 배치 + "충청북도", + "충청남도", + "전북특별자치도", // 충청남도 다음에 배치 + "전라남도", + "경상북도", + "경상남도", + "제주특별자치도" + ]; + + const indexA = customOrder.indexOf(a.text); + const indexB = customOrder.indexOf(b.text); + + // 둘 다 커스텀 순서에 있으면 해당 순서로 + if (indexA !== -1 && indexB !== -1) { + return indexA - indexB; + } + + // 하나만 커스텀 순서에 있으면 그것을 앞으로 + if (indexA !== -1) return -1; + if (indexB !== -1) return 1; + + // 둘 다 커스텀 순서에 없으면 기본 알파벳 순서 + return a.text.localeCompare(b.text, 'ko'); + }); const sigunList = json .filter((item: any) => item.lwpoCd?.length > 2) - .map((item: any) => ({ val: item.lwpoCd, text: item.lwpoCdNm })); + .map((item: any) => ({ val: item.lwpoCd, text: item.lwpoCdNm })) + .sort((a, b) => a.text.localeCompare(b.text, 'ko')); setSidoData([{ text: "시도", val: "0" }, ...sidoList]); setSigunData(sigunList); }; diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index fa7c9b7fe..dcf7f4aa4 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -30,6 +30,7 @@ type UiTableProps = { boldArray?: number[]; resetSelection?: boolean; index?: number; + headButton?: React.ReactNode | undefined; }; type UiHeadProps = { @@ -53,6 +54,7 @@ export function UiTable({ boldArray = [], resetSelection = false, index = -1, + headButton = undefined, }: UiTableProps) { const [cur, setCur] = useState(1); const [idx, setIdx] = useState(index); @@ -100,9 +102,16 @@ export function UiTable({ return ( <> {headName ? ( -
-

{headName}

-
+ headButton ? ( +
+

{headName}

+ <>{headButton} +
+ ) : ( +
+

{headName}

+
+ ) ) : ( <> )} diff --git a/packages/common/components/src/ui/dialog/index.tsx b/packages/common/components/src/ui/dialog/index.tsx index 1b6ff30f9..2f4bedd3d 100644 --- a/packages/common/components/src/ui/dialog/index.tsx +++ b/packages/common/components/src/ui/dialog/index.tsx @@ -37,7 +37,7 @@ const DialogContent = React.forwardRef< =21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.33: + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.39.0(eslint@9.33.0)(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.25.0 - '@typescript-eslint/type-utils': 8.25.0(eslint@9.33.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.25.0(eslint@9.33.0)(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.25.0 - eslint: 9.33.0 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.6.3) - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color + '@types/yargs-parser': 21.0.3 dev: true /@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0)(eslint@9.33.0)(typescript@5.6.3): @@ -5089,14 +5081,6 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@8.25.0: - resolution: {integrity: sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/visitor-keys': 8.25.0 - dev: true - /@typescript-eslint/scope-manager@8.39.0: resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5123,23 +5107,6 @@ packages: typescript: 5.9.2 dev: true - /@typescript-eslint/type-utils@8.25.0(eslint@9.33.0)(typescript@5.6.3): - resolution: {integrity: sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.25.0(eslint@9.33.0)(typescript@5.6.3) - debug: 4.4.1 - eslint: 9.33.0 - ts-api-utils: 2.1.0(typescript@5.6.3) - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/type-utils@8.39.0(eslint@9.33.0)(typescript@5.6.3): resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5158,35 +5125,11 @@ packages: - supports-color dev: true - /@typescript-eslint/types@8.25.0: - resolution: {integrity: sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: true - /@typescript-eslint/types@8.39.0: resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true - /@typescript-eslint/typescript-estree@8.25.0(typescript@5.6.3): - resolution: {integrity: sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/visitor-keys': 8.25.0 - debug: 4.4.1 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.6.3) - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/typescript-estree@8.39.0(typescript@5.6.3): resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5229,23 +5172,6 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@8.25.0(eslint@9.33.0)(typescript@5.6.3): - resolution: {integrity: sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0) - '@typescript-eslint/scope-manager': 8.25.0 - '@typescript-eslint/types': 8.25.0 - '@typescript-eslint/typescript-estree': 8.25.0(typescript@5.6.3) - eslint: 9.33.0 - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/utils@8.39.0(eslint@9.33.0)(typescript@5.6.3): resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5263,14 +5189,6 @@ packages: - supports-color dev: true - /@typescript-eslint/visitor-keys@8.25.0: - resolution: {integrity: sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.25.0 - eslint-visitor-keys: 4.2.1 - dev: true - /@typescript-eslint/visitor-keys@8.39.0: resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6159,15 +6077,6 @@ packages: wrap-ansi: 7.0.0 dev: true - /cliui@9.0.1: - resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} - engines: {node: '>=20'} - dependencies: - string-width: 7.2.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 - dev: false - /clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -6651,6 +6560,11 @@ packages: csstype: 3.1.3 dev: false + /dotenv@17.2.1: + resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==} + engines: {node: '>=12'} + dev: false + /dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -6704,10 +6618,6 @@ packages: resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} dev: false - /emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} - dev: false - /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -7006,7 +6916,7 @@ packages: dependencies: '@next/eslint-plugin-next': 15.1.7 '@rushstack/eslint-patch': 1.12.0 - '@typescript-eslint/eslint-plugin': 8.25.0(@typescript-eslint/parser@8.39.0)(eslint@9.33.0)(typescript@5.6.3) + '@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0)(eslint@9.33.0)(typescript@5.6.3) '@typescript-eslint/parser': 8.39.0(eslint@9.33.0)(typescript@5.6.3) eslint: 9.33.0 eslint-import-resolver-node: 0.3.9 @@ -7437,6 +7347,15 @@ packages: path-exists: 4.0.0 dev: true + /find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + dev: false + /flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -7571,11 +7490,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-east-asian-width@1.3.0: - resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} - engines: {node: '>=18'} - dev: false - /get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -8396,6 +8310,13 @@ packages: p-locate: 5.0.0 dev: true + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + dev: false + /lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: true @@ -8921,6 +8842,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.2.1 + dev: false + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -8935,6 +8863,13 @@ packages: p-limit: 3.1.0 dev: true + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + dev: false + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -8976,6 +8911,11 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -10236,15 +10176,6 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - /string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - dependencies: - emoji-regex: 10.4.0 - get-east-asian-width: 1.3.0 - strip-ansi: 7.1.0 - dev: false - /string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -10829,6 +10760,11 @@ packages: /undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + /unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + dev: false + /unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -11262,15 +11198,6 @@ packages: string-width: 5.1.2 strip-ansi: 7.1.0 - /wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} - engines: {node: '>=18'} - dependencies: - ansi-styles: 6.2.1 - string-width: 7.2.0 - strip-ansi: 7.1.0 - dev: false - /ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -11318,6 +11245,7 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + dev: true /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -11346,11 +11274,6 @@ packages: engines: {node: '>=12'} dev: true - /yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - dev: false - /yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} @@ -11381,23 +11304,16 @@ packages: yargs-parser: 21.1.1 dev: true - /yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - dependencies: - cliui: 9.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - string-width: 7.2.0 - y18n: 5.0.8 - yargs-parser: 22.0.0 - dev: false - /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + /yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + dev: false + /zod-validation-error@3.5.3(zod@3.25.76): resolution: {integrity: sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==} engines: {node: '>=18.0.0'}