diff --git a/apps/fo/.env.development b/apps/fo/.env.development new file mode 100644 index 000000000..b270a2694 --- /dev/null +++ b/apps/fo/.env.development @@ -0,0 +1,12 @@ +NEXT_PUBLIC_SITE_ID = FO +NEXT_PUBLIC_PORT = 11300 +NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 +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_CLAIM_NAME = Authorization +NEXT_PUBLIC_AUTH_USER_ID = token-id +NEXT_PUBLIC_ACCESS_TOKEN = access-token +NEXT_PUBLIC_REFRESH_TOKEN = refresh-token + +NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API diff --git a/apps/fo/.env.local b/apps/fo/.env.local new file mode 100644 index 000000000..ccb832d70 --- /dev/null +++ b/apps/fo/.env.local @@ -0,0 +1,13 @@ +NEXT_PUBLIC_SITE_ID = FO +NEXT_PUBLIC_PORT = 3000 +NEXT_PUBLIC_PROXY_HOST = http://localhost:3000 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 +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_CLAIM_NAME = Authorization +NEXT_PUBLIC_AUTH_USER_ID = token-id +NEXT_PUBLIC_ACCESS_TOKEN = access-token +NEXT_PUBLIC_REFRESH_TOKEN = refresh-token + +NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API +#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 PC 서버 API diff --git a/apps/fo/.gitignore b/apps/fo/.gitignore new file mode 100644 index 000000000..8c5b2ee3a --- /dev/null +++ b/apps/fo/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +#.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/fo/app/components/api/config/env.ts b/apps/fo/app/components/api/config/env.ts new file mode 100644 index 000000000..22272ecd8 --- /dev/null +++ b/apps/fo/app/components/api/config/env.ts @@ -0,0 +1,12 @@ +const DEV_PORT_COMMON = "11081"; +const DEV_PORT_FO = "11082"; +const DEV_PORT_WR = "11080"; + +export const LOCAL_URL_COMMON = `http://192.168.1.129:${DEV_PORT_COMMON}/api/v1`; +export const DEV_URL_COMMON = `http://con.mind-one.co.kr:${DEV_PORT_COMMON}/api/v1`; + +export const LOCAL_URL_FO = `http://192.168.1.129:${DEV_PORT_FO}/api/v1`; +export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`; + +export const LOCAL_URL_WR = `http://192.168.1.129:${DEV_PORT_WR}/api/v1`; +export const DEV_URL_WR = `http://con.mind-one.co.kr:${DEV_PORT_WR}/api/v1`; diff --git a/apps/fo/app/components/api/config/index.ts b/apps/fo/app/components/api/config/index.ts new file mode 100644 index 000000000..b9f9f76d6 --- /dev/null +++ b/apps/fo/app/components/api/config/index.ts @@ -0,0 +1 @@ +export * from './env'; \ No newline at end of file diff --git a/apps/fo/app/components/api/index.ts b/apps/fo/app/components/api/index.ts new file mode 100644 index 000000000..fe8e54bb6 --- /dev/null +++ b/apps/fo/app/components/api/index.ts @@ -0,0 +1,79 @@ +import { DEV_URL_COMMON, DEV_URL_FO, DEV_URL_WR } from "./config/env"; + +export const getFetch = async (part : string, url: string) => { + let reqUrl = ""; + reqUrl = part === "fo" ? DEV_URL_FO+url : part === "wr" ? DEV_URL_WR+url : DEV_URL_COMMON+url; + const response = await fetch(reqUrl , { + method: "GET", + headers: { + accept: "application/json", + } + }); + if(!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + const res = await response.json() + return res; +} + +export const getFetchAreaList = async (url: string) => { + const response = await fetch(url , { + method: "GET", + headers: { + accept: "application/json", + } + }); + if(!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + const res = await response.json() + return res; +} + +export const postFetch = async (part : string, url: string, params: unknown) => { + let reqUrl = ""; + reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url; + const response = await fetch(reqUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }); + if(!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + return await response.json(); +} + +export const putFetch = async (part : string, url: string, params: unknown) => { + let reqUrl = ""; + reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url; + const response = await fetch(reqUrl, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }); + if(!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + return response; +} + +export const deleteFetch = async (part : string, url: string, params: unknown) => { + let reqUrl = ""; + reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url; + const response = await fetch(reqUrl, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }); + if(!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + return response; +} \ No newline at end of file diff --git a/apps/fo/app/components/common/DataForm.tsx b/apps/fo/app/components/common/DataForm.tsx new file mode 100644 index 000000000..66530aed9 --- /dev/null +++ b/apps/fo/app/components/common/DataForm.tsx @@ -0,0 +1,46 @@ +type DataFormProps = { + type: string; + column: string; + item: Record; +} + +export default function DataForm({ type, column, item }: DataFormProps) { + if(item) { + const value = item[column]; + switch(type){ + case 'date': + if(value && typeof value === 'string' && value.length >= 8) + item[column] = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8); + break; + case 'number': + if(value && value != '') { + item[column] = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + } + break; + case 'float1': + if(value) { + item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 10) / 10).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 '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, ","); + } + 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, ","); + } + break; + } + } +} diff --git a/apps/fo/app/components/common/FacilityTermModalHeader.tsx b/apps/fo/app/components/common/FacilityTermModalHeader.tsx new file mode 100644 index 000000000..b360f496c --- /dev/null +++ b/apps/fo/app/components/common/FacilityTermModalHeader.tsx @@ -0,0 +1,279 @@ +import { useFoStore } from '@/app/store'; +import { DatePickerRange, SelectBox } from '@common/business_components/ui'; +import ListDiv from '@common/business_components/ui/list_div'; +import { Table, TableCell, TableHead, TableRow } from '@common/components/ui'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +type FacilityTermModalHeaderProps = { + fcltyCd : string; + fcltyName: string; + tabName: string; + searchYear: string; + searchList: T[]; + ref: React.RefObject>; + queryClick:(returnType:ReturnType) => void; +} + +export type ReturnType = { + startDate: Date; + endDate: Date; + tabName: string; + filteredList: T[]; +} + +export default function FacilityTermModalHeader({ + searchList, + fcltyName, + tabName, + searchYear, + queryClick, + ref +}: FacilityTermModalHeaderProps) { + const {Common} = useFoStore((state) => state); + const { selectFlowPanel } = Common; + const [startDate, setStartDate] = useState(new Date(Number(searchYear), 0, 1)); + const [endDate, setEndDate] = useState(new Date(Number(searchYear), 11, 31)); + const startPickerRef = useRef(null); + const endPickerRef = useRef(null); + const [flowPanel, setFlowPanel] = useState(selectFlowPanel[0]?.val ?? '00'); + const [copySearchList, setCopySearchList] = useState(searchList); + const [standSearchList, setStandSearchList] = useState(searchList); + + const isDateInRanges = (date: Date) => { + const standYear = parseInt(searchYear); + const dateRanges = []; + + if (tabName.includes("_summer")) { + dateRanges.push({ + start: new Date(standYear, 5, 1), + end: new Date(standYear, 7, 31) + }); + } else if (tabName.includes("_winter")) { + dateRanges.push({ + start: new Date(standYear, 0, 1), + end: new Date(standYear, 1, 28) + }); + } else { + dateRanges.push({ + start: new Date(standYear, 0, 1), + end: new Date(standYear, 11, 31) + }); + } + + return dateRanges.some(range => { + const start = new Date(range.start); + const end = new Date(range.end); + return date >= start && date <= end; + }); + }; + + useEffect(() => { + let startDate = new Date(Number(searchYear), 0, 1); + let endDate = new Date(Number(searchYear), 11, 31); + + if(tabName.includes("_summer")) { + startDate = new Date(Number(searchYear), 5, 1); + endDate = new Date(Number(searchYear), 7, 31); + setStartDate(startDate); + setEndDate(endDate); + } else if (tabName.includes("_winter")) { + startDate = new Date(Number(searchYear), 0, 1); + endDate = new Date(Number(searchYear), 1, 28); + setStartDate(startDate); + setEndDate(endDate); + } else { + startDate = new Date(Number(searchYear), 0, 1); + endDate = new Date(Number(searchYear), 11, 31); + setStartDate(startDate); + setEndDate(endDate); + } + + let filteredData: T[] = []; + + if(tabName.includes("_rain")) { + filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay; + if(flowPanel === '1') { + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay>0 && infoRnDay < 3; + } else if(flowPanel === '2'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10; + } else if(flowPanel === '3'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20; + } else if(flowPanel === '4'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30; + } else if(flowPanel === '5'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40; + } else if(flowPanel === '6'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50; + } else if(flowPanel === '7'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60; + } else if(flowPanel === '8'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60; + } else { + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (infoRnDay>0 || rainEffectDay ===1); + } + }); + } else if(tabName.includes("_chuncheon")) { + filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay==0; + }); + } else { + filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly; + }); + } + + setCopySearchList(filteredData); + setStandSearchList(filteredData); + }, [searchList, searchYear, tabName, flowPanel]); + + const handleStartDateSelect = (day: Date | undefined) => { + if(day && day > endDate) { + alert('종료일은 시작일 이전일 수 없습니다.'); + return; + } + + const selectedDate = day ?? startDate; + setStartDate(selectedDate); + const button = startPickerRef.current?.querySelector('button'); + if (button) { + setTimeout(() => button.click(), 0); + } + + const filteredData = standSearchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly; + }); + setCopySearchList(filteredData); + }; + + const handleEndDateSelect = (day: Date | undefined) => { + if(day && day < startDate) { + alert('종료일은 시작일 이후일 수 없습니다.'); + return; + } + + const selectedDate = day ?? endDate; + setEndDate(selectedDate); + const button = endPickerRef.current?.querySelector('button'); + if (button) { + setTimeout(() => button.click(), 0); + } + + const filteredData = standSearchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate()); + return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly; + }); + setCopySearchList(filteredData); + }; + + const fncSetFilteredList = useCallback(() => { + queryClick({ + startDate: startDate, + endDate: endDate, + tabName: tabName, + filteredList: copySearchList + }); + }, [queryClick, startDate, endDate, tabName, copySearchList]); + + useEffect(() => { + fncSetFilteredList(); + }, [fncSetFilteredList]); + + useEffect(() => { + if(ref && ref.current) { + const customRef = ref.current; + customRef.startDate = startDate; + customRef.endDate = endDate; + customRef.tabName = tabName; + customRef.filteredList = copySearchList; + } + }, [startDate, endDate, tabName, copySearchList, ref]); + + return ( + +
+ + + + {'시설명'} + {tabName === "tab_term_rain" ? {fcltyName} : + {fcltyName}} + {tabName === "tab_term_rain" && ( + <> + {'강수구분'} + +
+ setFlowPanel(val.toString())} + className="mx-3 w-[150px]" + label="" + selectClass="w-full p-1 border rounded bg-white" + /> +
+
+ + )} + {'년/월'} + +
+
+ handleStartDateSelect(date as Date)} + className="w-full p-1 border rounded bg-white" + disabled={(date) => !isDateInRanges(date)} + defaultMonth={startDate} + /> +
+

~

+
+ handleEndDateSelect(date as Date)} + className="w-full p-1 border rounded bg-white" + disabled={(date) => !isDateInRanges(date)} + defaultMonth={endDate} + /> +
+
+
+
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/fo/app/components/common/FacilityTermModalHeaderMonth.tsx b/apps/fo/app/components/common/FacilityTermModalHeaderMonth.tsx new file mode 100644 index 000000000..a284d04cf --- /dev/null +++ b/apps/fo/app/components/common/FacilityTermModalHeaderMonth.tsx @@ -0,0 +1,276 @@ +import { useFoStore } from '@/app/store'; +import { SelectBox } from '@common/business_components/ui'; +import ListDiv from '@common/business_components/ui/list_div'; +import { DatePicker, Table, TableCell, TableHead, TableRow } from '@common/components/ui'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +type FacilityTermModalHeaderProps = { + fcltyCd : string; + fcltyName: string; + tabName: string; + searchYear: string; + searchList: T[]; + ref: React.RefObject>; + queryClick:(returnType:ReturnType) => void; +} + +export type ReturnType = { + startDate: Date; + endDate: Date; + tabName: string; + filteredList: T[]; +} + +export default function FacilityTermModalHeaderMonth({ + searchList, + fcltyName, + tabName, + searchYear, + queryClick, + ref +}: FacilityTermModalHeaderProps) { + const {Common} = useFoStore((state) => state); + const { selectFlowPanel } = Common; + const [startDate, setStartDate] = useState(new Date(Number(searchYear), 0, 1)); + const [endDate, setEndDate] = useState(new Date(Number(searchYear), 11, 31)); + const startPickerRef = useRef(null); + const endPickerRef = useRef(null); + const [flowPanel, setFlowPanel] = useState(selectFlowPanel[0]?.val ?? '00'); + const [copySearchList, setCopySearchList] = useState(searchList); + + /* const isDateInRanges = (date: Date) => { + const standYear = parseInt(searchYear); + const dateRanges = []; + + if (tabName.includes("_summer")) { + dateRanges.push({ + start: new Date(standYear, 5, 1), + end: new Date(standYear, 7, 31) + }); + } else if (tabName.includes("_winter")) { + dateRanges.push({ + start: new Date(standYear, 0, 1), + end: new Date(standYear, 1, 28) + }); + } else { + dateRanges.push({ + start: new Date(standYear, 0, 1), + end: new Date(standYear, 11, 31) + }); + } + + return dateRanges.some(range => { + const start = new Date(range.start); + const end = new Date(range.end); + return date >= start && date <= end; + }); + }; */ + + useEffect(() => { + let startDate = new Date(Number(searchYear), 0, 1); + let endDate = new Date(Number(searchYear), 11, 31); + + if(tabName.includes("_summer")) { + startDate = new Date(Number(searchYear), 5, 1); + endDate = new Date(Number(searchYear), 7, 31); + setStartDate(startDate); + setEndDate(endDate); + } else if (tabName.includes("_winter")) { + startDate = new Date(Number(searchYear), 0, 1); + endDate = new Date(Number(searchYear), 1, 28); + setStartDate(startDate); + setEndDate(endDate); + } else { + startDate = new Date(Number(searchYear), 0, 1); + endDate = new Date(Number(searchYear), 11, 31); + setStartDate(startDate); + setEndDate(endDate); + } + + let filteredData: T[] = []; + + if(tabName.includes("_rain")) { + filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay; + + if(flowPanel === '1') { + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay>0 && infoRnDay < 3; + } else if(flowPanel === '2'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10; + } else if(flowPanel === '3'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20; + } else if(flowPanel === '4'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30; + } else if(flowPanel === '5'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40; + } else if(flowPanel === '6'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50; + } else if(flowPanel === '7'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60; + } else if(flowPanel === '8'){ + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60; + } else { + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (infoRnDay>0 || rainEffectDay ===1); + } + }); + } else if(tabName.includes("_chuncheon")) { + filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay==0; + }); + } else { + filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly; + }); + } + + setCopySearchList(filteredData); + }, [searchList, searchYear, tabName, flowPanel]); + + const handleStartDateSelect = (day: Date | undefined) => { + if(day && day > endDate) { + alert('종료일은 시작일 이전일 수 없습니다.'); + return; + } + + const selectedDate = day ?? startDate; + setStartDate(selectedDate); + const button = startPickerRef.current?.querySelector('button'); + if (button) { + setTimeout(() => button.click(), 0); + } + + const filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate()); + const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); + return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly; + }); + setCopySearchList(filteredData); + }; + + const handleEndDateSelect = (day: Date | undefined) => { + if(day && day < startDate) { + alert('종료일은 시작일 이후일 수 없습니다.'); + return; + } + + const selectedDate = day ?? endDate; + setEndDate(selectedDate); + const button = endPickerRef.current?.querySelector('button'); + if (button) { + setTimeout(() => button.click(), 0); + } + + const filteredData = searchList.filter((item) => { + const itemDate = new Date((item as T & { msrDt: string }).msrDt); + const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); + const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); + const endDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate()); + return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly; + }); + setCopySearchList(filteredData); + }; + + const fncSetFilteredList = useCallback(() => { + queryClick({ + startDate: startDate, + endDate: endDate, + tabName: tabName, + filteredList: copySearchList + }); + }, [queryClick, startDate, endDate, tabName, copySearchList]); + + useEffect(() => { + fncSetFilteredList(); + }, [fncSetFilteredList]); + + useEffect(() => { + if(ref && ref.current) { + const customRef = ref.current; + customRef.startDate = startDate; + customRef.endDate = endDate; + customRef.tabName = tabName; + customRef.filteredList = copySearchList; + } + }, [startDate, endDate, tabName, copySearchList, ref]); + + return ( + +
+ + + + {'시설명'} + {tabName === "tab_term_rain" ? {fcltyName} : + {fcltyName}} + {tabName === "tab_term_rain" && ( + <> + {'강수구분'} + +
+ setFlowPanel(val.toString())} + className="mx-3 w-[150px]" + label="" + selectClass="w-full p-1 border rounded bg-white" + /> +
+
+ + )} + {'년/월'} + +
+
+ handleStartDateSelect(date as Date)} + className="w-full p-1 border rounded bg-white" + defaultMonth={startDate} + /> +
+

~

+
+ handleEndDateSelect(date as Date)} + className="w-full p-1 border rounded bg-white" + defaultMonth={endDate} + /> +
+
+
+
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/fo/app/components/common/HeadMakeColSpan.tsx b/apps/fo/app/components/common/HeadMakeColSpan.tsx new file mode 100644 index 000000000..06617839a --- /dev/null +++ b/apps/fo/app/components/common/HeadMakeColSpan.tsx @@ -0,0 +1,65 @@ +import { TableHead, TableRow } from "@common/components/ui"; +import type { TableUpperProps } from "../../store/dashboard"; + +const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: string }) => { + return ( + layoutNum === 'A' ? ( + <> + {upHeadList.map((head: TableUpperProps) => + (head.id === 'trtFrfNm' ? {head.title} : + head.id === 'laeNm' ? {head.title} : + head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? {head.title} : + head.id === 'trstOprEtpNm' ? {head.title} : + head.upName === 'upChangeRe' && head.upSequnce === 1 ? {'행정구역'} : + !head.upName ? {head.title} : null))} + + + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe') { + return {head.title}; + } + })} + + + ) : layoutNum === 'R' ? ( + <> + {upHeadList.map((head: TableUpperProps) => + (head.id === 'trtFrfNm' ? {head.title} : + head.id === 'laeNm' ? {head.title} : + head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? {head.title} : + head.id === 'trstOprEtpNm' ? {head.title} : + head.upName === 'upChangeRe' && head.upSequnce === 1 ? {'유역'} : + !head.upName ? {head.title} : null))} + + + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe') { + return {head.title}; + } + })} + + + ) : ( + <> + {upHeadList.map((head: TableUpperProps) => + (head.id === 'trtFrfNm' ? {head.title} : + head.id === 'laeNm' ? {head.title} : + head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? {head.title} : + head.id === 'trstOprEtpNm' ? {head.title} : + head.upName === 'upChangeRe1' && head.upSequnce === 1 ? {'권역'} : + head.upName === 'upChangeRe2' && head.upSequnce === 1 ? {'행정구역'} : + !head.upName ? {head.title} : null))} + + + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') { + return {head.title}; + } + })} + + + ) + ); +} + +export { HeadMakeColSpan }; diff --git a/apps/fo/app/components/common/SearchInput.tsx b/apps/fo/app/components/common/SearchInput.tsx new file mode 100644 index 000000000..11bdd3676 --- /dev/null +++ b/apps/fo/app/components/common/SearchInput.tsx @@ -0,0 +1,108 @@ +import { useFoStore } from '@/app/store'; +import type { facilityListProps } from '@/app/store/processFacility'; +import { Label } from '@common/components/ui'; +import React, { useEffect, useState, useRef, useCallback } from 'react' + +type thisProps = { + label: string | undefined; + setValue : (fcltyCd?: string, fcltyNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => void; + sidoSigun? : string | undefined; + fcltyName? : string | undefined; +} + +export default function SearchInputText({label=undefined, setValue=()=>{}, sidoSigun=undefined, fcltyName=undefined} :thisProps) { + + const { ProcessFacilityList, searchProcessFacilitySearching } = useFoStore((state) => state); + const [isOpen, setIsOpen] = useState(false); + const [textValue, setTextValue] = useState(fcltyName || ''); + const wrapperRef = useRef(null); + const processFacilityList = ProcessFacilityList?.processFacilityList || []; + + const handleInputChange = useCallback((e: React.ChangeEvent) => { + const value = e.target.value; + setTextValue(value); + if (value) { + setIsOpen(true); + if(sidoSigun) { + searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0', value); + } else { + searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0', value); + } + } else { + setIsOpen(false); + } + }, [searchProcessFacilitySearching, sidoSigun]); + + const onInputFocus = useCallback(() => { + setIsOpen(true); + if(sidoSigun) { + searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0'); + } else { + searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0'); + } + }, [searchProcessFacilitySearching, sidoSigun]); + + useEffect(() => { + setTextValue(fcltyName || ''); + }, [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); + }, [setValue]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ {label && } + + {isOpen && processFacilityList.length > 0 && ( +
+
+ {processFacilityList.map((item : facilityListProps, index : number) => ( +
handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm); + } + }} + > + {item.sido} {item.gugun} {item.bscFctNm} +
+ ))} +
+
+ )} +
+ ) +} \ No newline at end of file diff --git a/apps/fo/app/favicon.ico b/apps/fo/app/favicon.ico new file mode 100644 index 000000000..718d6fea4 Binary files /dev/null and b/apps/fo/app/favicon.ico differ diff --git a/apps/fo/app/globals.css b/apps/fo/app/globals.css new file mode 100644 index 000000000..4ff2b8802 --- /dev/null +++ b/apps/fo/app/globals.css @@ -0,0 +1,7 @@ +@import "../../../packages/common/components/src/base.css"; + +html, +body { + font-family: "Noto Sans KR", sans-serif; + font-size: 10px; +} \ No newline at end of file diff --git a/apps/fo/app/layout.tsx b/apps/fo/app/layout.tsx new file mode 100644 index 000000000..548ef77a9 --- /dev/null +++ b/apps/fo/app/layout.tsx @@ -0,0 +1,24 @@ +"use client" +import { BaseLayout } from "@common/components/ui"; +import "./globals.css"; +import { StoreProvider } from "./store"; + + + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + {children} + + + + + ); +} diff --git a/apps/fo/app/page.tsx b/apps/fo/app/page.tsx new file mode 100644 index 000000000..823e5a408 --- /dev/null +++ b/apps/fo/app/page.tsx @@ -0,0 +1,44 @@ +"use client" + +import Link from "next/link"; +import { Button } from "@common/components/ui"; + +export default function Home() { + return ( +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ ); +} diff --git a/apps/fo/app/pages/dashboard/drainageEquipment/page.tsx b/apps/fo/app/pages/dashboard/drainageEquipment/page.tsx new file mode 100644 index 000000000..f6f1188de --- /dev/null +++ b/apps/fo/app/pages/dashboard/drainageEquipment/page.tsx @@ -0,0 +1,98 @@ +"use client" + +import { useFoStore } from '@/app/store'; +import type { DrainageEquipmentListProps, TableUpperProps } from '@/app/store/dashboard'; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@common/components'; +import { useEffect, useState } from 'react'; + +export type DrainageEquipmentProps = { + searchYear?: string; + fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[]; +}; + +export default function DrainageEquipment({searchYear = '', fcltyList = []}: DrainageEquipmentProps) { + + const { + Common, + DrainageEquipment, + searchDrainageEquipment, + } = useFoStore((state) => state); + + const { + isInit, + } = Common; + + const { + upHeadListDrainageEquipment, + drainageEquipmentList, + } = DrainageEquipment; + + const [hasSearched, setHasSearched] = useState(false); + // 이전 시설코드 저장 + const [prevFacilityCode, setPrevFacilityCode] = useState(''); + + const tableData = fcltyList; + + useEffect(() => { + if (!isInit && tableData[0]?.bscFctCd && (tableData[0].bscFctCd !== prevFacilityCode || !hasSearched)) { + searchDrainageEquipment(searchYear, tableData[0].bscFctCd, false); + setHasSearched(true); + setPrevFacilityCode(tableData[0].bscFctCd); + } + }, [fcltyList, isInit, searchDrainageEquipment, searchYear, tableData, hasSearched, prevFacilityCode]); + + // 연도가 바뀌면 hasSearched를 false로 리셋 + useEffect(() => { + setHasSearched(false); + }, [searchYear]); + + // 시설계획 table header 변경 + const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => { + return ( + <> + {upHeadList.map((head: TableUpperProps) => + (head.id === 'part1' ? 구분 : + head.id === 'part2' ? 구분 : + head.id == 'nowSum' ? {'현재\n(기준년도)'} : + head.id == 'stepOneSum' ? {'변경(금회계획)'} : + head.id == 'etc' ? 비고 : null))} + + {upHeadList.map((head: TableUpperProps) => + (head.id === 'stepOneSum' ? 1단계 : + head.id === 'stepTwoSum' ? 2단계 : + head.id === 'stepThreeSum' ? 3단계 : + head.id === 'stepFourSum' ? 4단계 : null))} + + + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') { + return {head.title}; + } + })} + + + ) + } + + const upHeadList = HeadMakeColSpan({upHeadList:upHeadListDrainageEquipment}); + return ( +
+ + + {upHeadList} + + + {drainageEquipmentList.map((el: DrainageEquipmentListProps, index) => ( + + {upHeadListDrainageEquipment.map((head) => ( + (head.id==='nowSum')? + {333} + :{el[head.id as keyof DrainageEquipmentListProps]} + ))} + + ))} + +
+
+ ) +} diff --git a/apps/fo/app/pages/dashboard/facilityInfo/modal/page.tsx b/apps/fo/app/pages/dashboard/facilityInfo/modal/page.tsx new file mode 100644 index 000000000..94bc28500 --- /dev/null +++ b/apps/fo/app/pages/dashboard/facilityInfo/modal/page.tsx @@ -0,0 +1,184 @@ +"use client" +import type { OperationalStatusListProps } from "@/app/store/dashboard"; +import { CHART_FORM, MainChart, type ChartConfig } from "@common/business_components"; +import { Button } from "@common/business_components/ui/table/lib/button"; +import html2canvas from "html2canvas"; +import { useEffect, useState, useRef } from "react"; + +type yearProps = { + label: string; + 평균 : number | string | ''; + 최대 : number | string | ''; + 최소 : number | string | ''; +} + +type summerProps = { + 전기간 : number | string | ''; + 강우일3mm이상 : number | string | ''; + 비강우시 : number | string | ''; +} + +type winterProps = { + 전기간 : number | string | ''; + 청천시최대 : number | string | ''; + 청천시평균 : number | string | ''; + 청천시최소 : number | string | ''; +} + +type dashBoardChartModalProps = { + title : string; + item : string; + period : string; + modalData : OperationalStatusListProps[]; + waterQlty : string; +} + +export default function DashBoardChartModal({item='', period='', title='',modalData=[], waterQlty=''} : dashBoardChartModalProps) { + + const [chartData, setChartData] = useState([]); + const [chartConfig, setChartConfig] = useState({}); + const chartRef = useRef(null); + const chartOption = { + chartForm : CHART_FORM.BAR, + chartConfig : chartConfig, + chartData : chartData, + chartTitle : title, + chartClassName : "h-[calc(100%-15%)] w-[90%] mt-5", + graphSize : 40, + yFieldText : item === 'flowRate' ? '유량(㎥/일)' : item === 'waterQlty' ? '수질(mg/L)' : item === 'chongin' ? '수질(mg/L)' : '', + showLegend : true + } + + const handleDownload = () => { + if (!chartRef.current) { + return alert('결과 저장에 실패했습니다.'); + } + html2canvas(chartRef.current).then((canvas) => { + const link = document.createElement('a'); + document.body.appendChild(link); + link.href = canvas.toDataURL('image/png'); + link.download = 'result.png'; + link.click(); + document.body.removeChild(link); + }); + } + + /** + * useEffect + */ + useEffect(() => { + if(period === 'year'){ + const filteredData: yearProps[] = modalData.map((item) => { + if(waterQlty.trim().includes('T-N') || waterQlty.trim().includes('T-P')){ + return { + label: item.waterQlty, + 평균: typeof item.yearAvg === 'string' ? Number(item.yearAvg.replace(/,/g, '')).toFixed(3) : (item.yearAvg || 0), + 최대: typeof item.yearMax === 'string' ? Number(item.yearMax.replace(/,/g, '')).toFixed(3) : (item.yearMax || 0), + 최소: typeof item.yearMin === 'string' ? Number(item.yearMin.replace(/,/g, '')).toFixed(3) : (item.yearMin || 0), + } + } + else{ + return { + label: item.waterQlty, + 평균: typeof item.yearAvg === 'string' ? Number(item.yearAvg.replace(/,/g, '')).toFixed(1) : (item.yearAvg || 0), + 최대: typeof item.yearMax === 'string' ? Number(item.yearMax.replace(/,/g, '')).toFixed(1) : (item.yearMax || 0), + 최소: typeof item.yearMin === 'string' ? Number(item.yearMin.replace(/,/g, '')).toFixed(1) : (item.yearMin || 0), + } + } + }) + if (item === 'flowRate') { + setChartData(filteredData.slice(0,5)); + } else if (item === 'waterQlty') { + setChartData(filteredData.slice(5,11)); + } else { + setChartData(filteredData.slice(11,13)); + } + } + else if (period === 'summer') { + const filteredData: summerProps[] = modalData.map((item) => { + if(waterQlty.trim().includes('T-N') || waterQlty.trim().includes('T-P')){ + return { + label: item.waterQlty, + 전기간: typeof item.summerAll === 'string' ? Number(item.summerAll.replace(/,/g, '')).toFixed(3) : (item.summerAll || 0), + 강우일3mm이상: typeof item.summerThree === 'string' ? Number(item.summerThree.replace(/,/g, '')).toFixed(3) : (item.summerThree || 0), + 비강우시: typeof item.summerRain === 'string' ? Number(item.summerRain.replace(/,/g, '')).toFixed(3) : (item.summerRain || 0), + } + }else{ + return { + label: item.waterQlty, + 전기간: typeof item.summerAll === 'string' ? Number(item.summerAll.replace(/,/g, '')).toFixed(1) : (item.summerAll || 0), + 강우일3mm이상: typeof item.summerThree === 'string' ? Number(item.summerThree.replace(/,/g, '')).toFixed(1) : (item.summerThree || 0), + 비강우시: typeof item.summerRain === 'string' ? Number(item.summerRain.replace(/,/g, '')).toFixed(1) : (item.summerRain || 0), + } + } + }) + if (item === 'flowRate') { + setChartData(filteredData.slice(0,5)); + } else if (item === 'waterQlty') { + setChartData(filteredData.slice(5,11)); + } else { + setChartData(filteredData.slice(11,13)); + } + } + else if (period === 'winter') { + const filteredData: winterProps[] = modalData.map((item) => { + return { + label: item.waterQlty, + 전기간: typeof item.winterAll === 'string' ? Number(item.winterAll.replace(/,/g, '')).toFixed(3) : (item.winterAll || 0), + 청천시최대: typeof item.winterSunnyMax === 'string' ? Number(item.winterSunnyMax.replace(/,/g, '')).toFixed(3) : (item.winterSunnyMax || 0), + 청천시평균: typeof item.winterSunnyAvg === 'string' ? Number(item.winterSunnyAvg.replace(/,/g, '')).toFixed(3) : (item.winterSunnyAvg || 0), + 청천시최소: typeof item.winterSunnyMin === 'string' ? Number(item.winterSunnyMin.replace(/,/g, '')).toFixed(3) : (item.winterSunnyMin || 0), + } + }) + setChartData(filteredData); + + if (item === 'flowRate') { + setChartData(filteredData.slice(0,5)); + } else if (item === 'waterQlty') { + setChartData(filteredData.slice(5,11)); + } else { + setChartData(filteredData.slice(11,13)); + } + } else { + setChartData([]); + } + }, [item, modalData, period, waterQlty]); + + useEffect(() => { + const newChartConfig = chartData.reduce((config, element) => { + const label = 'label' in element ? element.label : ''; + if (label) { + config[label] = { + label: label, + color : "hsl(var(--chart-1))" + }; + } + return config; + }, {} as Record) satisfies ChartConfig; + setChartConfig(newChartConfig); + }, [chartData]); + + return ( + <> +
+ + (하절기 : 6/1~8/31 동절기 : 1/1~2/28) + + +
+
+ +
+ + ); +} + diff --git a/apps/fo/app/pages/dashboard/facilityInfo/page.tsx b/apps/fo/app/pages/dashboard/facilityInfo/page.tsx new file mode 100644 index 000000000..7a88755ce --- /dev/null +++ b/apps/fo/app/pages/dashboard/facilityInfo/page.tsx @@ -0,0 +1,277 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/processFacility"; +import { ContentsDiv, UiModal, UiTable } from "@common/business_components"; +import { cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; +import { useEffect, useState } from "react"; +import DashBoardChartModal from "./modal/page"; + +export type facilityInfoProps = { + searchYear?: string; + fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[]; +}; + +// 시설계획 table header 변경 +const headMakeColSpanPlanFclty = (upHeadList: TableUpperProps[], planYearList: string[]) => { + return ( <> + { + upHeadList.map((head: TableUpperProps) => + (!head.upSequnce?{head.title}: + head.upName === 'upChangeRe'&& head.upSequnce=== 1?{'시설계획\n(금회)'}:null))} + + + {upHeadList.map((head: TableUpperProps, index: number) => + (head.upName === 'upChangeRe'?{index===0?head.title:head.title+'\n('+planYearList[index-1]+')'}:null))} + + ) +} + +// 시설계획 table header 변경 +const headMakeColSpanOperationStatus = (upHeadList: TableUpperProps[], searchYear: string) => { + return ( <> + + {searchYear+'년도 운영실태'} + + { + upHeadList.map((head: TableUpperProps) => + (head.upName === 'upChangeRe1'&&head.upSequnce===1?{head.title}: + !head.upSequnce?{head.title}: + head.upName === 'upChangeRe2'&&head.upSequnce===1?{'연간 운영실태'}: + head.upName === 'upChangeRe3'&&head.upSequnce===1?{'하절기 운영실태\n(6~8월)'}: + head.upName === 'upChangeRe4'&&head.upSequnce===1?{'동절기 운영실태\n(1~2월)'}: + head.upName === 'upChangeRe5'&&head.upSequnce===1?{'지하수유입량'}:null))} + + + {upHeadList.map((head: TableUpperProps) => + (head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'||head.upName === 'upChangeRe4'||head.upName === 'upChangeRe5' ?{head.title}:null))} + + ) +} + +export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}: facilityInfoProps & { onFacilityChange: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }) => void }) { + // 입력값 상태 관리 + const { + Common, + FcltyMain, + PlanFclty, + OperationStatus, + searchOperationStatus, + searchPlanInfo + } = useFoStore((state) => state); + + const [modalItem, setModalItem] = useState<{item: string, period: string, title: string, waterQlty: string} | null>(null); + const [hasSearched, setHasSearched] = useState(false); + const [prevFacilityCode, setPrevFacilityCode] = useState(''); + + const { + isInit, + } = Common; + + // Tab1 + // fclty 관련 + const { + upHeadListFclty, + } = FcltyMain; + + // 시설계획 관련 + const { + upHeadListPlanFclty, + planFcltyList, + planYearList + } = PlanFclty; + + // 그리드 관련 + const { + upHeadListOperationStatus, + operationalStatusList + } = OperationStatus; + + const closeModal = () => { + setModalItem(null); + }; + + // const fcltyListArr = useMemo(() => [fcltyList], [fcltyList]); + + const tableData = fcltyList; + + // key 변환 + const mappedTableData = tableData.map(item => ({ + fcltyNm: item.bscFctNm, + facilityCapacity: item.fcltyCpc, + publicMethod: item.trtFrfNm, + location: item.efflWqltStndrdNm, + })); + + const fncLoadModal = (item: string, period: string, waterQlty: string) => { + // 유량 + if(item === 'flowRate'){ + if(period === 'year'){ + setModalItem({item: 'flowRate', period: 'year', title: '연간 유량', waterQlty: waterQlty}); + } + else if(period === 'summer'){ + setModalItem({item: 'flowRate', period: 'summer', title: '하절기 유량', waterQlty: waterQlty}); + } + else if(period === 'winter'){ + setModalItem({item: 'flowRate', period: 'winter', title: '동절기 유량', waterQlty: waterQlty}); + } + } + // 수질 + else if (item == 'waterQlty'){ + if(period === 'year'){ + setModalItem({item: 'waterQlty', period: 'year', title: '연간 수질', waterQlty: waterQlty}); + } + else if(period === 'summer'){ + setModalItem({item: 'waterQlty', period: 'summer', title: '하절기 수질', waterQlty: waterQlty}); + } + else if(period === 'winter'){ + setModalItem({item: 'waterQlty', period: 'winter', title: '동절기 수질', waterQlty: waterQlty}); + } + } + // 총인 + else { + if(period === 'year'){ + setModalItem({item: 'chongin', period: 'year', title: '총인설비 연간 수질', waterQlty: waterQlty}); + } + else if(period === 'summer'){ + setModalItem({item: 'chongin', period: 'summer', title: '총인설비 하절기 수질', waterQlty: waterQlty}); + } + else if(period === 'winter'){ + setModalItem({item: 'chongin', period: 'winter', title: '총인설비 동절기 수질', waterQlty: waterQlty}); + } + } + } + + useEffect(() => { + if (!isInit && fcltyList[0]?.bscFctCd && (fcltyList[0].bscFctCd !== prevFacilityCode || !hasSearched)) { + searchOperationStatus(false, searchYear, fcltyList[0].bscFctCd); + searchPlanInfo(searchYear, fcltyList[0].bscFctCd); + setHasSearched(true); + setPrevFacilityCode(fcltyList[0].bscFctCd); + planFcltyList.forEach((el: { [key: string]: string | number | boolean }, index) => { + if (index === 6) { + el['item'] = (Number(searchYear)-2).toString(); + el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6; + } else if (index === 7) { + el['item'] = (Number(searchYear)-1).toString(); + el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6; + } else if (index === 8) { + el['item'] = (Number(searchYear)-0).toString(); + el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6; + } + }); + } + }, [isInit, searchOperationStatus, searchYear, fcltyList, hasSearched, prevFacilityCode, searchPlanInfo, planFcltyList]); + // 연도가 바뀌면 hasSearched를 false로 리셋 + useEffect(() => { + setHasSearched(false); + }, [searchYear]); + + useEffect(() => { + operationalStatusList.map((item: { [key: string]: string | number | boolean },index) => { + if(index===8 || index===9 || index===10 || index===11 || index===12 || index===13 || index===14 || index===15){ + item['yearAvg'] = Number(item['yearAvg']).toFixed(3); + item['yearMax'] = item['yearMax']+'%'; + item['yearMin'] = item['yearMin']+'%'; + item['summerAll'] = item['summerAll']+'%'; + item['summerThree'] = item['summerThree']+'%'; + item['summerRain'] = item['summerRain']+'%'; + item['winterAll'] = item['winterAll']+'%'; + item['winterSunnyAvg'] = item['winterSunnyAvg']+'%'; + item['winterSunnyMax'] = item['winterSunnyMax']+'%'; + item['winterSunnyMin'] = item['winterSunnyMin']+'%'; + } + + }); + }, [operationalStatusList]); + + const upChangeRePlanFclty = headMakeColSpanPlanFclty(upHeadListPlanFclty, planYearList); + const upChangeHeadListOperationStatus = headMakeColSpanOperationStatus(upHeadListOperationStatus, searchYear); + return ( + <> + { } } /> + +
+
+ + + {upChangeRePlanFclty} + + + {planFcltyList.map((el: { [key: string]: string | number | boolean },index) => ( + + {upHeadListPlanFclty.map((head) => ( + ((index === 6 || index === 7 || index === 8) && (head.id === 'provisionalApproval' || head.id === 'stepTwo'))? + {el[head.id]} + :((index === 6 || index === 7 || index === 8) && (head.id === 'now' || head.id === 'stepOne' || head.id === 'stepThree' || head.id === 'stepFour'))? + null + :((index === 5) && (head.id === 'provisionalApproval' || head.id === 'stepTwo'))? + {el[head.id]} + :((index === 5) && (head.id === 'now' || head.id === 'stepOne' || head.id === 'stepThree'|| head.id === 'stepFour'))? + null + :(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) + ?{(Number(fcltyList[0]?.fcltyCpc)* 0.6).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 })} + :((head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') && index === 4) + ?null + :(head.id === 'item' && index != 5)? + {el[head.id]} + :{el[head.id]} + ))} + + ))} + +
+
+
+
+ + + {upChangeHeadListOperationStatus} + + + {operationalStatusList.map((el: { [key: string]: string | number | boolean },index) => ( + + {upHeadListOperationStatus.map((head) => ( + (head.id === 'flowRate' && index === 0)?{el[head.id]}: + (head.id === 'flowRate' && index === 5)?{el[head.id]}: + (head.id === 'flowRate' && index === 11)?{el[head.id]}: + (head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 0)?{el[head.id]}: + (head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 7)?{el[head.id]}: + (head.id === 'rdi'&&index === 0)?{el[head.id]}: + (head.id === 'rdi'&&index === 2)?{'RDII추정시\n일강수량(mm)'}: + (head.id === 'rdi'&&index === 5)?{el['rdiRain']}: + (head.id === 'rdi'&&index === 7)?: + (head.id === 'waterQlty')?{el[head.id]}: + ((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=0 && index<=4))?{fncLoadModal('flowRate', 'year', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=5 && index<=10))?{fncLoadModal('waterQlty', 'year', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=11 && index<=12))?{fncLoadModal('chongin', 'year', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=0 && index<=4))?{fncLoadModal('flowRate', 'summer', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=5 && index<=10))?{fncLoadModal('waterQlty', 'summer', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=11 && index<=12))?{fncLoadModal('chongin', 'summer', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=0 && index<=4))?{fncLoadModal('flowRate', 'winter', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=5 && index<=10))?{fncLoadModal('waterQlty', 'winter', el['waterQlty'] as string);}}>{el[head.id]}: + ((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=11 && index<=12))?{fncLoadModal('chongin', 'winter', el['waterQlty'] as string);}}>{el[head.id]}: + null + ))} + + ))} + +
+
+
+
+
+ {modalItem && (modalItem.item === 'flowRate' || modalItem.item === 'waterQlty' || modalItem.item === 'chongin') && ( + } + bodyClass="sm:max-w-[80%] h-[88%]" + open={true} + onOpenChange={(open) => !open && closeModal()} + noHeaderYn="Y" + noFooterYn="Y" + /> + )} + + ); +} diff --git a/apps/fo/app/pages/dashboard/maingraph/page.tsx b/apps/fo/app/pages/dashboard/maingraph/page.tsx new file mode 100644 index 000000000..dc0a08912 --- /dev/null +++ b/apps/fo/app/pages/dashboard/maingraph/page.tsx @@ -0,0 +1,243 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { lineChartDataProps } from "@/app/store/dashboard"; +import { CHART_FORM, type ChartConfig, ContentsDiv, MainChart } from "@common/business_components"; +import { Card, CardContent } from "@common/business_components/ui/chart/lib/card"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useEffect, useState } from "react"; + +type facilityInfoProps = { + searchYear?: string; + fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[]; +}; + +export default function DashBoardChart({searchYear = '', fcltyList = []}: facilityInfoProps) { + const {MainGraph + ,searchMainGraph + } = useFoStore((state) => state); + + const [hasSearched, setHasSearched] = useState(false); + const [prevFacilityCode, setPrevFacilityCode] = useState(''); + const [chart_tpTp, setChart_tpTp] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + }>(); + const [chart_tntn, setChart_tntn] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + }>(); + const [chart_inflBodInflTn, setChart_inflBodInflTn] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + }>(); + const [chart_inflBodInflTp, setChart_inflBodInflTp] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + }>(); + + const { + chart_inflowRain, + chart_bodRain, + chart_inflTp, + chart_chongInTp, + chart_efflTp, + chart_inflTn, + chart_efflTn, + chart_inflBod, + } = MainGraph; + + /** + * 차트 Config + */ + const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5"]; + + //유입하수량-강수량 + const chartConfig_inflowRain = { + barVal0: { + label: '강수량', + color: colors[2], + }, + lineVal0: { + label: '시설용량', + color: colors[1], + }, + lineVal1: { + label: '유입량', + color: colors[4], + } + } satisfies ChartConfig; + //강우량-유입BOD + const chartConfig_rainBod = { + barVal0: { + label: '강수량', + color: colors[2], + }, + lineVal0: { + label: '유입BOD', + color: colors[0], + }, + } satisfies ChartConfig; + + //총인유입T-P-방류T-P + const chartConfig_tptp = { + chartData1: { + label: '총인유입T-P', + color: colors[0], + }, + chartData2: { + label: '방류T-P', + color: colors[1], + }, + } satisfies ChartConfig; + + // 유입T-N-방류T-N + const chartConfig_tntn = { + chartData1: { + label: '유입T-N', + color: colors[0], + }, + chartData2: { + label: '방류T-N', + color: colors[1], + }, + } satisfies ChartConfig; + + // 유입BOD-유입T-N + const chartConfig_inflBodInflTn = { + chartData1: { + label: '유입BOD', + color: colors[0], + }, + chartData2: { + label: '유입T-N', + color: colors[1], + }, + } satisfies ChartConfig; + + // 유입BOD-유입T-P + const chartConfig_inflBodInflTp = { + chartData1: { + label: '유입BOD', + color: colors[0], + }, + chartData2: { + label: '유입T-P', + color: colors[1], + }, + + } satisfies ChartConfig; + + useEffect(() => { + if (fcltyList.length > 0 && fcltyList[0]?.bscFctCd && (fcltyList[0].bscFctCd !== prevFacilityCode || !hasSearched)) { + searchMainGraph(searchYear, fcltyList[0].bscFctCd); + setHasSearched(true); + setPrevFacilityCode(fcltyList[0].bscFctCd); + } + }, [searchYear, fcltyList, searchMainGraph, hasSearched, chart_inflTp, chart_efflTp, prevFacilityCode]); + + useEffect(() => { + if (!chart_chongInTp || !chart_efflTp) return; + const chartTpTpData = { + chartData1: chart_chongInTp, + chartData2: chart_efflTp + }; + setChart_tpTp(chartTpTpData); + }, [chart_chongInTp, chart_efflTp]); + + useEffect(() => { + if (!chart_inflTn || !chart_efflTn) return; + const chartTntnData = { + chartData1: chart_inflTn, + chartData2: chart_efflTn + }; + setChart_tntn(chartTntnData); + }, [chart_inflTn, chart_efflTn]); + + useEffect(() => { + if (!chart_inflBod || !chart_inflTn) return; + const chartInflBodInflTnData = { + chartData1: chart_inflBod, + chartData2: chart_inflTn + }; + setChart_inflBodInflTn(chartInflBodInflTnData); + }, [chart_inflBod, chart_inflTn]); + + useEffect(() => { + if (!chart_inflBod || !chart_inflTp) return; + const chartInflBodInflTpData = { + chartData1: chart_inflBod, + chartData2: chart_inflTp + }; + setChart_inflBodInflTp(chartInflBodInflTpData); + }, [chart_inflBod, chart_inflTp]); + + // 연도가 바뀌면 hasSearched를 false로 리셋 + useEffect(() => { + setHasSearched(false); + }, [searchYear]); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + {/* 유입T-N-방류T-N */} + + + + + + + + {/* 유입BOD-유입T-N */} + + + + + + + + {/* 유입BOD-유입T-P */} + + + + + + + + + + ); + {/*
> + +
+
+ +
+
+ +
+
+ +
+ */} +} diff --git a/apps/fo/app/pages/dashboard/page.tsx b/apps/fo/app/pages/dashboard/page.tsx new file mode 100644 index 000000000..97b45fd06 --- /dev/null +++ b/apps/fo/app/pages/dashboard/page.tsx @@ -0,0 +1,306 @@ +"use client" +import { useFoStore } from "@/app/store"; +import { ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, SelectBox, UITab, type SelectDataType } from "@common/business_components"; +import { Button, TabsContent } from "@common/components/ui"; +import { useEffect, useState } from "react"; +import DashBoardFclty from "../dashboardMain/page"; +/* import DrainageEquipment from "./drainageEquipment/page"; */ +import ProcessFacilitySearch from "./facilityInfo/page"; +import DashBoardChart from "./maingraph/page"; +import RainManageTab from "./rainManage/page"; +import DrainageEquipment from "./drainageEquipment/page"; + +const tabList = [ + { value: 'facilityInfo', label: '시설정보 및 운영현황' }, + { value: 'mainGraph', label: '주요그래프' }, + { value: 'rainManage', label: '강우일 운영현황' }, + { value: 'drainageEquipment', label: '배수설비 정비현황' } +]; + +export default function DashBoard() { + // 입력값 상태 관리 + const { Common, initializeCommonData,searchMainGraph, searchRainManage, searchOperationStatus, OperationStatus, RainManage, MainGraph, PlanFclty } = useFoStore((state) => state); + const [activeTab, setActiveTab] = useState('facilityInfo'); + + const [filteredSigunData, setFilteredSigunData] = useState([{ text: "전체", val: '0' }]); + const [sidoValue, setSidoValue] = useState('0'); + const [sigunValue, setSigunValue] = useState('0'); + const { + isInit, + selectSearchYear, + btnClass, + selectSidoData, + selectSigunData, + } = Common; + + const { + planFcltyList + } = PlanFclty; + + const { + operationalStatusList + } = OperationStatus; + + const { + excelList + } = RainManage; + + const { + chart_inflowRain, + chart_bodRain, + chart_inflTp, + chart_chongInTp, + chart_efflTp, + chart_inflTn, + chart_efflTn, + } = MainGraph; + + /******************선택된 데이터******************/ + // 연도 + const [searchYear, setSearchYear] = useState(selectSearchYear[0]?.val || '0'); + // 시설명 + const [fcltyList, setFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({ + bscFctCd: '', + bscFctNm: '', + fcltyCpc: '', + trtFrfNm: '', + efflWqltStndrdNm: '' + }); + + useEffect(() => { + if (!isInit) { + initializeCommonData(); + setFcltyList({ + bscFctCd: '11000SW001R', + bscFctNm: '난지', + fcltyCpc: 860000, + trtFrfNm: '표준활성슬러지법, MLE, A2O', + efflWqltStndrdNm: '500톤 이상(Ⅲ지역)' + }); + } + }, [isInit, initializeCommonData]); + + const handleFacilityChange = (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => { + setFcltyList(facility); + }; + + const onSelectValue = (val: string, type: string) => { + switch(type) { + case 'sido': + setSidoValue(val); + initializeSigunData(val); + onSelectValue('0', 'sigun'); + break; + case 'sigun': + setSigunValue(val); + break; + } + } + + // 시군구 세팅 + const initializeSigunData = (sidoCd: string) => { + if (sidoCd === '0') { + setSigunValue('0'); + setFilteredSigunData([{ text: "전체", val: '0' }]); + } else { + const newFilteredData = [ + { text: "전체", val: '0' }, + ...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd) + ]; + setSigunValue('0'); + setFilteredSigunData(newFilteredData); + } + } + + const excelDownload = () => { + if (!fcltyList.bscFctCd) return; + switch (activeTab) { + case 'facilityInfo': + searchOperationStatus(false, searchYear, fcltyList.bscFctCd); + + fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + + const excelData1 = planFcltyList.map((item) => { + return [ + item.item, + item.provisionalApproval, + item.now, + item.stepOne, + item.stepTwo, + item.stepThree, + item.stepFour, + ]; + }); + + // operationalStatusList를 2차원 배열로 변환 + const excelData2 = operationalStatusList.map((item, index) => { + const baseData = [ + item.yearAvg, + item.yearMax, + item.yearMin, + item.summerAll, + item.summerThree, + item.summerRain, + item.winterAll, + item.winterSunnyAvg, + item.winterSunnyMax, + item.winterSunnyMin, + item.groundWaterAvg, + item.groundWaterSummer, + item.groundWaterWinter + ]; + + // index가 0일 때만 rdi와 rdiRain 추가 + return index === 0 ? [...baseData, item.rdi, item.rdiRain] : baseData; + }); + + const sheetParameter = [ + { + data: excelData1, + headerNum: 3, + headerRange: 'A1:G2', + sheetName: '운영실태', + startCol: 'A' + }, + { + data: excelData2, + headerNum: 4, + headerRange: 'A1:Q3', + sheetName: '운영실태', + startCol: 'C' + } + ]; + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter); + }); + break; + + case 'mainGraph': + searchMainGraph(searchYear, fcltyList.bscFctCd); + + const sheetParameter = [ + { + data: [['날짜', '강수량'],...chart_inflowRain.map((item) => [item.label, item.barVal0])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '강수량' + }, + { + data: [['날짜', '유입하수량'],...chart_inflowRain.map((item) => [item.label, item.lineVal1])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '유입하수량' + }, + { + data: [['날짜', '시설용량'],...chart_inflowRain.map((item) => [item.label, item.lineVal0])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '시설용량' + }, + { + data: [['날짜', '유입BOD'],...chart_bodRain.map((item) => [item.label, item.lineVal0])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '유입BOD' + }, + { + data: [['날짜', '유입T-P'],...chart_inflTp.map((item) => [item.label, item.val])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '유입T-P' + }, + { + data: [['날짜', '총인유입T-P'],...chart_chongInTp.map((item) => [item.label, item.val])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '총인유입T-P' + }, + { + data: [['날짜', '방류T-P'],...chart_efflTp.map((item) => [item.label, item.val])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '방류T-P' + }, + { + data: [['날짜', '유입T-N'],...chart_inflTn.map((item) => [item.label, item.val])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '유입T-N' + }, + { + data: [['날짜', '방류T-N'],...chart_efflTn.map((item) => [item.label, item.val])], + headerNum: 1, + headerRange: 'A1:B1', + sheetName: '방류T-N' + } + ]; + + downloadMultipleExcel('주요그래프', sheetParameter); + break; + + case 'rainManage': + searchRainManage(searchYear, fcltyList.bscFctCd, true); + if (excelList && excelList.length > 0) { + downloadExcel({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], headerRange: 'A1:T1' }); + } + break; + // Add other cases for different tabs as needed + default: + break; + } + } + + const initVal = () => { + setFcltyList({ + bscFctCd: '11000SW001R', + bscFctNm: '난지', + fcltyCpc: 860000, + trtFrfNm: '표준활성슬러지법, MLE, A2O', + efflWqltStndrdNm: '500톤 이상(Ⅲ지역)' + }); + } + + return ( + + +
+
+ setSearchYear(val.toString()) } value={searchYear} selectClass={undefined}/> + onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined}/> + onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined}/> +
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + +
+
+ ); +} diff --git a/apps/fo/app/pages/dashboard/rainManage/page.tsx b/apps/fo/app/pages/dashboard/rainManage/page.tsx new file mode 100644 index 000000000..ea20eb92c --- /dev/null +++ b/apps/fo/app/pages/dashboard/rainManage/page.tsx @@ -0,0 +1,59 @@ +"use client" +import { useFoStore } from "@/app/store"; +import { UiTable } from "@common/business_components/ui/table"; +import { useEffect, useState } from "react"; + +export type facilityInfoProps = { + searchYear?: string; + fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[]; +}; + +export default function RainManageTab({searchYear = '', fcltyList = []}: facilityInfoProps) { + + const { + Common, + searchRainManage, + RainManage + } = useFoStore((state) => state); + + const [hasSearched, setHasSearched] = useState(false); + // 이전 시설코드 저장 + const [prevFacilityCode, setPrevFacilityCode] = useState(''); + + const { + isInit, + } = Common; + + const { + upHeadListRainManage, + rainManageList, + } = RainManage; + + const tableData = fcltyList; + + useEffect(() => { + if (!isInit && tableData[0]?.bscFctCd && (tableData[0].bscFctCd !== prevFacilityCode || !hasSearched)) { + searchRainManage(searchYear, tableData[0].bscFctCd, false); + setHasSearched(true); + setPrevFacilityCode(tableData[0].bscFctCd); + } + }, [fcltyList, isInit, searchRainManage, searchYear, tableData, hasSearched, prevFacilityCode]); + + // 연도가 바뀌면 hasSearched를 false로 리셋 + useEffect(() => { + setHasSearched(false); + }, [searchYear]); + + return ( + {}} + > + + ) +} diff --git a/apps/fo/app/pages/dashboardMain/page.tsx b/apps/fo/app/pages/dashboardMain/page.tsx new file mode 100644 index 000000000..3fa403d90 --- /dev/null +++ b/apps/fo/app/pages/dashboardMain/page.tsx @@ -0,0 +1,58 @@ +"use client" +import { useFoStore } from "@/app/store"; +import { useEffect, useState } from "react"; +import SearchInput from "../../components/common/SearchInput"; + +export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm }: { onFacilityChange?: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => void, sidoSigun?: string, facilityNm?: string }) { + // 입력값 상태 관리 + const { Common, initializeCommonData } = useFoStore((state) => state); + + const { + isInit, + } = Common; + + // 시설명 + const [bscFctNm, setFcltyName] = useState(facilityNm || '난지'); + const [bscFctCd, setbscFctCd] = useState('11000SW001R'); + const [fcltyCpc, setFcltyCpc] = useState(860000); + const [trtFrfNm, setTrtFrfNm] = useState('표준활성슬러지법, MLE, A2O'); + const [efflWqltStndrdNm, setEfflWqltStndrdNm] = useState('500톤 이상(Ⅲ지역)'); + + useEffect(() => { + if (!isInit) { + initializeCommonData(); + } + }, [isInit, initializeCommonData ]); + + useEffect(() => { + if (facilityNm) { + setFcltyName(facilityNm); + } + }, [facilityNm]); + + const fncNextPage = (bscFctCd?: string, bscFctNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => { + setbscFctCd(bscFctCd || ''); + setFcltyName(bscFctNm || ''); + setFcltyCpc(fcltyCpc || ''); + setTrtFrfNm(trtFrfNm || ''); + setEfflWqltStndrdNm(efflWqltStndrdNm || ''); + + if (bscFctCd && onFacilityChange) { + onFacilityChange({ + bscFctCd: bscFctCd || '', + bscFctNm: bscFctNm || '', + fcltyCpc: fcltyCpc || '', + trtFrfNm: trtFrfNm || '', + efflWqltStndrdNm: efflWqltStndrdNm || '' + }); + } + } + + console.log('bscFctNm : ', bscFctNm); + + return ( + <> + + + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/facilityPrecipitation/modal/page.tsx b/apps/fo/app/pages/facilityPrecipitation/modal/page.tsx new file mode 100644 index 000000000..ead9b56fd --- /dev/null +++ b/apps/fo/app/pages/facilityPrecipitation/modal/page.tsx @@ -0,0 +1,203 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { PrecipitationPropsDetailProps } from "@/app/store/facilityPrecipitation"; +import { DatePickerRange, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Table, TableCell, TableHead, TableRow } from "@common/components/ui"; +import { useEffect, useRef, useState } from "react"; + +type PrecipitationModalProps = { + searchYear: string; + fcltyName: string; + facilityCd: string; + detailList: PrecipitationPropsDetailProps[]; +} + +type DateRange = { + start: Date; + end: Date; +} + +export default function PrecipitationModal(props: PrecipitationModalProps) { + const { PrecipitationDetail } = useFoStore((state) => state); + const { upHeadListPrecipitationDetail } = PrecipitationDetail; + + // Refs + const startPickerRef = useRef(null); + const endPickerRef = useRef(null); + + // States + const [startDate, setStartDate] = useState(null); + const [endDate, setEndDate] = useState(null); + const [filteredDetailList, setFilteredDetailList] = useState([]); + const [copyDetailList, setCopyDetailList] = useState([]); + + const getDateRanges = (): DateRange[] => { + const standYear = parseInt(props.searchYear); + return [{ + start: new Date(standYear, 0, 1), + end: new Date(standYear, 11, 31) + }]; + }; + + const isDateInRanges = (date: Date): boolean => { + return getDateRanges().some(range => { + const start = new Date(range.start); + const end = new Date(range.end); + return date >= start && date <= end; + }); + }; + + const filterDataByDateRange = (start: Date | null, end: Date | null): PrecipitationPropsDetailProps[] => { + if (!start || !end) return filteredDetailList; + return filteredDetailList.filter((item) => { + const itemDate = new Date(item.rainDt); + return itemDate >= start && itemDate <= end; + }); + }; + + const handleStartDateSelect = (date: Date | null) => { + if (!date || !endDate) return; + + if (date > endDate) { + alert('시작일은 종료일 이후일 수 없습니다.'); + return; + } + + const filteredData = filterDataByDateRange(date, endDate); + setCopyDetailList(filteredData); + setStartDate(date); + + const button = startPickerRef.current?.querySelector('button'); + if (button) { + setTimeout(() => button.click(), 0); + } + }; + + const handleEndDateSelect = (date: Date | null) => { + if (!date || !startDate) return; + + if (date < startDate) { + alert('종료일은 시작일 이전일 수 없습니다.'); + return; + } + + const filteredData = filterDataByDateRange(startDate, date); + setCopyDetailList(filteredData); + setEndDate(date); + + const button = endPickerRef.current?.querySelector('button'); + if (button) { + setTimeout(() => button.click(), 0); + } + }; + + const calculateTotals = () => { + return copyDetailList.reduce((acc, item) => ({ + infoRnDay: acc.infoRnDay + Number(item.infoRnDay), + rainDay: acc.rainDay + Number(item.rainDay), + rainEffectDay: acc.rainEffectDay + Number(item.rainEffectDay) + }), { infoRnDay: 0, rainDay: 0, rainEffectDay: 0 }); + }; + + const getTableData = () => { + const totals = calculateTotals(); + return [ + { + bscFctCd: "", + rainDt: '합계', + ...totals + }, + ...copyDetailList + ]; + }; + + useEffect(() => { + if (props.detailList.length > 0) { + const filteredList = props.detailList.filter((item) => + props.facilityCd === item.bscFctCd + ); + + setFilteredDetailList(filteredList); + setCopyDetailList(filteredList); + + if (filteredList.length > 0) { + const firstDateStr = filteredList[0]?.rainDt; + const lastDateStr = filteredList[filteredList.length - 1]?.rainDt; + + if (firstDateStr) { + const firstDate = new Date(firstDateStr); + if (!isNaN(firstDate.getTime())) { + setStartDate(firstDate); + } + } + + if (lastDateStr) { + const lastDate = new Date(lastDateStr); + if (!isNaN(lastDate.getTime())) { + setEndDate(lastDate); + } + } + } + } + }, [props.detailList, props.facilityCd]); + + if (!startDate || !endDate) { + return
로딩 중...
; + } + + return ( + <> + +
+ + + + {'시설명'} + {props.fcltyName} + {'년월'} + +
+
+ handleStartDateSelect(date as Date)} + className="w-full p-1 border rounded bg-white" + disabled={(date) => !isDateInRanges(date)} + defaultMonth={startDate} + /> +
+ ~ +
+ handleEndDateSelect(date as Date)} + className="w-full p-1 border rounded bg-white" + disabled={(date) => !isDateInRanges(date)} + defaultMonth={endDate} + /> +
+
+
+
+ +
+
+
+ + {}} + boldArray={[0]} + /> + + + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/facilityPrecipitation/page.tsx b/apps/fo/app/pages/facilityPrecipitation/page.tsx new file mode 100644 index 000000000..c9403fd14 --- /dev/null +++ b/apps/fo/app/pages/facilityPrecipitation/page.tsx @@ -0,0 +1,249 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import PrecipitationModal from "./modal/page"; + +export default function FacilityPrecipitation() { + const {Common + ,FacilityPrecipitation + ,FacilityPrecipitationSearch + ,PrecipitationDetail + ,initializeCommonData + ,searchFacilityPrecipitationList + } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }}; + + const { + isInit, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectUpdownData, + selectOperationData, + selectSearchYear, + btnClass + } = Common; + + const { + labelArray, + } = FacilityPrecipitationSearch; + + const { + upHeadList1, + upHeadList2, + upHeadList3, + excelList, + facilityPrecipitation, + facilityPrecipitationCount + } = FacilityPrecipitation; + + const { + gridListPrecipitationDetail + } = PrecipitationDetail; + + const ref = useRef({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + const facilityPrecipitationList = FacilityPrecipitation?.facilityPrecipitation || []; + + // 그리드 관련 + const searchInputRef = useRef(null); + + // 모달 상태 추가 + const [selectedRow, setSelectedRow] = useState(null); + const [fcltyNm, setFcltyNm] = useState(""); + const [fcltyCd, setFcltyCd] = useState(""); + + // 초기화 변수 + const [resetPage, setResetPage] = useState(false); + // 표 너비 + const [widthSize, setWidthSize] = useState("w-[120%]"); + // 구분별 그리드 헤더 + const [upheadList, setUpheadList] = useState(upHeadList1); + // 용량별 + const [fcltCpct, setFcltCpct] = useState("0"); + // 시설명 + const [fcltyName, setFcltyName] = useState(""); + // 시설상태 + const [operationValue, setOperationValue] = useState("0"); + // 조회기간 + const [searchYear, setSearchYear] = useState(selectSearchYear[0]?.val || "0"); + + /** + * 페이지 함수 + */ + // 초기화 + const resetVal = () => { + setWidthSize("w-[120%]"); + setUpheadList(upHeadList1) + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYear(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0"); + } + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + }; + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + if (facilityPrecipitation[index]) { + setFcltyNm(facilityPrecipitation[index].bscFctNm); + setFcltyCd(facilityPrecipitation[index].bscFctCd); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + }; + + /** + * useEffect + */ + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0"); + } + }, [isInit, initializeCommonData, searchFacilityPrecipitationList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '시설별 강수현황', + sheetNameArray: ['시설별 강수현황'], + headerRange: 'A1:N1' + }); + } + }, [excelList]); + + useEffect(() => { + if (facilityPrecipitation.length > 0) { + + } + }, [facilityPrecipitation]); + + /** + * 콜백함수 + */ + // 조회 + const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + console.log('partValue : ', partValue); + console.log('regionValue : ', regionValue); + console.log('unitWaterShedValue : ', unitWaterShedValue); + console.log('middleRegionValue : ', middleRegionValue); + console.log('envPartValue : ', envPartValue); + console.log('sidoValue : ', sidoValue); + console.log('sigunValue : ', sigunValue); + + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + if (partStr.length > 1 || operationValue!== '0' || fcltCpct !== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchFacilityPrecipitationList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchFacilityPrecipitationList(pageNo, pageCnt, searchYear); + } + }, [operationValue, fcltyName, searchFacilityPrecipitationList, searchYear, fcltCpct]); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + // 엑셀다운로드 + const excelDownload = useCallback((searchReturn: ReturnType) => { + searchFacilityPrecipitation(searchReturn, 1, 10000); + }, [searchFacilityPrecipitation]); + + // 페이지 변경 + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchFacilityPrecipitation(ref.current, page, 10); + }, [searchFacilityPrecipitation]); + + const mainList = facilityPrecipitationList || []; + const total = facilityPrecipitationCount || 0; + + return ( + + + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} isAdmin={false} /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 강수현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + <span className="text-2xl font-bold text-gray-500">☞ 강우일은 3mm 이상만 해당 / 강우영향일은 강우일 및 강우일 후 연속 2일간으로 정의</span> + </div> + </ListDiv> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<PrecipitationModal searchYear={searchYear} fcltyName={fcltyNm} facilityCd={fcltyCd} detailList={gridListPrecipitationDetail}></PrecipitationModal>} + bodyClass="sm:max-w-[90vw] w-[72vw] h-[40vw]" + open={selectedRow !== null} + onOpenChange={(open) => !open && closeModal()} + noHeaderYn="Y" + noFooterYn="Y" + /> + )} + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/facilityWaterQlty/HeadMakeColSpan.tsx b/apps/fo/app/pages/facilityWaterQlty/HeadMakeColSpan.tsx new file mode 100644 index 000000000..61076c105 --- /dev/null +++ b/apps/fo/app/pages/facilityWaterQlty/HeadMakeColSpan.tsx @@ -0,0 +1,176 @@ +import { TableHead, TableRow } from "@common/components/ui"; + +interface TableUpperProps { + id: string; + title: string; + upName?: string; + upSequnce?: number; +} + +const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: number }) => { + return ( + layoutNum === 0 ? ( + <> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if(!head.upName) { + return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> + }else if(head.upName==="upChangeRe1"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>행정구역</TableHead> + }else if(head.upName==="upChangeRe3"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead> + }else if(head.upName==="upChangeRe4"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead> + } + })} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps, idx: number) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') { + if (idx == 1 || idx == 2 || idx == 3) { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + else { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + } + })} + </TableRow> + </> + ) : layoutNum === 1 ? ( + <> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if(!head.upName) { + return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> + }else if(head.upName==="upChangeRe1"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id}>권역</TableHead> + }else if(head.upName==="upChangeRe3"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead> + }else if(head.upName==="upChangeRe4"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead> + } + })} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps, idx: number) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') { + if (idx == 1 || idx == 2 || idx == 3) { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + else { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + } + })} + </TableRow> + </> + ) : layoutNum === 2 ? ( + <> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if(!head.upName) { + return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> + }else if(head.upName==="upChangeRe1"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id}>권역</TableHead> + }else if(head.upName==="upChangeRe2"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>행정구역</TableHead> + }else if(head.upName==="upChangeRe3"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead> + }else if(head.upName==="upChangeRe4"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead> + } + })} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps, idx: number) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') { + if (idx == 1 || idx == 2 || idx == 3) { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + else { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + } + })} + </TableRow> + </> + ) : layoutNum === 3 ? ( + <> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if(!head.upName) { + return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> + }else if(head.upName==="upChangeRe1"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead> + }else if(head.upName==="upChangeRe2"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead> + } + })} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps, idx: number) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') { + if (idx == 1 || idx == 2 || idx == 3) { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + else { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + } + })} + </TableRow> + </> + ) : layoutNum === 4 ? ( + <> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if(!head.upName) { + return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> + }else if(head.upName==="upChangeRe"&&head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={4} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead> + } + })} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps, idx: number) => { + if (head.upName === 'upChangeRe') { + if (idx == 1 || idx == 2 || idx == 3) { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + else { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + } + })} + </TableRow> + </> + ) : layoutNum === 5 ? ( // 총인설비 수질현황 헤더 + <> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if(!head.upName) { + return <TableHead className="w-[180px] bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> + }else if(head.upName==="upChangeRe1" && head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead> + }else if(head.upName==="upChangeRe2" && head.upSequnce===1) { + return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead> + } + })} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>; + } + })} + </TableRow> + </> + ): ( + <> + </> + ) + ) +} + +export { HeadMakeColSpan }; diff --git a/apps/fo/app/pages/facilityWaterQlty/modal/page.tsx b/apps/fo/app/pages/facilityWaterQlty/modal/page.tsx new file mode 100644 index 000000000..ede0175aa --- /dev/null +++ b/apps/fo/app/pages/facilityWaterQlty/modal/page.tsx @@ -0,0 +1,537 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { FacilityWaterQltyProps } from "@/app/store/facilityWaterQlty"; +import { UITab, UiTable } from "@common/business_components/ui"; +import { TabsContent } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; +import type { ReturnType } from "../../../components/common/FacilityTermModalHeader"; +import FacilityTermModalHeader from "../../../components/common/FacilityTermModalHeader"; +import { HeadMakeColSpan } from "../HeadMakeColSpan"; + +type ModalProps ={ + fcltyName: string; + facilityCd : string; + searchYear : string; +} + +const tabList1 = [ + { value: 'tab_num1', label: '처리시설 수질현황' }, + { value: 'tab_num2', label: '총인설비 수질현황' }, +]; + +const tabList2 = [ + { value: 'tab_term_year', label: '연간' }, + { value: 'tab_term_summer', label: '하절기' }, + { value: 'tab_term_winter', label: '동절기' }, + { value: 'tab_term_chuncheon', label: '청천시' }, + { value: 'tab_term_rain', label: '강우시' } +]; + +const tabList3 = [ + { value: 'tab_term_year_chongin', label: '연간' }, + { value: 'tab_term_summer_chongin', label: '하절기' }, + { value: 'tab_term_winter_chongin', label: '동절기' } +]; + +export default function WaterQualityModal({fcltyName='', facilityCd='', searchYear=new Date().getFullYear().toString()} : ModalProps) { + // 그리드 관련 + const {FacilityWaterQltyModal, + searchFacilityWaterQltyListHist, + } = useFoStore((state) => state); + + const { + upHeadListModalYear, + facilityWaterQltyYearList, + upHeadListModalTms, + tmsList, + msrmtDate, + upHeadListModalChongIn, + } = FacilityWaterQltyModal; + + // 필터링 된 후 최대, 최소, 평균 값 계산 + const setFilterAfterData = (filteredData: FacilityWaterQltyProps[]) => { + // 쉼표가 포함된 문자열을 숫자로 변환하는 함수 + const parseNumber = (value: string | number | null | undefined): number => { + if (value === null || value === undefined || value === '') return 0; + if (typeof value === 'number') return value; + return Number(value.replace(/,/g, '')); + }; + + let maxEfflWqltBod = 0; + let maxEfflWqltTn = 0; + let maxEfflWqltTp = 0; + let maxefflWqltCod = 0; + let maxEfflWqltSs = 0; + + let maxInflAssctAftWqltBod = 0; + let maxInflAssctAftWqltTn = 0; + let maxInflAssctBfrWqltTp = 0; + let maxInflAssctAftWqltToc = 0; + let maxInflAssctAftWqltSs = 0; + + let maxTpInflWqltBod = 0; + let maxTpInflWqltTp = 0; + let maxInfoRnDay = 0; + + // 초기값 설정 + let minEfflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltBod) : 0; + let minEfflWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTn) : 0; + let minEfflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTp) : 0; + let minefflWqltCod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltCod) : 0; + let minEfflWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltSs) : 0; + + let minInflAssctAftWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltBod) : 0; + let minInflAssctAftWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltTn) : 0; + let minInflAssctBfrWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltTp) : 0; + let minInflAssctAftWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltToc) : 0; + let minInflAssctAftWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltSs) : 0; + + let minTpInflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.tpInflWqltBod) : 0; + let minTpInflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.tpInflWqltTp) : 0; + + let minInfoRnDay = filteredData.length > 0 ? parseNumber(filteredData[0]?.infoRnDay) : 0; + + let avgEfflWqltBod = 0; + let avgEfflWqltTn = 0; + let avgEfflWqltTp = 0; + let avgefflWqltCod = 0; + let avgEfflWqltSs = 0; + + let avgInflAssctAftWqltBod = 0; + let avgInflAssctAftWqltTn = 0; + let avgInflAssctBfrWqltTp = 0; + let avgInflAssctAftWqltToc = 0; + let avgInflAssctAftWqltSs = 0; + + let avgTpInflWqltBod = 0; + let avgTpInflWqltTp = 0; + + let avgInfoRnDay = 0; + + filteredData.forEach((item: FacilityWaterQltyProps) => { + const efflWqltBod = parseNumber(item.efflWqltBod); + const efflWqltTn = parseNumber(item.efflWqltTn); + const efflWqltTp = parseNumber(item.efflWqltTp); + const efflWqltCod = parseNumber(item.efflWqltCod); + const efflWqltSs = parseNumber(item.efflWqltSs); + + const inflAssctAftWqltBod = parseNumber(item.inflAssctAftWqltBod); + const inflAssctAftWqltTn = parseNumber(item.inflAssctAftWqltTn); + const inflAssctBfrWqltTp = parseNumber(item.inflAssctBfrWqltTp); + const inflAssctAftWqltToc = parseNumber(item.inflAssctAftWqltToc); + const inflAssctAftWqltSs = parseNumber(item.inflAssctAftWqltSs); + + + const tpInflWqltBod = parseNumber(item.tpInflWqltBod); + const tpInflWqltTp = parseNumber(item.tpInflWqltTp); + + const infoRnDay = parseNumber(item.infoRnDay); + + maxEfflWqltBod = Math.max(maxEfflWqltBod, efflWqltBod); + maxEfflWqltTn = Math.max(maxEfflWqltTn, efflWqltTn); + maxEfflWqltTp = Math.max(maxEfflWqltTp, efflWqltTp); + maxefflWqltCod = Math.max(maxefflWqltCod, efflWqltCod); + maxEfflWqltSs = Math.max(maxEfflWqltSs, efflWqltSs); + + maxInflAssctAftWqltBod = Math.max(maxInflAssctAftWqltBod, inflAssctAftWqltBod); + maxInflAssctAftWqltTn = Math.max(maxInflAssctAftWqltTn, inflAssctAftWqltTn); + maxInflAssctBfrWqltTp = Math.max(maxInflAssctBfrWqltTp, inflAssctBfrWqltTp); + maxInflAssctAftWqltToc = Math.max(maxInflAssctAftWqltToc, inflAssctAftWqltToc); + maxInflAssctAftWqltSs = Math.max(maxInflAssctAftWqltSs, inflAssctAftWqltSs); + + maxTpInflWqltBod = Math.max(maxTpInflWqltBod, tpInflWqltBod); + maxTpInflWqltTp = Math.max(maxTpInflWqltTp, tpInflWqltTp); + + maxInfoRnDay = Math.max(maxInfoRnDay, infoRnDay); + + minEfflWqltBod = Math.min(minEfflWqltBod, efflWqltBod); + minEfflWqltTn = Math.min(minEfflWqltTn, efflWqltTn); + minEfflWqltTp = Math.min(minEfflWqltTp, efflWqltTp); + minefflWqltCod = Math.min(minefflWqltCod, efflWqltCod); + minEfflWqltSs = Math.min(minEfflWqltSs, efflWqltSs); + + minInflAssctAftWqltBod = Math.min(minInflAssctAftWqltBod, inflAssctAftWqltBod); + minInflAssctAftWqltTn = Math.min(minInflAssctAftWqltTn, inflAssctAftWqltTn); + minInflAssctBfrWqltTp = Math.min(minInflAssctBfrWqltTp, inflAssctBfrWqltTp); + minInflAssctAftWqltToc = Math.min(minInflAssctAftWqltToc, inflAssctAftWqltToc); + minInflAssctAftWqltSs = Math.min(minInflAssctAftWqltSs, inflAssctAftWqltSs); + + minTpInflWqltBod = Math.min(minTpInflWqltBod, tpInflWqltBod); + minTpInflWqltTp = Math.min(minTpInflWqltTp, tpInflWqltTp); + + minInfoRnDay = Math.min(minInfoRnDay, infoRnDay); + + avgEfflWqltBod += efflWqltBod; + avgEfflWqltTn += efflWqltTn; + avgEfflWqltTp += efflWqltTp; + avgefflWqltCod += efflWqltCod; + avgEfflWqltSs += efflWqltSs; + + avgInflAssctAftWqltBod += inflAssctAftWqltBod; + avgInflAssctAftWqltTn += inflAssctAftWqltTn; + avgInflAssctBfrWqltTp += inflAssctBfrWqltTp; + avgInflAssctAftWqltToc += inflAssctAftWqltToc; + avgInflAssctAftWqltSs += inflAssctAftWqltSs; + + avgTpInflWqltBod += tpInflWqltBod; + avgTpInflWqltTp += tpInflWqltTp; + + avgInfoRnDay += infoRnDay; + }); + + const newFilteredData = [ + { + msrDt: '평균', + efflWqltBod: filteredData.length==0?'0.0':(Math.round(avgEfflWqltBod / filteredData.length * 10) / 10).toFixed(1), + efflWqltCod: filteredData.length==0?'0.0':(Math.round(avgefflWqltCod / filteredData.length * 10) / 10).toFixed(1), + efflWqltSs: filteredData.length==0?'0.0':(Math.round(avgEfflWqltSs / filteredData.length * 10) / 10).toFixed(1), + efflWqltTn: filteredData.length==0?'0.000':(Math.round(avgEfflWqltTn / filteredData.length * 1000) / 1000).toFixed(3), + efflWqltTp: filteredData.length==0?'0.000':(Math.round(avgEfflWqltTp / filteredData.length * 1000) / 1000).toFixed(3), + inflAssctAftWqltBod: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltBod / filteredData.length * 10) / 10).toFixed(1), + inflAssctAftWqltTn: filteredData.length==0?'0.000':(Math.round(avgInflAssctAftWqltTn / filteredData.length * 1000) / 1000).toFixed(3), + inflAssctBfrWqltTp: filteredData.length==0?'0.000':(Math.round(avgInflAssctBfrWqltTp / filteredData.length * 1000) / 1000).toFixed(3), + inflAssctAftWqltToc: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltToc / filteredData.length * 10) / 10).toFixed(1), + inflAssctAftWqltSs: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltSs / filteredData.length * 10) / 10).toFixed(1), + bscFctCd: "", + bscFctNm: "", + lclgvrnmntNm: "", + sidoNm: "", + sggNm: "", + addr: "", + fctCpc: 0, + infoRnDay: filteredData.length==0?'0.0':(Math.round(avgInfoRnDay / filteredData.length * 10) / 10).toFixed(1), + tpInflWqltBod: filteredData.length==0?'0.0':(Math.round(avgTpInflWqltBod / filteredData.length * 10) / 10).toFixed(1), + tpInflWqltTp: filteredData.length==0?'0.000':(Math.round(avgTpInflWqltTp / filteredData.length * 1000) / 1000).toFixed(3) + }, + { + msrDt : '최대', + efflWqltBod : Math.round(maxEfflWqltBod * 10) / 10?(Math.round(maxEfflWqltBod * 10) / 10).toFixed(1):'0.0', + efflWqltCod : Math.round(maxefflWqltCod * 10) / 10?(Math.round(maxefflWqltCod * 10) / 10).toFixed(1):'0.0', + efflWqltSs : Math.round(maxEfflWqltSs * 10) / 10?(Math.round(maxEfflWqltSs * 10) / 10).toFixed(1):'0.0', + efflWqltTn : Math.round(maxEfflWqltTn * 1000) / 1000?(Math.round(maxEfflWqltTn * 1000) / 1000).toFixed(3):'0.000', + efflWqltTp : Math.round(maxEfflWqltTp * 1000) / 1000?(Math.round(maxEfflWqltTp * 1000) / 1000).toFixed(3):'0.000', + inflAssctAftWqltBod : Math.round(maxInflAssctAftWqltBod * 10) / 10?(Math.round(maxInflAssctAftWqltBod * 10) / 10).toFixed(1):'0.0', + inflAssctAftWqltTn : Math.round(maxInflAssctAftWqltTn * 1000) / 1000?(Math.round(maxInflAssctAftWqltTn * 1000) / 1000).toFixed(3):'0.000', + inflAssctBfrWqltTp : Math.round(maxInflAssctBfrWqltTp * 1000) / 1000?(Math.round(maxInflAssctBfrWqltTp * 1000) / 1000).toFixed(3):'0.000', + inflAssctAftWqltToc : Math.round(maxInflAssctAftWqltToc * 10) / 10?(Math.round(maxInflAssctAftWqltToc * 10) / 10).toFixed(1):'0.0', + inflAssctAftWqltSs : Math.round(maxInflAssctAftWqltSs * 10) / 10?(Math.round(maxInflAssctAftWqltSs * 10) / 10).toFixed(1):'0.0', + bscFctCd: "", + bscFctNm: "", + lclgvrnmntNm: "", + sidoNm: "", + sggNm: "", + addr: "", + fctCpc: 0, + infoRnDay: 0, + tpInflWqltBod: Math.round(maxTpInflWqltBod * 10) / 10?(Math.round(maxTpInflWqltBod * 10) / 10).toFixed(1):'0.0', + tpInflWqltTp: Math.round(maxTpInflWqltTp * 1000) / 1000?(Math.round(maxTpInflWqltTp * 1000) / 1000).toFixed(3):'0.000' + }, + { + msrDt : '최소', + efflWqltBod : Math.round(minEfflWqltBod * 10) / 10?(Math.round(minEfflWqltBod?minEfflWqltBod:0 * 10) / 10).toFixed(1) : '0.0', + efflWqltCod : Math.round(minefflWqltCod * 10) / 10?(Math.round(minefflWqltCod?minefflWqltCod:0 * 10) / 10).toFixed(1) : '0.0', + efflWqltSs : Math.round(minEfflWqltSs * 10) / 10?(Math.round(minEfflWqltSs * 10) / 10).toFixed(1) : '0.0', + efflWqltTn : Math.round(minEfflWqltTn * 1000) / 1000?(Math.round(minEfflWqltTn * 1000) / 1000).toFixed(3) : '0.000', + efflWqltTp : Math.round(minEfflWqltTp * 1000) / 1000?(Math.round(minEfflWqltTp * 1000) / 1000).toFixed(3) : '0.000', + inflAssctAftWqltBod : Math.round(minInflAssctAftWqltBod * 10) / 10?(Math.round(minInflAssctAftWqltBod * 10) / 10).toFixed(1) : '0.0', + inflAssctAftWqltTn : Math.round(minInflAssctAftWqltTn * 1000) / 1000?(Math.round(minInflAssctAftWqltTn * 1000) / 1000).toFixed(3) : '0.000', + inflAssctBfrWqltTp : Math.round(minInflAssctBfrWqltTp * 1000) / 1000?(Math.round(minInflAssctBfrWqltTp * 1000) / 1000).toFixed(3) : '0.000', + inflAssctAftWqltToc : Math.round(minInflAssctAftWqltToc * 10) / 10?(Math.round(minInflAssctAftWqltToc * 10) / 10).toFixed(1) : '0.0', + inflAssctAftWqltSs : Math.round(minInflAssctAftWqltSs * 10) / 10?(Math.round(minInflAssctAftWqltSs * 10) / 10).toFixed(1) : '0.0', + bscFctCd: "", + bscFctNm: "", + lclgvrnmntNm: "", + sidoNm: "", + sggNm: "", + addr: "", + fctCpc: 0, + infoRnDay: 0, + tpInflWqltBod: Math.round(minTpInflWqltBod * 10) / 10?'0.0':(Math.round(minTpInflWqltBod * 10) / 10).toFixed(1), + tpInflWqltTp: Math.round(minTpInflWqltTp * 1000) / 1000?'0.000':(Math.round(minTpInflWqltTp * 1000) / 1000).toFixed(3) + }, + ...filteredData + ]; + return newFilteredData; + } + + // 날짜 필터링 관련 상태 + const [copyYearList, setCopyYearList] = useState<FacilityWaterQltyProps[]>([]); + const [chongInWaterQltyYearList, setChongInWaterQltyYearList] = useState<FacilityWaterQltyProps[]>([]); + + useEffect(() => { + searchFacilityWaterQltyListHist(searchYear, facilityCd); + }, [searchYear, facilityCd, searchFacilityWaterQltyListHist]); + + useEffect(() => { + const filteredYearData = setFilterAfterData(facilityWaterQltyYearList); + setCopyYearList(filteredYearData as FacilityWaterQltyProps[]); + setChongInWaterQltyYearList(filteredYearData as FacilityWaterQltyProps[]); + }, [facilityWaterQltyYearList]); + + const yesterDay = new Date(); + yesterDay.setDate(yesterDay.getDate() - 1); + const yesterDayStr = yesterDay.toISOString().split('T')[0]; + + const ref = useRef<ReturnType<FacilityWaterQltyProps>>({ + startDate: new Date(), + endDate: new Date(), + tabName: '', + filteredList: [], + }); + + const filterData = useCallback((returnType: ReturnType<FacilityWaterQltyProps>) => { + const filteredData = setFilterAfterData(returnType.filteredList); + setCopyYearList(filteredData as FacilityWaterQltyProps[]); + }, []); + + return ( + <> + <UITab tabList={tabList1} defaultValue="tab_num1"> + {/* 처리시설 수질현황 */} + <TabsContent value="tab_num1"> + <UITab tabList={tabList2} defaultValue="tab_term_year"> + <TabsContent value="tab_term_year"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={facilityWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName={"tab_term_year"} + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[350px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[310px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> + </UiTable> + </div> + <div className="card-header"> + <h3 className="text-2xl font-medium text-label">수질 TMS</h3> + <h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3> + </div> + <div className="overflow-y-auto h-[250px]"> + <UiTable + headName={undefined} + tableData={tmsList} + headlist={upHeadListModalTms} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalTms} layoutNum={4} /> + </UiTable> + {msrmtDate != yesterDayStr?.replaceAll('-','')?<h3 className="text-2xl text-left text-gray-500">※전날 데이터 결측으로 인해 가장 최근 데이터 표출함</h3> : ''} + </div> + </TabsContent> + + <TabsContent value="tab_term_summer"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={facilityWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_summer" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => { } } + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_winter"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={facilityWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_winter" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_chuncheon"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={facilityWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_chuncheon" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_rain"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={facilityWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_rain" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> + </UiTable> + </div> + </TabsContent> + </UITab> + </TabsContent> + + {/* 총인설비 수질현황 */} + <TabsContent value="tab_num2"> + <UITab tabList={tabList3} defaultValue="tab_term_year_chongin"> + <TabsContent value="tab_term_year_chongin"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={chongInWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_year" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalChongIn} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_summer_chongin"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={chongInWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_summer" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalChongIn} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_winter_chongin"> + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={chongInWaterQltyYearList} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_winter" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalChongIn} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> + </UiTable> + </div> + </TabsContent> + </UITab> + </TabsContent> + </UITab> + </> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/facilityWaterQlty/page.tsx b/apps/fo/app/pages/facilityWaterQlty/page.tsx new file mode 100644 index 000000000..2c728f679 --- /dev/null +++ b/apps/fo/app/pages/facilityWaterQlty/page.tsx @@ -0,0 +1,208 @@ +"use client" + +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import WaterQualityModal from "./modal/page"; +import type { FacilityWaterQltyProps } from "@/app/store/facilityWaterQlty"; + +export default function ProcessFacilitySearch() { + const { Common + , FacilityWaterQltyMain + , initializeCommonData + , searchFacilityWaterQltyList + } = useFoStore((state) => state) || { FacilityWaterQltyList: { facilityWaterQltyList: [], facilityWaterQltyCount: 0 } }; + + const { + isInit, + selectUpdownData, + selectOperationData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectSearchYear, + btnClass + } = Common; + + const { + mainLabelArray, + upHeadList1, + upHeadList2, + upHeadList3, + facilityWaterQltyList, + facilityWaterQltyCount, + excelList + } = FacilityWaterQltyMain; + + const mainList: FacilityWaterQltyProps[] = facilityWaterQltyList || []; + + // 그리드 관련 + const [resetPage, setResetPage] = useState<boolean>(false); + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); + const [fcltyName, setFcltyName] = useState<string>(""); + const [operationValue, setOperationValue] = useState<string>("0"); + const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0"); + const [fcltCpct, setFcltCpct] = useState<string>("0"); + + // 모달 상태 추가 + const [selectedRow, setSelectedRow] = useState<number | null>(null); + const [fcltyNm, setFcltyNm] = useState<string>(""); + const [fcltyCd, setFcltyCd] = useState<string>(""); + const [modalYear, setModalYear] = useState<string>(""); + + const searchFcltyQltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + let nameCd, partStr; + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + + if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue); + } + }, [operationValue, fcltyName, searchFacilityWaterQltyList, searchYearValue, fcltCpct]); + + const resetVal = () => { + setWidthSize("w-[120%]"); + setUpheadList(upHeadList1) + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYearValue(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + searchFacilityWaterQltyList(1, 10, selectSearchYear[0]?.val || "0"); + } + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + const facility = facilityWaterQltyList[index]; + if (facility) { + setFcltyNm(facility.bscFctNm); + setFcltyCd(facility.bscFctCd); + setModalYear(searchYearValue); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + } + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + } + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchFacilityWaterQltyList(1, 10, selectSearchYear[0]?.val || "0"); + } + }, [isInit, initializeCommonData, searchFacilityWaterQltyList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '시설별 수질현황', + sheetNameArray: ['시설별 수질현황'], + headerRange: 'A1:T1' + }); + } + }, [excelList]); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + const excelDownload = useCallback(() => { + searchFcltyQltyList(ref.current, 1, 10000); + }, [searchFcltyQltyList]); + + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchFcltyQltyList(ref.current, page, 10); + }, [searchFcltyQltyList]); + + const total = facilityWaterQltyCount || 1; + console.log('total : ', total); + return ( + <MainDiv> + <Title title="시설별 수질현황" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={mainLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className="col-span-3" label={mainLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={mainLabelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} /> + <SelectBox className="col-span-3" label={mainLabelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} isAdmin={false} /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 수질현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total<10?facilityWaterQltyList.length:total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + </div> + </ListDiv> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<WaterQualityModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={modalYear}></WaterQualityModal>} + bodyClass="sm:max-w-[90vw] w-[1200px] h-[920px]" + open={selectedRow !== null} + onOpenChange={(open) => !open && closeModal()} + noHeaderYn="Y" + noFooterYn="Y" + /> + )} + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/flowRate/modal/page.tsx b/apps/fo/app/pages/flowRate/modal/page.tsx new file mode 100644 index 000000000..665ade3b5 --- /dev/null +++ b/apps/fo/app/pages/flowRate/modal/page.tsx @@ -0,0 +1,238 @@ +"use client" +import FacilityTermModalHeader from "@/app/components/common/FacilityTermModalHeader"; +import { useFoStore } from "@/app/store"; +import type { FlowRateModalInterface } from "@/app/store/flowRate"; +import { UITab, UiTable } from "@common/business_components/ui"; +import { TabsContent } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; +import type { ReturnType } from "../../../components/common/FacilityTermModalHeader"; + +const tabList = [ + { value: 'tab_term_year', label: '연간' }, + { value: 'tab_term_summer', label: '하절기' }, + { value: 'tab_term_winter', label: '동절기' }, + { value: 'tab_term_chuncheon', label: '청천시' }, + { value: 'tab_term_rain', label: '강우시' } +]; + +export default function FlowRateModal(props: { fcltyName: string; facilityCd: string; searchYear: string }) { + // 그리드 관련 + const { FlowRateModal, searchFlowRateModalList } = useFoStore((state) => state); + const [copyYearList, setCopyYearList] = useState<FlowRateModalInterface[]>([]); + const [isLoading, setIsLoading] = useState(true); + + const { + modalFlowRateListYear, + upHeadListFlowRateModalYear, + } = FlowRateModal; + + const ref = useRef<ReturnType<FlowRateModalInterface>>({ + startDate: new Date(), + endDate: new Date(), + tabName: '', + filteredList: [], + }); + + const filterData = useCallback((returnType: ReturnType<FlowRateModalInterface>) => { + if (!returnType.filteredList) return; + const filteredData = setFilterAfterData(returnType.filteredList); + setCopyYearList(filteredData); + }, []); + + // 필터링 된 후 최대, 최소, 평균 값 계산 + const setFilterAfterData = (filteredData: FlowRateModalInterface[]) => { + if (!filteredData || filteredData.length === 0) return []; + + // 쉼표가 포함된 문자열을 숫자로 변환하는 함수 + const parseNumber = (value: string | number | null | undefined): number => { + if (value === null || value === undefined || value === '') return 0; + if (typeof value === 'number') return value; + return Number(value.replace(/,/g, '')); + }; + + let maxtotInflAmnt = 0; + let maxinflAmnt = 0; + let maxsumFlow = 0; + let maxreturnAmnt = 0; + let maxefflAmnt = 0; + + // 초기값 설정 + let mintotInflAmnt = parseNumber(filteredData[0]?.totInflAmnt); + let mininflAmnt = parseNumber(filteredData[0]?.inflAmnt); + let minsumFlow = parseNumber(filteredData[0]?.sumFlow); + let minreturnAmnt = parseNumber(filteredData[0]?.returnAmnt); + let minefflAmnt = parseNumber(filteredData[0]?.efflAmnt); + + let avgtotInflAmnt = 0; + let avginflAmnt = 0; + let avgsumFlow = 0; + let avgreturnAmnt = 0; + let avgefflAmnt = 0; + + filteredData.forEach((item: FlowRateModalInterface) => { + const totInflAmnt = parseNumber(item.totInflAmnt); + const inflAmnt = parseNumber(item.inflAmnt); + const sumFlow = parseNumber(item.sumFlow); + const returnAmnt = parseNumber(item.returnAmnt); + const efflAmnt = parseNumber(item.efflAmnt); + + maxtotInflAmnt = Math.max(maxtotInflAmnt, totInflAmnt); + maxinflAmnt = Math.max(maxinflAmnt, inflAmnt); + maxsumFlow = Math.max(maxsumFlow, sumFlow); + maxreturnAmnt = Math.max(maxreturnAmnt, returnAmnt); + maxefflAmnt = Math.max(maxefflAmnt, efflAmnt); + + mintotInflAmnt = Math.min(mintotInflAmnt, totInflAmnt); + mininflAmnt = Math.min(mininflAmnt, inflAmnt); + minsumFlow = Math.min(minsumFlow, sumFlow); + minreturnAmnt = Math.min(minreturnAmnt, returnAmnt); + minefflAmnt = Math.min(minefflAmnt, efflAmnt); + + avgtotInflAmnt += totInflAmnt; + avginflAmnt += inflAmnt; + avgsumFlow += sumFlow; + avgreturnAmnt += returnAmnt; + avgefflAmnt += efflAmnt; + }); + + 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, + }, + { + 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, + }, + { + 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, + }, + ...filteredData + ]; + return newFilteredData; + } + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + await searchFlowRateModalList(props.facilityCd, props.searchYear); + } finally { + setIsLoading(false); + } + }; + fetchData(); + }, [props.facilityCd, props.searchYear, searchFlowRateModalList]); + + if (isLoading) { + return <div>로딩 중...</div>; + } + + return ( + <> + <UITab tabList={tabList} defaultValue="tab_term_year"> + <TabsContent value="tab_term_year"> + <FacilityTermModalHeader<FlowRateModalInterface> + searchList={modalFlowRateListYear} + fcltyCd={props.facilityCd} + fcltyName={props.fcltyName} + tabName={"tab_term_year"} + searchYear={props.searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="h-[370px] overflow-y-auto overflow-x-hidden"> + <UiTable + headName="유량현황" + tableData={copyYearList} + headlist={upHeadListFlowRateModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[370px]" + boldArray={[0,1,2]} + /> + </div> + </TabsContent> + <TabsContent value="tab_term_summer"> + <FacilityTermModalHeader<FlowRateModalInterface> + searchList={modalFlowRateListYear} + fcltyCd={props.facilityCd} + fcltyName={props.fcltyName} + tabName={"tab_term_summer"} + searchYear={props.searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="h-[370px] overflow-y-auto overflow-x-hidden"> + <UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}> + {null} + </UiTable> + </div> + </TabsContent> + <TabsContent value="tab_term_winter"> + <FacilityTermModalHeader<FlowRateModalInterface> + searchList={modalFlowRateListYear} + fcltyCd={props.facilityCd} + fcltyName={props.fcltyName} + tabName={"tab_term_winter"} + searchYear={props.searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="h-[370px] overflow-y-auto overflow-x-hidden"> + <UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}> + {null} + </UiTable> + </div> + </TabsContent> + <TabsContent value="tab_term_chuncheon"> + <FacilityTermModalHeader<FlowRateModalInterface> + searchList={modalFlowRateListYear} + fcltyCd={props.facilityCd} + fcltyName={props.fcltyName} + tabName={"tab_term_chuncheon"} + searchYear={props.searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="h-[370px] overflow-y-auto overflow-x-hidden"> + <UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}> + {null} + </UiTable> + </div> + </TabsContent> + <TabsContent value="tab_term_rain"> + <FacilityTermModalHeader<FlowRateModalInterface> + searchList={modalFlowRateListYear} + fcltyCd={props.facilityCd} + fcltyName={props.fcltyName} + tabName={"tab_term_rain"} + searchYear={props.searchYear} + ref={ref} + queryClick={filterData} + /> + <div className="h-[370px] overflow-y-auto overflow-x-hidden"> + <UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}> + {null} + </UiTable> + </div> + </TabsContent> + </UITab> + </> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/flowRate/page.tsx b/apps/fo/app/pages/flowRate/page.tsx new file mode 100644 index 000000000..3910944cf --- /dev/null +++ b/apps/fo/app/pages/flowRate/page.tsx @@ -0,0 +1,207 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import FlowRateModal from "./modal/page"; +import type { ExcelType } from "@/app/store/flowRate"; + +export default function ProcessFacilitySearch() { + const {Common + ,FlowRateSearch + ,FlowRateList + ,initializeCommonData + ,searchFlowRateList + } = useFoStore((state) => state); + + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + const { + isInit, + selectUpdownData, + selectSearchYear, + selectOperationData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + btnClass + } = Common; + + const { + flowRateSearchLabelArray + } = FlowRateSearch; + + // 그리드 관련 + const { + upHeadList1, + upHeadList2, + upHeadList3, + flowRateList, + flowRateCount, + excelList + } = FlowRateList; + + // 입력값 상태 관리 + const [fcltCpct, setFcltCpct] = useState<string>("0"); + const [fcltyName, setFcltyName] = useState<string>(""); + const [operationValue, setOperationValue] = useState<string>("0"); + const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0"); + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); + const [resetPage, setResetPage] = useState<boolean>(false); + const prevExcelListRef = useRef<ExcelType[] | null>(null); + + // 모달 상태 추가 + const [selectedRow, setSelectedRow] = useState<number | null>(null); + const [fcltyNm, setFcltyNm] = useState<string>(""); + const [fcltyCd, setFcltyCd] = useState<string>(""); + + const searchFlowRate = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchFlowRateList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchFlowRateList(pageNo, pageCnt, searchYearValue); + } + }, [operationValue, fcltyName, searchFlowRateList, searchYearValue, fcltCpct]); + + const resetVal = () => { + setWidthSize("w-[120%]"); + setUpheadList(upHeadList1) + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYearValue(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0"); + } + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + if (flowRateList[index]) { + setFcltyNm(flowRateList[index].bscFctNm); + setFcltyCd(flowRateList[index].bscFctCd); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + }; + + // 페이지 네비게이션션 + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchFlowRate(ref.current, page, 10); + }, [searchFlowRate]); + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + }; + + // 시도 데이터 초기화 + useEffect(() => { + if(!isInit) { + initializeCommonData(); + searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0"); + } + }, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ data: JSON.parse(JSON.stringify(excelList)), fileName: '시설별 유량현황', sheetNameArray: ['시설별 유량현황'], headerRange: 'A1:O1' }); + } else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) { + alert('다운받을 데이터가 없습니다.'); + } + prevExcelListRef.current = excelList; + }, [excelList]); + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + const excelDownload = useCallback(() => { + searchFlowRate(ref.current, 1, 10000); + }, [searchFlowRate]); + + const total = flowRateCount; + + return ( + <MainDiv> + <Title title="시설별 유량현황" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={flowRateSearchLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className="col-span-3" label={flowRateSearchLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={flowRateSearchLabelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} /> + <SelectBox className="col-span-3" label={flowRateSearchLabelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} isAdmin={false} /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 유량현황"} tableData={flowRateList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + </div> + </ListDiv> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<FlowRateModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYearValue}></FlowRateModal>} + bodyClass="sm:max-w-[90vw] w-[1050px] h-[700px]" + open={selectedRow !== null} + onOpenChange={(open: boolean) => !open && closeModal()} + /> + )} + </MainDiv> + ); +} diff --git a/apps/fo/app/pages/map/page.tsx b/apps/fo/app/pages/map/page.tsx new file mode 100644 index 000000000..cbbe0dd11 --- /dev/null +++ b/apps/fo/app/pages/map/page.tsx @@ -0,0 +1,145 @@ +"use client"; + +import Gis from "@common/business_components/ui/map/Gis"; +import GisApp from "@common/business_components/ui/map/lib/gis.core"; +import { useEffect, useRef, useState } from "react"; + +interface FeatureData { + fid?: string; + ftrIdn?: string; + id?: string; + getGeometry?: () => { getExtent: () => number[] }; + setStyle?: () => void; +} + +interface MapInstance { + getLayers: () => { getArray: () => Array<{ getSource?: () => { getFeatureById?: (id: string) => FeatureData | null } }> }; + getView: () => { fit: (extent: number[], options: object) => void }; +} + +const Map = () => { + const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 + const gisMaps = useRef<MapInstance | null>(null); + const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 + const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태 + const [featureData, setFeatureData] = useState<FeatureData | null>(null); // 선택된 피쳐 데이터 + + const fetchData = async () => { + try { + const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map` + const reqUrl = `${API_URL}/lyrStngs/FO_NETWORK`; + const response = await fetch(reqUrl, { + method: "GET", + headers: { + accept: "application/json", + } + }); + + if (!response.ok) { + throw new Error(`HTTP 오류! 상태: ${response.status}`); + } + + const data = await response.json(); // 여기서 json 파싱 + setTreeData(data); // 데이터 설정 + } catch (error) { + console.error("데이터 불러오기 실패:", error); + } + }; + + // 컴포넌트 마운트 시 1회 실행 + useEffect(() => { + fetchData(); + }, []); + + // SearchModal, GisContent 등에서 호출 + const onFeatureSelect = (data: FeatureData) => { + const map = gisMaps.current; + if (!map) { + console.warn("지도가 초기화되지 않았습니다."); + return; + } + + // 항상 같은 select 객체 사용 + const select = (GisApp.Module.select as unknown) as { resetSelectedFeature: () => void; selectFeature: (feature: FeatureData, selected: boolean) => void } | undefined; + if (!select) return; + + // 1. 기존 선택 초기화 + select.resetSelectedFeature(); + + // 2. data가 OpenLayers Feature 인스턴스인지 확인 + let foundFeature: FeatureData | null = null; + if ( + data && + typeof data.getGeometry === "function" && + typeof data.setStyle === "function" + ) { + // 이미 Feature 인스턴스라면 그대로 사용 + foundFeature = data; + } else { + // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 + const featureId = data.fid || data.ftrIdn || data.id; + map + .getLayers() + .getArray() + .forEach((layer) => { + const source = layer.getSource?.(); + if (source?.getFeatureById) { + const feature = source.getFeatureById(String(featureId).trim()); + if (feature) foundFeature = feature; + } + }); + } + + // 3. 피처 선택 + if (foundFeature && foundFeature.getGeometry) { + const geometry = foundFeature.getGeometry(); + if (geometry) { + const extent = geometry.getExtent(); + + map.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 16, + constrainResolution: true, + nearest: true, + }); + } + select.selectFeature(foundFeature, true); + } else { + console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); + } + + setFeatureData(data); + setIsOpen(true); + setIsSearchModalOpen(false); + }; + + // onCheckedLayersChange handler + const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: unknown[]) => { + // Handle checked layers change if needed + console.log('Checked layers changed:', checkedIds, checkedNodesData); + }; + + return ( + <div className="bg-container"> + <Gis + treeData={treeData} + isSearch={false} + gisMaps={gisMaps} + isMiniMap={true} + searchView={null} + detailView={null} + onFeatureSelect={onFeatureSelect} + setIsOpen={setIsOpen} + setFeatureData={setFeatureData} + featureData={featureData} + setIsSearchModalOpen={setIsSearchModalOpen} + isOpen={isOpen} + isSearchModalOpen={isSearchModalOpen} + onCheckedLayersChange={handleCheckedLayersChange} + /> + </div> + ); +}; + +export default Map; diff --git a/apps/fo/app/pages/planSewage/page.tsx b/apps/fo/app/pages/planSewage/page.tsx new file mode 100644 index 000000000..8d7de5d80 --- /dev/null +++ b/apps/fo/app/pages/planSewage/page.tsx @@ -0,0 +1,197 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; + +export default function PlanSewage() { + const {Common + ,PlanSewage + ,PlanSewageSearch + ,initializeCommonData + ,searchPlanSewageList + } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }}; + + const { + isInit, + selectUpdownData, + selectOperationData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectSearchYear, + btnClass + } = Common; + + const { + labelArray, + } = PlanSewageSearch; + + const { + upHeadList1, + upHeadList2, + upHeadList3, + planSewageList, + planSewageCount, + excelList + } = PlanSewage; + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + // 그리드 관련 + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + // 모달 상태 추가 + + // 초기화 변수 + const [resetPage, setResetPage] = useState<boolean>(false); + // 표 너비 + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + // 구분별 그리드 헤더 + const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); + // 용량별 + const [fcltCpct, setFcltCpct] = useState<string>("0"); + // 시설명 + const [fcltyName, setFcltyName] = useState<string>(""); + // 시설상태 + const [operationValue, setOperationValue] = useState<string>("0"); + // 조회기간 + const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + + /** + * 페이지 함수 + */ + // 초기화 + const resetVal = () => { + setWidthSize("w-[120%]"); + setUpheadList(upHeadList1); + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYear(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + } + + /** + * useEffect + */ + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchPlanSewageList(1, 10, searchYear, ref.current.partValue); + } + }, [isInit, initializeCommonData, searchYear, searchPlanSewageList]); + + useEffect(() => { + console.log('excelList' , excelList); + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '계획하수량 산정지표 현황', + sheetNameArray: ['계획하수량 산정지표 현황'], + headerRange: 'A1:M1' + }); + } + }, [excelList]); + + /** + * 콜백함수 + */ + // 조회 + const searchPlanSewage = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchPlanSewageList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchPlanSewageList(pageNo, pageCnt, searchYear, partValue); + } + }, [operationValue, fcltyName, searchPlanSewageList, searchYear, fcltCpct]); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + // 엑셀다운로드 + const excelDownload = useCallback((searchReturn: ReturnType) => { + searchPlanSewage(searchReturn, 1, 10000); + }, [searchPlanSewage]); + + // 페이지 변경 + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchPlanSewage(ref.current, page, 10); + }, [searchPlanSewage]); + + const mainList = planSewageList || []; + const total = planSewageCount || 0; + + return ( + <MainDiv> + <Title title="계획하수량 산정지표 현황" /> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} isAdmin={false}/> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={ undefined } headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + </div> + </ListDiv> + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/processArea/HeadMakeColSpan.tsx b/apps/fo/app/pages/processArea/HeadMakeColSpan.tsx new file mode 100644 index 000000000..ffd556b2e --- /dev/null +++ b/apps/fo/app/pages/processArea/HeadMakeColSpan.tsx @@ -0,0 +1,87 @@ +import { TableHead, TableRow } from "@common/components/ui"; +import type { TableUpperProps } from "../../store/dashboard"; + +const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: number }) => { + return ( + layoutNum === 0 ? ( + <> + <TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) => + (head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> : + head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> : + head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={3} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : + head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={3} key={head.id}>{head.title}</TableHead> : + head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={2}>{'행정구역'}</TableHead> : + head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={6}>{'증설계획'}</TableHead> : + head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={19}>{'시설계획'}</TableHead> : + !head.upName ? <TableHead rowSpan={3} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => ( + head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> : + head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> : + head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> : + null + ))} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe' || head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>; + } + })} + </TableRow> + </> + ) : layoutNum === 1 ? ( + <> + <TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) => + (head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> : + head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> : + head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={3} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : + head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={3} key={head.id}>{head.title}</TableHead> : + head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={3}>{'유역'}</TableHead> : + head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={6}>{'증설계획'}</TableHead> : + !head.upName ? <TableHead rowSpan={3} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => ( + head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> : + head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> : + head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> : + null + ))} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe' || head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>; + } + })} + </TableRow> + </> + ) : ( + <> + <TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) => + (head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> : + head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> : + head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : + head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> : + head.upName === 'upChangeRe1' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'권역'}</TableHead> : + head.upName === 'upChangeRe2' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'환경청'}</TableHead> : + head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> : + head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> : + head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> : + !head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))} + </TableRow> + <TableRow className="bg-gray-50"> + {upHeadList.map((head: TableUpperProps) => { + if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' ||head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') { + return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>; + } + })} + </TableRow> + </> + ) + ); +} + +export { HeadMakeColSpan }; \ No newline at end of file diff --git a/apps/fo/app/pages/processArea/modal/page.tsx b/apps/fo/app/pages/processArea/modal/page.tsx new file mode 100644 index 000000000..06e26bac6 --- /dev/null +++ b/apps/fo/app/pages/processArea/modal/page.tsx @@ -0,0 +1,55 @@ +"use client" +import { useFoStore } from "@/app/store"; +import { UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Table, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui"; +import { useEffect } from "react"; + +export default function ProcessFacilityInfoModal(props: { fcltyName: string; facilityCd : string;}) { + // 그리드 관련 + const {ProcessAreaDetail, initModalData} = useFoStore((state) => state); + + useEffect(() => { + initModalData(props.facilityCd); + }, [initModalData, props.facilityCd]); + + const { + upHeadListFcltyImproveInfo, + gridListFcltyImproveInfo, + upHeadListProcessPopulation, + gridListProcessPopulation, + upHeadListProcessAreaPlan, + gridListProcessAreaPlan, + } = ProcessAreaDetail + + return ( + <> + <ListDiv> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <TableHeader> + <TableRow> + <TableHead className="w-1/6">{'시설명'}</TableHead> + <TableCell className="w-1/3">{props.fcltyName}</TableCell> + <TableHead className="w-1/6">{'시설코드'}</TableHead> + <TableCell className="w-1/3">{props.facilityCd}</TableCell> + </TableRow> + </TableHeader> + </Table> + </div> + </ListDiv> + <ListDiv> + <UiTable headName={"시설증설 계획"} tableData={gridListFcltyImproveInfo} headlist={upHeadListFcltyImproveInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}> + </UiTable> + </ListDiv> + <ListDiv> + <UiTable headName={"처리인구 계획"} tableData={gridListProcessPopulation} headlist={upHeadListProcessPopulation} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}> + </UiTable> + </ListDiv> + <ListDiv> + <UiTable headName={"처리구역 계획"} tableData={gridListProcessAreaPlan} headlist={upHeadListProcessAreaPlan} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}> + </UiTable> + </ListDiv> + </> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/processArea/page.tsx b/apps/fo/app/pages/processArea/page.tsx new file mode 100644 index 000000000..92369cb45 --- /dev/null +++ b/apps/fo/app/pages/processArea/page.tsx @@ -0,0 +1,172 @@ +"use client" +import { useFoStore } from "@/app/store"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import ProcessFacilityInfoModal from "./modal/page"; + +export default function ProcessArea() { + const {Common, + ProcessArea, + ProcessAreaSearch, + initializeCommonData, + searchProcessAreaList + } = useFoStore((state) => state) || { + Common: { isInit: false, selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" }, + ProcessArea: { processAreaList: [], processAreaCount: 0, excelList: [], upHeadList1: [] }, + ProcessAreaSearch: { processFacilityLabelArray: [] }, + initializeCommonData: () => {}, + searchProcessAreaList: () => {} + }; + + const { + isInit, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectSearchYear, + btnClass, + } = Common; + + const { + processFacilityLabelArray, + } = ProcessAreaSearch; + + const { + upHeadList1, + processAreaList, + processAreaCount, + excelList + } = ProcessArea; + + const mainList: Record<string, unknown>[] = processAreaList || []; + // 그리드 관련 + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + // 모달 상태 추가 + const [selectedRow, setSelectedRow] = useState<number | null>(null); + const [fcltyNm, setFcltyNm] = useState<string>(""); + const [fcltyCd, setFcltyCd] = useState<string>(""); + + const [resetPage, setResetPage] = useState<boolean>(false); + const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0"); + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + const searchProcessArea = useCallback(({ sidoText, sigunText}: ReturnType, pageNo: number, pageCnt:number=20) => { + searchProcessAreaList(pageNo, pageCnt, sidoText, sigunText, searchYearValue); + }, [searchProcessAreaList, searchYearValue]); + + // 조회 + const chkVal = useCallback(() => { + setResetPage(true); + }, []); + + // 초기화 + const resetVal = () => { + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + setSearchYearValue(selectSearchYear[0]?.val || "0"); + searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val); + } + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + if (processAreaList[index]) { + setFcltyNm(processAreaList[index].swtrmtFcltNm); + setFcltyCd(processAreaList[index].swtrmtFcltCd); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + }; + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + }; + + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val); + } + }, [isInit, initializeCommonData, searchProcessAreaList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '처리구역정보', + sheetNameArray: ['처리구역정보'], + headerRange: 'A1:W1' + }); + } + }, [excelList]); + + const excelDownload = useCallback(() => { + searchProcessArea(ref.current, 1, 10000); + }, [searchProcessArea]); + + const handlePageChange = useCallback((pageNo: number) => { + setResetPage(false); + searchProcessArea(ref.current, pageNo, 10); + }, [searchProcessArea]); + + const total = processAreaCount; + return ( + <MainDiv> + <Title title="처리구역정보" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined}/> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} + isAdmin={true} + /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className="w-[180%]"> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"처리구역정보"} tableData={mainList} headlist={upHeadList1} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + </div> + </ListDiv> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<ProcessFacilityInfoModal fcltyName={fcltyNm} facilityCd={fcltyCd}></ProcessFacilityInfoModal>} + bodyClass="sm:max-w-[90vw] w-[1200px] h-[900px]" + open={selectedRow !== null} + onOpenChange={(open: boolean) => !open && closeModal()} + /> + )} + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/processFacility/modal/page.tsx b/apps/fo/app/pages/processFacility/modal/page.tsx new file mode 100644 index 000000000..3afed1166 --- /dev/null +++ b/apps/fo/app/pages/processFacility/modal/page.tsx @@ -0,0 +1,211 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/dashboard"; +import { UITab, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { + TabsContent +} from "@common/components"; +import { Table, TableCell, TableHead, TableRow } from "@common/components/ui"; + +type ModalType = { + fcltyName: string; + facilityCd : string; + sido : string; + gugun : string; + daddr : string; + fctCpc : number | ''; + trtFrfNm : string; + rnDt : string; + cmplDt : string; + trstOprEtpNm : string; + bscFctClsNm : string; + mnstr : string; + efflWtrrgn : string; + lgbsn : string; + efflWqltStndrdNm : string; + rgnNm : string; + rvbsn : string; +} + +// 시설계획 table header 변경 +const headMakeList = (upHeadList: TableUpperProps[], gridNum:number) => { + if(gridNum===1){ + return ( <> + <TableRow>{ + upHeadList.map((head: TableUpperProps) => + (!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>: + head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'증설계획(m³/일)'}</TableHead>:null))} + </TableRow> + <TableRow> + {upHeadList.map((head: TableUpperProps) => + (head.upName === 'upChangeRe'?<TableHead className="w-150" key={head.id}>{head.title}</TableHead>:null))} + </TableRow> + </> ) + } else if(gridNum===2){ + return ( <> + <TableRow>{ + upHeadList.map((head: TableUpperProps) => + (!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>: + head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획유입수질(mg/L)'}</TableHead>: + head.upName === 'upChangeRe2'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'설계유입수질(mg/L)'}</TableHead>:null))} + </TableRow> + <TableRow> + {upHeadList.map((head: TableUpperProps) => + (head.upName === 'upChangeRe1'||head.upName === 'upChangeRe2'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))} + </TableRow> + </> ) + } else if(gridNum===3){ + return ( <> + <TableRow>{ + upHeadList.map((head: TableUpperProps) => + (!head.upSequnce?<TableHead key={head.id}>{head.title}</TableHead>: + head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} colSpan={2} style={{ whiteSpace: 'pre-line' }}>{'사업기간'}</TableHead>:null))} + </TableRow> + </> ) + } else if(gridNum===4){ + return ( <> + <TableRow>{ + upHeadList.map((head: TableUpperProps) => + (!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>: + head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획유입수질(mg/L)'}</TableHead>: + head.upName === 'upChangeRe2'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'설계유입수질(mg/L)'}</TableHead>: + head.upName === 'upChangeRe3'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획방류수질(mg/L)'}</TableHead>:null))} + </TableRow> + <TableRow> + {upHeadList.map((head: TableUpperProps) => + (head.upName === 'upChangeRe1'||head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))} + </TableRow> + </> ) + } +} + +export default function ProcessFacilityInfoModal(props: ModalType) { + // 그리드 관련 + const {ProcessFacilityDetail} = useFoStore((state) => state); + + const { + upHeadListPlanInfo, + gridListPlanInfo, + upHeadListFcltyAreaInfo, + gridListFcltyAreaInfo, + + upHeadListFcltyHistInfo, + gridListFcltyHistInfo, + upHeadListFcltyWaterQltyInfo, + gridListFcltyWaterQltyInfo, + + upHeadListLegalWaterQlty, + gridListLegalWaterQlty, + } = ProcessFacilityDetail; + + gridListFcltyAreaInfo.map((item) => { + item.efflWtrrgn = props.efflWtrrgn; + item.lgbsn = props.lgbsn; + item.efflWqltStndrdNm = props.efflWqltStndrdNm; + item.rgnNm = props.rgnNm; + item.rvbsn = props.rvbsn; + }) + + const tabList = [ + { value: 'common_info', label: '기본정보' }, + { value: 'history_info', label: '이력정보' }, + ]; + const fctCpc = props.fctCpc ? props.fctCpc.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ''; + const upHeadListPlanInfoChange = headMakeList(upHeadListPlanInfo, 2); + const upHeadListFcltyHistInfoChange = headMakeList(upHeadListFcltyHistInfo, 3); + const upHeadListFcltyWaterQltyInfoChange = headMakeList(upHeadListFcltyWaterQltyInfo, 4); + return ( + <> + <ListDiv className="!h-[80px]"> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <thead> + <TableRow className=""> + <TableHead className="w-1/6">{'시설명'}</TableHead> + <TableCell className="w-1/3">{props.fcltyName}</TableCell> + <TableHead className="w-1/6">{'시설코드'}</TableHead> + <TableCell className="w-1/3">{props.facilityCd}</TableCell> + </TableRow> + </thead> + </Table> + </div> + </ListDiv> + <div className="overflow-y-auto"> + <div className="!h-[600px]"> + <ListDiv className=""> + <UITab tabList={tabList} defaultValue="common_info"> + <TabsContent value="common_info"> + <div className="card-header"> + <h3 className="text-2xl font-medium text-label">시설 기본정보</h3> + </div> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <thead> + <TableRow className=""> + <TableHead className="w-1/6">{'시설명'}</TableHead> + <TableCell className="w-1/6">{props.fcltyName}</TableCell> + <TableHead className="w-1/6">{'시설구분'}</TableHead> + <TableCell className="w-1/6">{props.bscFctClsNm}</TableCell> + <TableHead className="w-1/6">{'시설용량(m³/일)'}</TableHead> + <TableCell className="w-1/6">{fctCpc}</TableCell> + </TableRow> + </thead> + <tbody> + <TableRow className=""> + <TableHead className="w-1/6">{'시도'}</TableHead> + <TableCell className="w-1/6">{props.sido}</TableCell> + <TableHead className="w-1/6">{'시군구'}</TableHead> + <TableCell className="w-1/6">{props.gugun}</TableCell> + <TableHead className="w-1/6">{'가동개시일'}</TableHead> + <TableCell className="w-1/6">{props.rnDt}</TableCell> + </TableRow> + <TableRow className=""> + <TableHead className="w-1/6">{'주소'}</TableHead> + <TableCell className="w-1/6" colSpan={3}>{props.daddr}</TableCell> + <TableHead className="w-1/6">{'준공일'}</TableHead> + <TableCell className="w-1/6">{props.cmplDt}</TableCell> + </TableRow> + <TableRow className=""> + <TableHead className="w-1/6">{'공법'}</TableHead> + <TableCell className="w-1/6" colSpan={3}>{props.trtFrfNm}</TableCell> + <TableHead className="w-1/6">{'관리대행업자'}</TableHead> + <TableCell className="w-1/6">{props.trstOprEtpNm}</TableCell> + </TableRow> + </tbody> + </Table> + </div> + <UiTable headName={"시설 계획정보"} tableData={gridListPlanInfo} headlist={upHeadListPlanInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}> + {upHeadListPlanInfoChange} + </UiTable> + <UiTable headName={"시설 법적방류수질"} tableData={gridListLegalWaterQlty} headlist={upHeadListLegalWaterQlty} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}} /> + <UiTable headName={"시설 수역 및 구역정보"} tableData={gridListFcltyAreaInfo} headlist={upHeadListFcltyAreaInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}} /> + </TabsContent> + + <TabsContent value="history_info"> + <ListDiv> + <div className="h-[270px] w-full overflow-y-auto overflow-x-hidden"> + <div className="h-[130%]"> + <UiTable headName={"시설 이력관리"} tableData={gridListFcltyHistInfo} headlist={upHeadListFcltyHistInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}> + {upHeadListFcltyHistInfoChange} + </UiTable> + </div> + </div> + </ListDiv> + <ListDiv> + <div className="h-[270px] w-full overflow-y-auto overflow-x-hidden"> + <div className="h-[130%]"> + <UiTable headName={"시설 수질정보"} tableData={gridListFcltyWaterQltyInfo} headlist={upHeadListFcltyWaterQltyInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}> + {upHeadListFcltyWaterQltyInfoChange} + </UiTable> + </div> + </div> + </ListDiv> + </TabsContent> + </UITab> + </ListDiv> + </div> + </div> + </> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/processFacility/page.tsx b/apps/fo/app/pages/processFacility/page.tsx new file mode 100644 index 000000000..ed58dea2b --- /dev/null +++ b/apps/fo/app/pages/processFacility/page.tsx @@ -0,0 +1,294 @@ +"use client" +import { HeadMakeColSpan } from "@/app/components/common/HeadMakeColSpan"; +import { useFoStore } from "@/app/store"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import ProcessFacilityInfoModal from "./modal/page"; + +export default function ProcessFacilitySearch() { + const {Common + ,ProcessFacility + ,ProcessFacilityList + ,initializeCommonData + //시설데이터 조회 + ,searchProcessFacility + //시설데이터 이력정보 조회 + ,searchProcessFacilityHist + //시설데이터 수질정보 조회 + ,searchProcessFacilityWaterQlty + } = useFoStore((state) => state) || { ProcessFacilityList: { processFacilityList: [], processFacilityCount: 0 }}; + + const { + isInit, + selectUpdownData, + selectOperationData, + selectFacilityPartData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + btnClass, + } = Common; + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + useEffect(() => { + if(!isInit) { + initializeCommonData(); + searchProcessFacility(1, 10, 'A', '0', '0', '0', '0'); + } + }, [isInit + ,initializeCommonData + ,searchProcessFacility + ]); + + const { + processFacilityLabelArray, + } = ProcessFacility; + + const { + upHeadList1, + upHeadList2, + upHeadList3, + excelList + } = ProcessFacilityList; + + const processFacilityList = ProcessFacilityList?.processFacilityList || []; + + // 그리드 관련 + const inputRef = useRef<HTMLInputElement>(null); + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + const [fcltCpct, setFcltCpct] = useState<string>("0"); + const [fcltyName, setFcltyName] = useState<string>(""); + const [operationValue, setOperationValue] = useState<string>("0"); + const [facilityPartValue, setFacilityPartValue] = useState<string>("0"); + + const [resetPage, setResetPage] = useState<boolean>(false); + + const [partValue, setPartValue] = useState<string>("A"); + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + const [upHeadList, setUpheadList] = useState(upHeadList1); + + /** + * 모달 상태 변수 + * selectedRow : 클릭한 행의 index + * modalFcltyNm : 시설명 + * modalFcltyCd : 시설코드 + * modalSido : 시도 + * modalGugun : 시군구 + * modalLaeNm : 주소 + * modalFctCpc : 시설용량 + * modalTrtFrfNm : 공법 + * modalRnDt : 가동개시일 + * modalCmplDt : 준공일 + * modalTrstOprEtpNm : 관리대행업자 + * modalBscFctClsNm : 시설구분 + */ + const [selectedRow, setSelectedRow] = useState<number | null>(null); + const [modalFcltyNm, setModalFcltyNm] = useState<string>(""); + const [modalFcltyCd, setModalFcltyCd] = useState<string>(""); + const [modalSido, setModalSido] = useState<string>(""); + const [modalGugun, setModalGugun] = useState<string>(""); + const [modalAddr, setModalAddr] = useState<string>(""); + const [modalFctCpc, setModalFctCpc] = useState<number | ''>(""); + const [modalTrtFrfNm, setModalTrtFrfNm] = useState<string>(""); + const [modalRnDt, setModalRnDt] = useState<string>(""); + const [modalCmplDt, setModalCmplDt] = useState<string>(""); + const [modalTrstOprEtpNm, setModalTrstOprEtpNm] = useState<string>(""); + const [modalBscFctClsNm, setModalBscFctClsNm] = useState<string>(""); + const [modalMnstr, setModalMnstr] = useState<string>(""); + const [modalEfflWtrrgn, setModalEfflWtrrgn] = useState<string>(""); + const [modalLgbsn, setModalLgbsn] = useState<string>(""); + const [modalEfflWqltStndrdNm, setModalEfflWqltStndrdNm] = useState<string>(""); + const [modalRgnNm, setModalRgnNm] = useState<string>(""); + const [modalRvbsn, setModalRvbsn] = useState<string>(""); + const [isSearch, setIsSearch] = useState<boolean>(false); + + // 표 레이아웃 상태 + const chkVal = useCallback((result:ReturnType) => { + setPartValue(result.partValue); + setResetPage(true); + setIsSearch(true); + }, []); + + useEffect(() => { + switch (partValue) { + case 'A': + setWidthSize("w-[130%]"); + setUpheadList(upHeadList1); + break; + case 'R': + setWidthSize("w-[140%]"); + setUpheadList(upHeadList2); + break; + case 'G': + setWidthSize("w-[150%]"); + setUpheadList(upHeadList3); + break; + } + }, [partValue, upHeadList1, upHeadList2, upHeadList3]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ data: excelList as unknown as JSON[], fileName: '처리시설 시설정보', sheetNameArray: ['처리시설 시설정보'], headerRange: 'A1:P1' }); + } + }, [excelList]); + + const searchFcltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=10) => { + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + searchProcessFacility(pageNo, pageCnt, partStr, nameCd || '0', fcltCpct, operationValue, facilityPartValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + if(isSearch) { + setIsSearch(false); + } + }, [facilityPartValue, fcltCpct, fcltyName, isSearch, operationValue, searchProcessFacility]) + + const changePage = useCallback((page: number) => { + searchFcltyList(ref.current, page); + },[searchFcltyList]) + + const resetVal = () => { + // SelectBox 초기화 - 각 selectArray의 첫 번째 옵션 값 사용 + searchProcessFacility(1, 10, 'A', '0', '0', '0', '0', ''); + setFcltCpct("0"); + setOperationValue("0"); + setFacilityPartValue("0"); + setPartValue('A'); + setUpheadList(upHeadList1); + // SearchInputSelect 초기화 + setFcltyName(""); + + // ref를 사용하여 input 값 초기화 + if (inputRef.current) { + inputRef.current.value = ""; + } + + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + } + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + const facility = processFacilityList[index]; + if (facility) { + setModalFcltyNm(facility.bscFctNm); + setModalFcltyCd(facility.bscFctCd); + setModalSido(facility.sido); + setModalGugun(facility.gugun); + setModalAddr(facility.daddr); + setModalFctCpc(Number(facility.fctCpc)); + setModalTrtFrfNm(facility.trtFrfNm); + setModalRnDt(facility.rnDt); + setModalCmplDt(facility.cmplDt); + setModalTrstOprEtpNm(facility.trstOprEtpNm); + setModalBscFctClsNm(facility.bscFctClsNm); + setModalMnstr(facility.mnstr); + setModalEfflWtrrgn(facility.efflWtrrgn); + setModalLgbsn(facility.lgbsn); + setModalEfflWqltStndrdNm(facility.efflWqltStndrdNm); + setModalRgnNm(facility.rgnNm); + setModalRvbsn(facility.rvbsn); + + searchProcessFacilityHist(facility.bscFctCd); + searchProcessFacilityWaterQlty(facility.bscFctCd); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + } + + const excelDownload = useCallback(() => { + searchFcltyList(ref.current, 1, 10000); + }, [searchFcltyList]); + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + } + + const total = ProcessFacilityList?.processFacilityCount || 1; + + return ( + <MainDiv> + <Title title="처리시설 시설정보" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={processFacilityLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={val => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className="col-span-3" label={processFacilityLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={processFacilityLabelArray[5] as string} selectArray={selectOperationData} onSelectValue={val => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + <SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectFacilityPartData} onSelectValue={val => setFacilityPartValue(val.toString())} value={facilityPartValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} isAdmin={false} /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable + cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} + headName={"처리시설 시설정보"} + tableData={processFacilityList} + headlist={upHeadList} + pageSize={10} + total={total} + resetPageNo={resetPage} + onPageChange={changePage} + > + <HeadMakeColSpan upHeadList={upHeadList} layoutNum={partValue}/> + </UiTable> + </div> + </div> + </ListDiv> + <span className="text-2xl text-gray-500">※계획유입수질 및 설계유입수질은 해당 BOD를 나타냄</span> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<ProcessFacilityInfoModal fcltyName={modalFcltyNm} facilityCd={modalFcltyCd} sido={modalSido} gugun={modalGugun} daddr={modalAddr} fctCpc={modalFctCpc} trtFrfNm={modalTrtFrfNm} rnDt={modalRnDt} cmplDt={modalCmplDt} trstOprEtpNm={modalTrstOprEtpNm} bscFctClsNm={modalBscFctClsNm} mnstr={modalMnstr} efflWtrrgn={modalEfflWtrrgn} lgbsn={modalLgbsn} efflWqltStndrdNm={modalEfflWqltStndrdNm} rgnNm={modalRgnNm} rvbsn={modalRvbsn}></ProcessFacilityInfoModal>} + bodyClass="sm:max-w-[70vw] sm:max-h-[95vh]" + open={selectedRow !== null} + onOpenChange={(open) => !open && closeModal()} + /> + )} + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/rdi/page.tsx b/apps/fo/app/pages/rdi/page.tsx new file mode 100644 index 000000000..d28c7241d --- /dev/null +++ b/apps/fo/app/pages/rdi/page.tsx @@ -0,0 +1,200 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; + +export default function Rdi() { + const {Common + ,initializeCommonData + ,Rdi + ,RdiSearch + ,searchRdiList + } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }}; + + const { + isInit, + selectUpdownData, + selectOperationData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectSearchYear, + btnClass + } = Common; + + const { + labelArray, + } = RdiSearch; + + const { + upHeadList1, + upHeadList2, + upHeadList3, + rdiList, + rdiCount, + excelList, + } = Rdi; + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + // 그리드 관련 + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + // 초기화 변수 + const [resetPage, setResetPage] = useState<boolean>(false); + // 표 너비 + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + // 구분별 그리드 헤더 + const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); + // 용량별 + const [fcltCpct, setFcltCpct] = useState<string>("0"); + // 시설명 + const [fcltyName, setFcltyName] = useState<string>(""); + // 시설상태 + const [operationValue, setOperationValue] = useState<string>("0"); + // 조회기간 + const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + + /** + * 페이지 함수 + */ + // 초기화 + const resetVal = () => { + setWidthSize("w-[120%]"); + + setUpheadList(upHeadList1) + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYear(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + searchRdiList(1, 10, selectSearchYear[0]?.val || "0"); + + } + + /** + * useEffect + */ + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchRdiList(1, 10, selectSearchYear[0]?.val || "0"); + } + }, [isInit, initializeCommonData, searchRdiList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: 'RDII추정', + sheetNameArray: ['RDII추정'], + headerRange: 'A1:O1' + }); + } + }, [excelList]); + + /** + * 콜백함수 + */ + // 조회 + const searchRdi = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchRdiList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchRdiList(pageNo, pageCnt, searchYear); + } + }, [operationValue, fcltyName, searchRdiList, searchYear, fcltCpct]); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + // 엑셀다운로드 + const excelDownload = useCallback(() => { + searchRdi(ref.current, 1, 10000); + }, [searchRdi]); + + // 페이지 변경 + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchRdi(ref.current, page, 10); + }, [searchRdi]); + + const mainList: Record<string, string | number | boolean>[] = rdiList || []; + const total = rdiCount || 0; + + return ( + <MainDiv> + <Title title="RDII추정" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput ref={searchInputRef} className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} isAdmin={false} /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={ undefined } headName={"RDII추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + <span className="text-2xl text-gray-500">※주) 처리장 유입부에서 측정된 유량 및 수질조사 자료를 기준으로 산정된 결과임</span> + </div> + </ListDiv> + </ContentsDiv> + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/sewageOccur/modal/page.tsx b/apps/fo/app/pages/sewageOccur/modal/page.tsx new file mode 100644 index 000000000..e8a90651e --- /dev/null +++ b/apps/fo/app/pages/sewageOccur/modal/page.tsx @@ -0,0 +1,161 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { SewageOccurDetailProps } from "@/app/store/sewageOccur"; +import { DateMonthPicker, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Table, TableCell, TableHead, TableRow } from "@common/components/ui"; +import { useEffect, useState } from "react"; + +export default function SewageOccurModal(props: { fcltyName: string; facilityCd : string; searchYear : string}) { + // 그리드 관련 + const {SewageOccurDetail} = useFoStore((state) => state); + + // searchYear 유효성 검사 및 기본값 설정 + const validSearchYear = props.searchYear && /^\d{4}$/.test(props.searchYear) ? props.searchYear : new Date().getFullYear().toString(); + + const [startYearMonth, setStartYearMonth] = useState(validSearchYear+'01'); + const [endYearMonth, setEndYearMonth] = useState(validSearchYear+'12'); + const [detailList, setDetailList] = useState<SewageOccurDetailProps[]>([]); + const { + upHeadListSewageOccurDetail, + gridListSewageOccurDetail + } = SewageOccurDetail; + + useEffect(() => { + setDetailList(gridListSewageOccurDetail); + }, [gridListSewageOccurDetail]); + + useEffect(() => { + const filteredList = gridListSewageOccurDetail.filter(item => item.msrDt.replace(/-/g, '') >= startYearMonth && item.msrDt.replace(/-/g, '') <= endYearMonth); + let maxWaterUsage = 0; + let maxSwageChangePercent = 0; + let maxEstimationSewage = 0; + + let minWaterUsage = filteredList[0]?.waterUsage || 0; + let minSwageChangePercent = filteredList[0]?.swageChangePercent || 0; + let minEstimationSewage = filteredList[0]?.estimationSewage || 0; + + let totalWaterUsage = 0; + let totalSwageChangePercent = 0; + let totalEstimationSewage = 0; + + let avgWaterUsage = 0; + let avgSwageChangePercent = 0; + let avgEstimationSewage = 0; + + filteredList.forEach(item => { + if (item.waterUsage > maxWaterUsage) { + maxWaterUsage = item.waterUsage; + } + if (item.swageChangePercent > maxSwageChangePercent) { + maxSwageChangePercent = item.swageChangePercent; + } + if (item.estimationSewage > maxEstimationSewage) { + maxEstimationSewage = item.estimationSewage; + } + + if (item.waterUsage < minWaterUsage) { + minWaterUsage = item.waterUsage; + } + if (item.swageChangePercent < minSwageChangePercent) { + minSwageChangePercent = item.swageChangePercent; + } + if (item.estimationSewage < minEstimationSewage) { + minEstimationSewage = item.estimationSewage; + } + + totalWaterUsage += item.waterUsage; + totalSwageChangePercent += item.swageChangePercent; + totalEstimationSewage += item.estimationSewage; + }); + + avgWaterUsage = totalWaterUsage?totalWaterUsage / filteredList.length:0; + avgSwageChangePercent = totalSwageChangePercent?totalSwageChangePercent / filteredList.length:0; + avgEstimationSewage = totalEstimationSewage?totalEstimationSewage / filteredList.length:0; + + const newData = [ + { + msrDt: '평균', + waterUsage: Math.round(avgWaterUsage * 100) / 100, + swageChangePercent: Math.round(avgSwageChangePercent * 100) / 100, + estimationSewage: Math.round(avgEstimationSewage * 100) / 100 + }, + { + msrDt: '최대', + waterUsage: maxWaterUsage, + swageChangePercent: maxSwageChangePercent, + estimationSewage: maxEstimationSewage + }, + { + msrDt: '최소', + waterUsage: minWaterUsage, + swageChangePercent: minSwageChangePercent, + estimationSewage: minEstimationSewage + }, + ...filteredList + ] + setDetailList(newData); + + + }, [startYearMonth, endYearMonth, gridListSewageOccurDetail]); + + return ( + <> + <ListDiv className="!h-[80px] !mt-10"> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <thead> + <TableRow className=""> + <TableHead className="w-1/6">{'시설명'}</TableHead> + <TableCell className="w-1/3">{props.fcltyName}</TableCell> + <TableHead className="w-1/6">{'년월'}</TableHead> + <TableCell className="w-1/3"> + <DateMonthPicker + field={{ + value: startYearMonth || (() => { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + return `${year}${month}`; + })(), + onChange: (value: string) => { + if (value && /^\d{6}$/.test(value)) { + setStartYearMonth(value); + } + } + }} + selectClass="ml-5 w-[100px]" + startYear={1900} + />~ + <DateMonthPicker + field={{ + value: endYearMonth || (() => { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + return `${year}${month}`; + })(), + onChange: (value: string) => { + if (value && /^\d{6}$/.test(value)) { + setEndYearMonth(value); + } + } + }} + selectClass="ml-5 w-[100px]" + startYear={1900} + /> + </TableCell> + </TableRow> + </thead> + </Table> + </div> + </ListDiv> + <ListDiv className="!h-[570px] overflow-y-auto"> + <div className="h-[120%]"> + <UiTable headName={"오수발생량 추정"} tableData={detailList} headlist={upHeadListSewageOccurDetail} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} boldArray={[0, 1, 2]}> + </UiTable> + </div> + </ListDiv> + </> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/sewageOccur/page.tsx b/apps/fo/app/pages/sewageOccur/page.tsx new file mode 100644 index 000000000..a137d9ebe --- /dev/null +++ b/apps/fo/app/pages/sewageOccur/page.tsx @@ -0,0 +1,237 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import SewageOccurModal from "./modal/page"; +import type { SewageOccurProps } from "@/app/store/sewageOccur"; + +export default function PlanSewage() { + const {Common + ,SewageOccur + ,SewageOccurSearch + ,initializeCommonData + ,searchSewageOccurList + } = useFoStore((state) => state) || { + Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" }, + SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] }, + SewageOccurSearch: { labelArray: [] }, + initializeCommonData: () => {}, + searchSewageOccurList: () => {} + }; + + const { + isInit, + selectUpdownData, + selectOperationData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectSearchYear, + btnClass + } = Common; + + const { + labelArray, + } = SewageOccurSearch; + + const { + upHeadList1, + upHeadList2, + upHeadList3, + sewageOccurList, + sewageOccurCount, + excelList + } = SewageOccur; + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + const sewageOccur = SewageOccur?.sewageOccurList || []; + + // 모달 상태 추가 + const [selectedRow, setSelectedRow] = useState<number | null>(null); + const [fcltyNm, setFcltyNm] = useState<string>(""); + const [fcltyCd, setFcltyCd] = useState<string>(""); + + // 그리드 관련 + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + // 초기화 변수 + const [resetPage, setResetPage] = useState<boolean>(false); + // 표 너비 + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + // 구분별 그리드 헤더 + const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); + // 용량별 + const [fcltCpct, setFcltCpct] = useState<string>("0"); + // 시설명 + const [fcltyName, setFcltyName] = useState<string>(""); + // 시설상태 + const [operationValue, setOperationValue] = useState<string>("0"); + // 조회기간 + const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + + /** + * 페이지 함수 + */ + // 초기화 + const resetVal = () => { + setWidthSize("w-[120%]"); + setUpheadList(upHeadList1) + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYear(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + } + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + }; + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + if (sewageOccur[index]) { + setFcltyNm(sewageOccur[index].bscFctNm); + setFcltyCd(sewageOccur[index].bscFctCd); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + }; + + /** + * useEffect + */ + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0"); + } + }, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '오수발생량 추정', + sheetNameArray: ['오수발생량 추정'], + headerRange: 'A1:O1' + }); + } + }, [excelList]); + + /** + * 콜백함수 + */ + // 조회 + const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchSewageOccurList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchSewageOccurList(pageNo, pageCnt, searchYear); + } + }, [fcltCpct, operationValue, fcltyName, searchSewageOccurList, searchYear]); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + // 엑셀다운로드 + const excelDownload = useCallback(() => { + searchFacilityPrecipitation(ref.current, 1, 10000); + }, [searchFacilityPrecipitation]); + + // 페이지 변경 + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchFacilityPrecipitation(ref.current, page, 10); + }, [searchFacilityPrecipitation]); + + const mainList: SewageOccurProps[] = sewageOccurList || []; + const total = sewageOccurCount || 0; + + return ( + <MainDiv> + <Title title="오수발생량 추정" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} + isAdmin={false} + /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"오수발생량 추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + </div> + </ListDiv> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<SewageOccurModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYear}></SewageOccurModal>} + bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]" + open={selectedRow !== null} + onOpenChange={(open) => !open && closeModal()} + noHeaderYn="Y" + noFooterYn="Y" + /> + )} + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/underWaterInput/modal/page.tsx b/apps/fo/app/pages/underWaterInput/modal/page.tsx new file mode 100644 index 000000000..9e49fb1bc --- /dev/null +++ b/apps/fo/app/pages/underWaterInput/modal/page.tsx @@ -0,0 +1,317 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { UnderWaterInputModalProps } from "@/app/store/underWaterInput"; +import { DateMonthPicker, UITab, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Table, TableCell, TableHead, TableRow, TabsContent } from "@common/components/ui"; +import { useEffect, useState } from "react"; + +type ModalProps ={ + fcltyName: string; + searchYear : string; +} + +const tabList1 = [ + { value: 'tab_term_year', label: '연간' }, + { value: 'tab_term_summer', label: '하절기' }, + { value: 'tab_term_winter', label: '동절기' }, +]; + +export default function UnderWaterInputModal({fcltyName='', searchYear=new Date().getFullYear().toString()} : ModalProps) { + // 그리드 관련 + const {UnderWaterDetail} = useFoStore((state) => state); + + const [startYmDate, setStartYmDate] = useState<string>(searchYear+'01'); + const [endYmDate, setEndYmDate] = useState<string>(searchYear+'12'); + const [tabTerm, setTabTerm] = useState<string>('tab_term_year'); + + // 이전 값들을 저장하기 위한 상태 + const [prevStartYmDate, setPrevStartYmDate] = useState<string>(searchYear+'01'); + const [prevEndYmDate, setPrevEndYmDate] = useState<string>(searchYear+'12'); + + const { + upHeadListUnderWaterDetail, + 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<UnderWaterInputModalProps[]>([]); + + useEffect(() => { + const filteredYearData = setFilterAfterData(gridListUnderWaterDetail); + setCopyYearList(filteredYearData as UnderWaterInputModalProps[]); + }, [gridListUnderWaterDetail]); + + useEffect(() => { + switch (tabTerm) { + case 'tab_term_summer': + setStartYmDate(searchYear+'06'); + setEndYmDate(searchYear+'08'); + setPrevStartYmDate(searchYear+'06'); + setPrevEndYmDate(searchYear+'08'); + break; + case 'tab_term_winter': + setStartYmDate(searchYear+'01'); + setEndYmDate(searchYear+'02'); + setPrevStartYmDate(searchYear+'01'); + setPrevEndYmDate(searchYear+'02'); + break; + default: + setStartYmDate(searchYear+'01'); + setEndYmDate(searchYear+'12'); + setPrevStartYmDate(searchYear+'01'); + setPrevEndYmDate(searchYear+'12'); + } + }, [searchYear, tabTerm]); + + useEffect(() => { + if(endYmDate < startYmDate) { + alert('종료일이 시작일보다 작습니다.') + // 이전 값으로 되돌리기 + setStartYmDate(prevStartYmDate); + setEndYmDate(prevEndYmDate); + return; + } + + // 유효한 경우 현재 값을 이전 값으로 저장 + 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]); + + return ( + <> + <UITab tabList={tabList1} defaultValue="tab_term_year" onValueChange={(val) => {setTabTerm(val)}}> + <TabsContent value="tab_term_year"> + <ListDiv className="!h-[100px]"> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <thead> + <TableRow> + <TableHead className="w-[120px]">{'시설명'}</TableHead> + <TableCell className="w-[300px]">{fcltyName}</TableCell> + <TableHead className="w-[120px]">{'년/월'}</TableHead> + <TableCell className=""> + <div className="flex"> + <DateMonthPicker + field={{ + value: startYmDate, + onChange: (value: string) => { + setStartYmDate(value); + } + }} + selectClass="ml-5 w-[100px]" + startYear={Number(searchYear)} // 시작 연도 설정 + /> + <h3 className="justify-center px-5">~</h3> + <DateMonthPicker + field={{ + value: endYmDate, + onChange: (value: string) => { + setEndYmDate(value); + } + }} + selectClass="ml-5 w-[100px]" + startYear={Number(searchYear)} // 시작 연도 설정 + /> + </div> + </TableCell> + </TableRow> + </thead> + </Table> + </div> + </ListDiv> + <div className="overflow-y-auto h-[500px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListUnderWaterDetail} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[200px]" + boldArray={[0,1,2]} + /> + </div> + </TabsContent> + + <TabsContent value="tab_term_summer"> + {/* <FacilityTermModalHeader<UnderWaterInputModalProps> + searchList={gridListUnderWaterDetail} + fcltyCd={facilityCd} + fcltyName={fcltyName} + tabName="tab_term_summer" + searchYear={searchYear} + ref={ref} + queryClick={filterData} + /> */} + <ListDiv className="!h-[100px]"> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <thead> + <TableRow> + <TableHead className="w-[120px]">{'시설명'}</TableHead> + <TableCell className="w-[300px]">{fcltyName}</TableCell> + <TableHead className="w-[120px]">{'년/월'}</TableHead> + <TableCell className=""> + <div className="flex"> + <DateMonthPicker + field={{ + value: startYmDate, + onChange: (value: string) => { + setStartYmDate(value); + } + }} + selectClass="ml-5 w-[100px]" + startYear={Number(searchYear)} // 시작 연도 설정 + /> + <h3 className="justify-center px-5">~</h3> + <DateMonthPicker + field={{ + value: endYmDate, + onChange: (value: string) => { + setEndYmDate(value); + } + }} + selectClass="ml-5 w-[100px]" + startYear={Number(searchYear)} // 시작 연도 설정 + /> + </div> + </TableCell> + </TableRow> + </thead> + </Table> + </div> + </ListDiv> + <div className="overflow-y-auto h-[500px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListUnderWaterDetail} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => { } } + className="h-[500px]" + boldArray={[0,1,2]} + /> + </div> + </TabsContent> + + <TabsContent value="tab_term_winter"> + <ListDiv className="!h-[100px]"> + <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> + <Table> + <thead> + <TableRow> + <TableHead className="w-[120px]">{'시설명'}</TableHead> + <TableCell className="w-[300px]">{fcltyName}</TableCell> + <TableHead className="w-[120px]">{'년/월'}</TableHead> + <TableCell className=""> + <div className="flex"> + <DateMonthPicker + field={{ + value: startYmDate, + onChange: (value: string) => { + setStartYmDate(value); + } + }} + selectClass="ml-5 w-[100px]" + startYear={Number(searchYear)} // 시작 연도 설정 + /> + <h3 className="justify-center px-5">~</h3> + <DateMonthPicker + field={{ + value: endYmDate, + onChange: (value: string) => { + setEndYmDate(value); + } + }} + selectClass="ml-5 w-[100px]" + startYear={Number(searchYear)} // 시작 연도 설정 + /> + </div> + </TableCell> + </TableRow> + </thead> + </Table> + </div> + </ListDiv> + <div className="overflow-y-auto h-[500px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListUnderWaterDetail} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => {}} + className="h-[500px]" + boldArray={[0,1,2]} + /> + </div> + </TabsContent> + </UITab> + </> + ); +} \ No newline at end of file diff --git a/apps/fo/app/pages/underWaterInput/page.tsx b/apps/fo/app/pages/underWaterInput/page.tsx new file mode 100644 index 000000000..1e821734b --- /dev/null +++ b/apps/fo/app/pages/underWaterInput/page.tsx @@ -0,0 +1,237 @@ +"use client" +import { useFoStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common"; +import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useCallback, useEffect, useRef, useState } from "react"; +import UnderWaterInputModal from "./modal/page"; +import type { UnderWaterProps } from "@/app/store/underWaterInput"; + +export default function UnderWaterInput() { + const {Common + ,UnderWater + ,UnderWaterSearch + ,initializeCommonData + ,searchUnderWaterList + } = useFoStore((state) => state) || { + Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" }, + UnderWater: { underWaterList: [], underWaterCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] }, + UnderWaterSearch: { labelArray: [] }, + initializeCommonData: () => {}, + searchUnderWaterList: () => {} + }; + + const { + isInit, + selectUpdownData, + selectOperationData, + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + selectSearchYear, + btnClass + } = Common; + + const { + labelArray, + } = UnderWaterSearch; + + const { + upHeadList1, + upHeadList2, + upHeadList3, + underWaterList, + underWaterCount, + excelList, + } = UnderWater; + + const ref = useRef<ReturnType>({ + partValue: '', + sidoValue: '', + sigunValue: '', + regionValue: '', + unitWaterShedValue: '', + middleRegionValue: '', + envPartValue: '', + sidoText: '', + sigunText: '' + }); + + const underWater = UnderWater?.underWaterList || []; + + // 모달 상태 추가 + const [selectedRow, setSelectedRow] = useState<number | null>(null); + const [fcltyNm, setFcltyNm] = useState<string>(""); + + // 그리드 관련 + const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); + + // 초기화 변수 + const [resetPage, setResetPage] = useState<boolean>(false); + // 표 너비 + const [widthSize, setWidthSize] = useState<string>("w-[120%]"); + // 구분별 그리드 헤더 + const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); + // 용량별 + const [fcltCpct, setFcltCpct] = useState<string>("0"); + // 시설명 + const [fcltyName, setFcltyName] = useState<string>(""); + // 시설상태 + const [operationValue, setOperationValue] = useState<string>("0"); + // 조회기간 + const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + + /** + * 페이지 함수 + */ + // 초기화 + const resetVal = () => { + setWidthSize("w-[120%]"); + setUpheadList(upHeadList1) + // 상태값 설정 + setFcltCpct(selectUpdownData[0]?.val || "0"); + setOperationValue(selectOperationData[0]?.val || "0"); + setSearchYear(selectSearchYear[0]?.val || "0"); + // SearchInputSelect 초기화 + setFcltyName(""); + if (searchInputRef.current) { + const input = searchInputRef.current.querySelector('input'); + if (input) { + input.value = ""; + } + } + searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); + } + + // 모달 닫기 + const closeModal = () => { + setSelectedRow(null); + }; + + // 셀 클릭 시 모달 표시 + const cellClick = (index: number) => { + if (underWater[index]) { + setFcltyNm(underWater[index].bscFctNm); + setSelectedRow(index); // 클릭한 행의 index 저장 + } + }; + + /** + * useEffect + */ + useEffect(() => { + if (!isInit) { + initializeCommonData(); + searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); + } + }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]); + + useEffect(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '지하수유입량 추정', + sheetNameArray: ['지하수유입량 추정'], + headerRange: 'A1:V1' + }); + } + }, [excelList]); + + /** + * 콜백함수 + */ + // 조회 + const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + let nameCd, partStr + switch (partValue) { + case 'R': + if (regionValue !== '0') { + if(unitWaterShedValue==='0') nameCd = regionValue; + else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + else nameCd = middleRegionValue; + } + partStr = partValue + break; + default: + if(sidoValue !== '0') nameCd = sidoValue; + if(sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue:partValue; + break; + } + if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { + searchUnderWaterList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + } else { + searchUnderWaterList(pageNo, pageCnt, searchYear); + } + }, [fcltCpct, operationValue, fcltyName, searchUnderWaterList, searchYear]); + + const chkVal = useCallback((returntype:ReturnType) => { + setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setResetPage(true); + }, [upHeadList1, upHeadList2, upHeadList3]); + + // 엑셀다운로드 + const excelDownload = useCallback(() => { + searchFacilityPrecipitation(ref.current, 1, 10000); + }, [searchFacilityPrecipitation]); + + // 페이지 변경 + const handlePageChange = useCallback((page: number) => { + setResetPage(false); + searchFacilityPrecipitation(ref.current, page, 10); + }, [searchFacilityPrecipitation]); + + const mainList: UnderWaterProps[] = underWaterList || []; + const total = underWaterCount || 0; + + return ( + <MainDiv> + <Title title="지하수유입량 추정" /> + <ContentsDiv> + <SearchBaseForm + leftChildren={<> + <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> + <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + </>} + rightChildren={undefined} + selectSidoData={selectSidoData} + selectSigunData={selectSigunData} + selectRegionData={selectRegionData} + selectUnitWaterShedData={selectUnitWaterShedData} + selectMiddleRegionData={selectMiddleRegionData} + selectEnvPartData={selectEnvPartData} + btnClass={btnClass} + queryClick={chkVal} + excelClick={excelDownload} + initClick={resetVal} + ref={ref} + isAdmin={false} + /> + <ListDiv> + <div className="h-full w-full overflow-x-scroll"> + <div className={widthSize}> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"지하수유입량 추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} /> + </div> + </div> + <span className="text-2xl text-gray-500">※지하수 유입량 = 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율)</span> + </ListDiv> + </ContentsDiv> + {selectedRow !== null && ( + <UiModal + body={<UnderWaterInputModal fcltyName={fcltyNm} searchYear={searchYear}></UnderWaterInputModal>} + bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]" + open={selectedRow !== null} + onOpenChange={(open) => !open && closeModal()} + noHeaderYn="Y" + noFooterYn="Y" + /> + )} + </MainDiv> + ); +} \ No newline at end of file diff --git a/apps/fo/app/store/common/index.tsx b/apps/fo/app/store/common/index.tsx new file mode 100644 index 000000000..df1126195 --- /dev/null +++ b/apps/fo/app/store/common/index.tsx @@ -0,0 +1,375 @@ +/**==================================== + * 보편적으로 쓰는 store 공간 + ====================================**/ + +import { getFetch } from "@/app/components/api"; + +/** + * STORE - TYPE + */ + +// selectBox +export type SelectDataType = { + text: string; + val: string; +} +// grid header +export type TableUpperProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; +} + +// 시도 데이터 +export type sidoDataType = { + adstDstrCd: number, + sido: string, +} + +// 시군구 데이터 +export type sigunDataType = { + adstDstrCd: number, + gugun: string +} + +// 권역 데이터 +export type regionDataType = { + rvbsnCd: string, + rvbsn: string, +} + +// 단위유역 데이터 +export type unitWaterShedDataType = { + rvbsnCd: string, + lgbsn: string, +} + +// 중권역 데이터 +export type middleRegionDataType = { + rvbsnCd: string, + mdmbsn: string, +} + +// 환경청 데이터 +export type envPartDataType = { + lclgvrnmntCd: string, + lclgvrnmntNm: string, + adstDliStrCd: string, + adstDstrCd: string, +} + +export type envPartSelectDataType = SelectDataType & { + adstDliStrCd: string, +} + +/** + * STORE - DATA + */ + +// 시도 데이터 +let selectSidoData: SelectDataType[] = [ + { text: "전체", val: '0' } +]; + +// 시군구 데이터 +let selectSigunData: SelectDataType[] = [ + { text: "전체", val: '0' } +]; + +// 권역 데이터 +const selectRegionData: SelectDataType[] = [ + { text: "전체", val: '0' } +]; + +// 단위유역 데이터 +const selectUnitWaterShedData: SelectDataType[] = [ +]; + +// 환경청 데이터 +let selectEnvPartData: envPartSelectDataType[] = [ + { text: "전체", val: '0', adstDliStrCd: '' } +]; + +// 환경청 내 시도 데이터 +const selectEnvPartSidoData: SelectDataType[] = [ + { text: "전체", val: '0' } +]; + +// 환경청 내 시군구 데이터 +const selectEnvPartSigunData: SelectDataType[] = [ + { text: "전체", val: '0' } +]; + +// 구분 데이터 +// const selectPartData:SelectDataType[] = [ +// { text: "행정별", val: '0' }, +// { text: "유역별", val: '01' }, +// { text: "환경청별", val: '02' }, +// ]; + +// 상향 하향 데이터 +const selectUpdownData:SelectDataType[] = [ + { text: "전체", val: '0' }, + { text: "500이하", val: '400' }, + { text: "500이상", val: '500' }, +]; + +// 운영 상태 데이터 +const selectOperationData:SelectDataType[] = [ + { text: "전체", val: '0' }, + { text: "가동중", val: '1' }, + { text: "재가동", val: '2' }, + { text: "가동중지", val: '3' }, + { text: "시설폐쇄", val: '4' }, +]; + +// 시설 구분 데이터 +const selectFacilityPartData:SelectDataType[] = [ + { text: "전체", val: '0' }, + { text: "공공하수처리시설", val: 'SW' }, + { text: "오수처리장", val: 'SS' }, + { text: "개인하수처리시설", val: 'PB' }, + { text: "분뇨처리장", val: 'NS' }, + { text: "폐수처리장", val: 'LW' }, + { text: "침출수처리장", val: 'LC' }, + { text: "기타기초시설", val: 'ET' }, +]; + +// 년도 데이터 +const getYearData = () => { + const year = new Date().getFullYear(); + const year1 = year - 1; + const year2 = year - 2; + const year3 = year - 3; + const year4 = year - 4; + const year5 = year - 5; + return [ + { text: year1 + "년", val: year1.toString() }, + { text: year2 + "년", val: year2.toString() }, + { text: year3 + "년", val: year3.toString() }, + { text: year4 + "년", val: year4.toString() }, + { text: year5 + "년", val: year5.toString() }, + ] +} + +const selectSearchYear:SelectDataType[] = getYearData(); + +// 유량 기간 구분 +const selectFlowTerm : SelectDataType[] = [ + { text: "연간", val: '0' }, + { text: "하절기", val: '01' }, + { text: "동절기", val: '02' }, + { text: "청천시", val: '03' }, + { text: "강우시", val: '04' }, +]; + +// 유량 기간 구분 +const selectFlowPanel : SelectDataType[] = [ + { text: "전체", val: '0'}, + { text: "3mm미만", val: '1'}, + { text: "3~10mm미만", val: '2'}, + { text: "10~20mm미만", val: '3'}, + { text: "20~30mm미만", val: '4'}, + { text: "30~40mm미만", val: '5'}, + { text: "40~50mm미만", val: '6'}, + { text: "50~60mm미만", val: '7'}, + { text: "60mm이상", val: '8'}, +]; + +// 중권역 데이터 +const selectMiddleRegionData: SelectDataType[] = [ + { text: "전체", val: '0' } +]; + +/** + * STORE - API + */ + +// 시도 API 호출 함수 추가 +const fetchSidoData = async (): Promise<SelectDataType[]> => { + try { + const res = await getFetch('fo', '/addr/city'); + const data = res.data; + selectSidoData = [ + { text: "전체", val: '0'}, + ...data.map((item: sidoDataType) => ({ + text: item.sido || '', + val: String(item.adstDstrCd) || '' + })) + ]; + return selectSidoData; + } catch (error) { + console.error('시도 데이터를 가져오는데 실패했습니다:', error); + return selectSidoData; + } +}; + +// 시군구 API 호출 함수 추가 +const fetchSigunData = async (): Promise<SelectDataType[]> => { + try { + const res = await getFetch('fo', '/addr/sgg'); + const data = res.data; + selectSigunData = [ + { text: "전체", val: '0' }, + ...data + .map((item: sigunDataType) => ({ + text: item.gugun || '', + val: String(item.adstDstrCd) || '' + })) + ]; + return selectSigunData; + } catch (error) { + console.error('시군구 데이터를 가져오는데 실패했습니다:', error); + return selectSigunData; + } +}; + +// 권역 API 호출 함수 추가 +const fetchRegionData = async (): Promise<SelectDataType[]> => { + try { + const res = await getFetch('fo', '/addr/sarea'); + const data = res.data; + return [ + { text: "전체", val: '0'}, + ...data.map((item: regionDataType) => ({ + text: item.rvbsn || '', + val: String(item.rvbsnCd) || '' + })) + ]; + } catch (error) { + console.error('권역 데이터를 가져오는데 실패했습니다:', error); + return selectRegionData; + } +}; + +// 중권역 API 호출 함수 추가 +const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => { + try { + const res = await getFetch('fo', '/addr/midArea'); + const data = res.data; + return [ + { text: "전체", val: '0' }, + ...data + .map((item: middleRegionDataType) => ({ + text: item.mdmbsn || '', + val: String(item.rvbsnCd) || '' + })) + ]; + } catch (error) { + console.error('중권역 데이터를 가져오는데 실패했습니다:', error); + return selectMiddleRegionData; + } +}; + +// 단위유역 API 호출 함수 추가 +const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => { + try { + const res = await getFetch('fo', '/addr/largeArea'); + const data = res.data; + return [ + { text: "전체", val: '0' }, + ...data + .map((item: unitWaterShedDataType) => ({ + text: item.lgbsn || '', + val: String(item.rvbsnCd) || '' + })) + ]; + } catch (error) { + console.error('단위유역 데이터를 가져오는데 실패했습니다:', error); + return selectUnitWaterShedData; + } +}; + +// 환경청 데이터 +const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => { + try { + const res = await getFetch('fo', '/addr/govArea'); + const data = res.data; + selectEnvPartData = [ + { text: "전체", val: '0', adstDliStrCd: '' }, + ...data + .map((item: envPartDataType) => ({ + text: item.lclgvrnmntNm || '', + val: String(item.lclgvrnmntCd) || '', + adstDliStrCd: item.adstDliStrCd || '' + })) + ]; + return selectEnvPartData; + } catch (error) { + console.error('환경청 데이터를 가져오는데 실패했습니다:', error); + return selectEnvPartData; + } +}; + +/**==================================== + * Store - expaort + ====================================*/ +export type CommonAction = { + initializeCommonData: () => Promise<void>; +} + +export type CommonType = { + Common: { + isInit: boolean; + selectSidoData: SelectDataType[]; + selectSigunData: SelectDataType[]; + selectUpdownData: SelectDataType[]; + selectOperationData: SelectDataType[]; + selectFacilityPartData: SelectDataType[]; + selectSearchYear: SelectDataType[]; + selectUnitWaterShedData: SelectDataType[]; + selectRegionData: SelectDataType[]; + selectMiddleRegionData: SelectDataType[]; + selectEnvPartSidoData: SelectDataType[]; + selectEnvPartSigunData: SelectDataType[]; + selectFlowTerm: SelectDataType[]; + selectFlowPanel: SelectDataType[]; + selectEnvPartData: envPartSelectDataType[]; + btnClass: string; + } +} + +export const commonState:CommonType = { + Common: { + isInit: false, + selectSidoData: selectSidoData, + selectSigunData: selectSigunData, + selectUpdownData: selectUpdownData, + selectOperationData: selectOperationData, + selectFacilityPartData: selectFacilityPartData, + selectSearchYear: selectSearchYear, + selectRegionData: selectRegionData, + selectUnitWaterShedData: selectUnitWaterShedData, + selectMiddleRegionData: selectMiddleRegionData, + selectEnvPartSidoData : selectEnvPartSidoData, + selectEnvPartSigunData : selectEnvPartSigunData, + selectFlowTerm: selectFlowTerm, + selectFlowPanel: selectFlowPanel, + selectEnvPartData : selectEnvPartData, + btnClass: "p-4 m-2 text-white rounded-sm text-lg font-medium" + } +} + +export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)=>CommonAction=(set: (fn: (state: CommonType) => CommonType) => void) => { + return { + // 시도 변경 이벤트 + initializeCommonData: async () => { + const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]); + const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet; + set((state: CommonType) => ({ + Common: { + ...state.Common, + selectSidoData: sidoData, + selectSigunData: sggData, + selectRegionData: regionData, + selectMiddleRegionData: midData, + selectUnitWaterShedData: unitData, + selectEnvPartData: envData + } + })) + }, + } +} + +export type CommonStore = CommonType & CommonAction; diff --git a/apps/fo/app/store/dashboard/index.tsx b/apps/fo/app/store/dashboard/index.tsx new file mode 100644 index 000000000..8b04da6ff --- /dev/null +++ b/apps/fo/app/store/dashboard/index.tsx @@ -0,0 +1,1866 @@ +/** + * Store - 타입 + */ + +import { getFetch, getFetchAreaList } from "@/app/components/api"; +import DataForm from "@/app/components/common/DataForm"; +import type { FacilityWaterQltyProps } from "../facilityWaterQlty"; + +// selectBox +export type SelectDataType = { + text: string; + val: string; +} + +// grid header +export type TableUpperProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; + align? : string; + row? : number; + rowSpan?: number; + colSpan?: number; +} + +// ===TAB1 +// main fclty +export type mainFcltyProps = { + fcltyNm : string, + facilityCapacity : string, + publicMethod : string, + location : string +} + +// 시설계획 +export type planFcltyListProps = { + item : string, + provisionalApproval : string | number | '', + now : string | number | '', + stepOne : string | number | '', + stepTwo : string | number | '', + stepThree : string | number | '', + stepFour : string | number | '', +} + +// 운영실태 +export type OperationalStatusListProps = { + flowRate : string, + waterQlty : string, + yearAvg : number | string | '', + yearMax : number | string | '', + yearMin : number | string | '', + summerAll : number | string | '', + summerThree : number | string | '', + summerRain : number | string | '', + winterAll : number | string | '', + winterSunnyAvg : number | string | '', + winterSunnyMax : number | string | '', + winterSunnyMin : number | string | '', + groundWaterAvg : number | string | '', + groundWaterSummer : number | string | '', + groundWaterWinter : number | string | '', + rdi : number | string | '', + rdiRain : number | string | '', +} + +export interface operationalStatusListProps { + flowRate : string, + waterQlty : string, + yearAvg : number | '', + yearMax : number | '', + yearMin : number | '', + summerAll : number | '', + summerThree : number | '', + summerRain : number | '', + winterAll : number | '', + winterSunnyAvg : number | '', + winterSunnyMax : number | '', + winterSunnyMin : number | '', + groundWaterAvg : number | string | '', + groundWaterSummer : number | string | '', + groundWaterWinter : number | string | '', + rdi : number | '', +} + +// 차트 +export type lineBarChartDataProps = { + label : string, + lineVal : number + barVal : number + barVal0 : number + barVal1 : number + lineVal0 : number + lineVal1 : number +} + +export type lineChartDataProps = { + label : string, + val : number +} + +export interface RainManageListProps { + rainItem : string, + rainRate : string, + rainDay : number, + bscFctCd?: string, + bscFctNm?: string, + msrDt?: string + sidoNm?: string, + sggNm?: string, + addr?: string, + fctCpc?: number, + inflAssctAftWqltBod: number | '', + inflAssctAftWqltToc: number | '', + inflAssctAftWqltSs: number | '', + inflAssctAftWqltTn: number | '', + inflAssctAftWqltTp: number | '', + efflWqltBod: number | '', + efflWqltCod: number | '', + efflWqltSs: number | '', + efflWqltTn: number | '', + efflWqltTp: number | '', + rgnNm?:string|'', + rvbsn?:string|'', + lgbsn?:string|'', + mdmbsn?:string|'', + inflAmnt: number | '', + lclgvrnmntNm?: string, + infoRnDay?: number | '', + tpInflWqltTmprtr?: number | '', + tpInflWqltPh?: number | '', + tpInflWqltBod?: number | '', + tpInflWqltCod?: number | '', + tpInflWqltSs?: number | '', + tpInflWqltTn?: number | '', + tpInflWqltTp?: number | '', + tpInflWqltEclgyTox?: number | '', + tpInflWqltClfgrpcnt?: number | '', + dsInflWqltBod: number | '', + dsInflWqltCod: number | '', + dsInflWqltSs: number | '', + dsInflWqltTn: number | '', + dsInflWqltTp: number | '', + extnsSeqno?: number | '', +} + +export interface RainManageExcelType { + 강우량: string, + 강우조건비율: number | '', + 일수: number | '', + 유입유량: number | '', + 시설용량대비: number | '', + 설계유입수질BOD: number | '', + 설계유입수질TOC: number | '', + 설계유입수질SS: number | '', + 설계유입수질TN: string | number | '', + 설계유입수질TP: string | number | '', + 유입수질BOD: number | '', + 유입수질TOC: number | '', + 유입수질SS: number | '', + 유입수질TN: string | number | '', + 유입수질TP: string | number | '', + 방류수질BOD: number | '', + 방류수질TOC: number | '', + 방류수질SS: number | '', + 방류수질TN: string | number | '', + 방류수질TP: string | number | '', +} + +export interface DrainageEquipmentListProps { + part1 : string | '', + part2 : string | '', + nowSum : number | '', + nowJoin : number | '', + nowSep : number | '', + stepOneSum : number | '', + stepOneJoin : number | '', + stepOneSep : number | '', + stepTwoSum : number | '', + stepTwoJoin : number | '', + stepTwoSep : number | '', + stepThreeSum : number | '', + stepThreeJoin : number | '', + stepThreeSep : number | '', + stepFourSum : number | '', + stepFourJoin : number | '', + stepFourSep : number | '', + etc : string | '', +} + +export interface DrainageEquipmentExcelType { + partOne : string | '', + partTwo : string | '', + nowSum : number | '', + nowJoin : number | '', + nowSep : number | '', + stepOneSum : number | '', + stepOneJoin : number | '', + stepOneSep : number | '', + stepTwoSum : number | '', + stepTwoJoin : number | '', + stepTwoSep : number | '', + stepThreeSum : number | '', + stepThreeJoin : number | '', + stepThreeSep : number | '', + stepFourSum : number | '', + stepFourJoin : number | '', + stepFourSep : number | '', +} + +/** + * Store - data + */ +// Main +const DashBoardTopLabelArray:string[] = ['구분', '시도', '시군구', '년도', '시설명']; + +// === Tab1 +// grid header +const upHeadListFclty:TableUpperProps[] = [ + { id : "fcltyNm", title : "시설명"}, + { id : "facilityCapacity", title : "시설용량(m³/일)"}, + { id : "publicMethod", title : "공법"}, + { id : "location", title : "지역구"}, +] + +const fcltyList : mainFcltyProps[] = [ +] + +let planYearList : string[] = [ + "2021", + "2022", + "2023", + "2024", + "2025", + "2026" +] + +const upHeadListPlanFclty : TableUpperProps[] = [ + { id: "item", title: "", upName:"upChangeRe", upSequnce:1 }, + { id: "provisionalApproval", title: "기승인\n4단계", upName:"upChangeRe", upSequnce:2 }, + { id: "now", title: "현재", upName:"upChangeRe", upSequnce:3 }, + { id: "stepOne", title: "1단계", upName:"upChangeRe", upSequnce:4 }, + { id: "stepTwo", title: "2단계", upName:"upChangeRe", upSequnce:5 }, + { id: "stepThree", title: "3단계", upName:"upChangeRe", upSequnce:6 }, + { id: "stepFour", title: "4단계", upName:"upChangeRe", upSequnce:7 }, +] + +const planFcltyList:planFcltyListProps[] = [ + { + item : "1.증설계획\n(㎥/일)", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "2.처리인구(인)", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "3.처리구역\n(km²)", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "4.분류식화율\n(%)", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "5.물사용량", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "년도\n(최근 3개년)", + provisionalApproval: "물사용량\n(㎥/일)", + now: "", + stepOne: "", + stepTwo: "비고", + stepThree: "", + stepFour : "", + }, + { + item : "2021", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "2022", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, + { + item : "2023", + provisionalApproval: "", + now: "", + stepOne: "", + stepTwo: "", + stepThree: "", + stepFour : "", + }, +] + +const upHeadListOperationStatus : TableUpperProps[] = [ + { id: "flowRate", title: "유량 및 수질", upName:"upChangeRe1", upSequnce:1 }, + { id: "waterQlty", title: "유량 및 수질", upName:"upChangeRe1", upSequnce:2 }, + { id: "yearAvg", title: "평균", upName:"upChangeRe2", upSequnce:1 }, + { id: "yearMax", title: "최대", upName:"upChangeRe2", upSequnce:2 }, + { id: "yearMin", title: "최소", upName:"upChangeRe2", upSequnce:3 }, + { id: "summerAll", title: "전기간", upName:"upChangeRe3", upSequnce:1 }, + { id: "summerThree", title: "강우시\n(3mm이상)", upName:"upChangeRe3", upSequnce:2 }, + { id: "summerRain", title: "청천시", upName:"upChangeRe3", upSequnce:3 }, + { id: "winterAll", title: "전기간", upName:"upChangeRe4", upSequnce:1 }, + { id: "winterSunnyAvg", title: "청천시\n평균", upName:"upChangeRe4", upSequnce:2 }, + { id: "winterSunnyMax", title: "청천시\n최대", upName:"upChangeRe4", upSequnce:3 }, + { id: "winterSunnyMin", title: "청천시\n최소", upName:"upChangeRe4", upSequnce:4 }, + { id: "groundWaterAvg", title: "연평균", upName:"upChangeRe5", upSequnce:1 }, + { id: "groundWaterSummer", title: "하절기\n평균", upName:"upChangeRe5", upSequnce:2 }, + { id: "groundWaterWinter", title: "동절기\n평균", upName:"upChangeRe5", upSequnce:3 }, + { id: "rdi", title: "RDII 추정" }, +] + +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: "A", title: "유입(㎥/일)", row:0, colSpan:2 }, + { id: "inflAmnt", title: "유입유량", row:1 }, + { id: "fctCpc", title: "시설용량대비", row:1 }, + { 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: "C", title: "유입수질(㎎/L)", row:0, colSpan:5 }, + { id: "inflAssctAftWqltBod", title: "BOD", row:1 }, + { id: "inflAssctAftWqltToc", title: "TOC\n(COD)", row:1 }, + { id: "inflAssctAftWqltSs", title: "SS", row:1 }, + { id: "inflAssctAftWqltTn", title: "T-N", row:1 }, + { id: "inflAssctAftWqltTp", title: "T-P", row:1 }, + { id: "D", title: "방류수질(㎎/L)", row:0, colSpan:5 }, + { id: "efflWqltBod", title: "BOD", row:1 }, + { id: "efflWqltCod", 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 }, +] + +const upHeadListDrainageEquipment : TableUpperProps[] = [ + { id: "part1", title: "구분", row:0, upName:"upChangeRe1", upSequnce:1 }, + { id: "part2", title: "구분", row:0, upName:"upChangeRe1", upSequnce:2 }, + { id: "nowSum", title: "계", row:2, upName:"upChangeRe2", upSequnce:1 }, + { id: "nowJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe2", upSequnce:2 }, + { id: "nowSep", title: "분리식\n가구수", row:2, upName:"upChangeRe2", upSequnce:3 }, + { id: "stepOneSum", title: "계", row:2, upName:"upChangeRe3", upSequnce:1 }, + { id: "stepOneJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe3", upSequnce:2 }, + { id: "stepOneSep", title: "분리식\n가구수", row:2, upName:"upChangeRe3", upSequnce:3 }, + { id: "stepTwoSum", title: "계", row:2, upName:"upChangeRe4", upSequnce:1 }, + { id: "stepTwoJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe4", upSequnce:2 }, + { id: "stepTwoSep", title: "분리식\n가구수", row:2, upName:"upChangeRe4", upSequnce:3 }, + { id: "stepThreeSum", title: "계", row:2, upName:"upChangeRe5", upSequnce:1 }, + { id: "stepThreeJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe5", upSequnce:2 }, + { id: "stepThreeSep", title: "분리식\n가구수", row:2, upName:"upChangeRe5", upSequnce:3 }, + { id: "stepFourSum", title: "계", row:2, upName:"upChangeRe6", upSequnce:1 }, + { id: "stepFourJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe6", upSequnce:2 }, + { id: "stepFourSep", title: "분리식\n가구수", row:2, upName:"upChangeRe6", upSequnce:3 }, + { id: "etc", title: "비고", row:0, upName:"upChangeRe7", upSequnce:1 }, +] + +const operationalStatusList : OperationalStatusListProps[] = [ + { + flowRate : '유량\n(㎥/일)', + waterQlty : '유입', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '유량\n(㎥/일)', + waterQlty : '순수하수', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '유량\n(㎥/일)', + waterQlty : '연계처리수', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '유량\n(㎥/일)', + waterQlty : '총인설비\n반류수', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '유량\n(㎥/일)', + waterQlty : '방류', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '수질\n(mg/L)', + waterQlty : '유입\n(BOD)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '수질\n(mg/L)', + waterQlty : '방류\n(BOD)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '수질\n(mg/L)', + waterQlty : '유입\n(T-N)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '수질\n(mg/L)', + waterQlty : '방류\n(T-N)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '수질\n(mg/L)', + waterQlty : '유입\n(T-P)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '수질\n(mg/L)', + waterQlty : '방류\n(T-P)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '총인\n시설\n수질\n(mg/L)', + waterQlty : '총인설비\n유입(T-P)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, + { + flowRate : '총인\n시설\n수질\n(mg/L)', + waterQlty : '총인설비\n방류(T-P)', + yearAvg : '', + yearMax : '', + yearMin : '', + summerAll : '', + summerThree : '', + summerRain : '', + winterAll : '', + winterSunnyAvg : '', + winterSunnyMax : '', + winterSunnyMin : '', + groundWaterAvg : '', + groundWaterSummer : '', + groundWaterWinter : '', + rdi : '', + rdiRain : '', + }, +] + +const chart_inflowRain : lineBarChartDataProps[] = []; +const chart_bodRain : lineBarChartDataProps[] = []; +const chart_inflTp : lineChartDataProps[] = []; +const chart_chongInTp : lineChartDataProps[] = []; +const chart_efflTp : lineChartDataProps[] = []; +const chart_inflTn : lineChartDataProps[] = []; +const chart_efflTn : lineChartDataProps[] = []; +const chart_inflBod : lineChartDataProps[] = []; +const chart_efflBod : lineChartDataProps[] = []; + +const rainManageList : RainManageListProps[] = []; + +const drainageEquipmentList : DrainageEquipmentListProps[] = [ + { + part1 : '청천시청', + part2 : '청천시청', + nowSum : 101, + nowJoin : 102, + nowSep : 103, + stepOneSum : 104, + stepOneJoin : 105, + stepOneSep : 106, + stepTwoSum : 107, + stepTwoJoin : 108, + stepTwoSep : 109, + stepThreeSum : 110, + stepThreeJoin : 111, + stepThreeSep : 112, + stepFourSum : 113, + stepFourJoin : 114, + stepFourSep : 115, + etc : '1050', + } +]; + +/**==================================== +* Store - api (데이터 호출) +====================================*/ +/* 시설정보 및 운영현황 - 계획 */ +const fetchFacilityPlan = async (targetYear : string | undefined, fcltyCd : string | undefined) => { + try { + targetYear = targetYear ? targetYear : "2024"; + const baseUrl = 'https://apigateway.rsonesoft.com/dc-service/api/v1/nowDmgMtncGnsvy'; + const queryParams = new URLSearchParams(); + + if (fcltyCd) queryParams.append('swtrmtFcltCd', fcltyCd); + if (targetYear) queryParams.append('targetYear',targetYear); + const queryString = queryParams.toString(); + const url = queryString ? `${baseUrl}?${queryString}` : baseUrl; + const res = await getFetchAreaList(url); + return res + } catch (error) { + console.error('시설계획정보를 가져오는데 실패했습니다:', error); + return {data: [], count: 0}; + } +}; + +/* 시설정보 및 운영현황 - 운영실태 */ +const fetchOperationStatus = async (searchYear: string|undefined, fcltyCd: string|undefined) => { + try{ + const url = '/dashBoard/mngInfo/' + (searchYear?searchYear:'2024') + '/' + (fcltyCd?fcltyCd:''); + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } + } catch (error) { + console.error('운영실태 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } +} + +// 주요그래프 데이터 +const fetchMainGraph = async (searchYear: string|undefined, fcltyCd: string|undefined) => { + try { + const url = '/fclt/qlty/hist/' + (searchYear?searchYear:'2024') + '/' + (fcltyCd?fcltyCd:'11680PB001R'); + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } + } catch (error) { + console.error('주요그래프 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } +} + +// 배수설비 정비현황 +const fetchDrainageEquipment = async (searchYear: string|undefined, fcltyCd: string|undefined) => { + try{ + /* const url = '/fclt/qlty/hist/' + (searchYear?searchYear:'2024') + '/' + (fcltyCd?fcltyCd:'11680PB001R'); + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } */ + console.log(searchYear, fcltyCd); + return { data : drainageEquipmentList, count : drainageEquipmentList.length } + } catch (error) { + console.error('배수설비 정비현황 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } +} + +/**==================================== +* Store - action +====================================*/ +export type DashBoardActions = { + initializeDashBoardData(): Promise<void>; + /** 시설정보 및 운영현황 - 계획정보 */ + searchPlanInfo(targetYear: string, fcltyCd: string): Promise<void>; + /** 시설정보 및 운영현황 - 운영실태 */ + searchOperationStatus(isExcel: boolean, searchYear: string, fcltyCd: string): Promise<void>; + /** 주요그래프 데이터 */ + searchMainGraph(searchYear: string, fcltyCd: string): Promise<void>; + /**강우일 운영현황 */ + searchRainManage(searchYear: string, fcltyCd: string, isExcel: boolean): Promise<void>; + /**배수설비 정비현황 */ + searchDrainageEquipment(searchYear: string, fcltyCd: string, isExcel: boolean): Promise<void>; +} + +export type DashBoardType = { + DashboardMain: { + isInit: boolean; + topLabelArray:string[]; + }, + + FcltyMain : { + isInit : boolean; + upHeadListFclty : TableUpperProps[]; + fcltyList : mainFcltyProps[]; + }, + + PlanFclty : { + isInit : boolean; + planYearList : string[]; + upHeadListPlanFclty : TableUpperProps[]; + planFcltyList : planFcltyListProps[]; + }, + + OperationStatus : { + isInit : boolean; + upHeadListOperationStatus : TableUpperProps[]; + operationalStatusList : OperationalStatusListProps[]; + } + + MainGraph : { + isInit : boolean; + chart_inflowRain : lineBarChartDataProps[]; + chart_bodRain : lineBarChartDataProps[]; + chart_inflTp : lineChartDataProps[]; + chart_chongInTp : lineChartDataProps[]; + chart_efflTp : lineChartDataProps[]; + chart_inflTn : lineChartDataProps[]; + chart_efflTn : lineChartDataProps[]; + chart_inflBod : lineChartDataProps[]; + chart_efflBod : lineChartDataProps[]; + }, + + RainManage : { + isInit : boolean; + upHeadListRainManage : TableUpperProps[]; + rainManageList : RainManageListProps[]; + excelList : RainManageExcelType[]; + }, + + DrainageEquipment : { + isInit : boolean; + upHeadListDrainageEquipment : TableUpperProps[]; + drainageEquipmentList : DrainageEquipmentListProps[]; + drainageEquipmentExcelList : DrainageEquipmentExcelType[]; + } +} + +export type DashBoardStore = DashBoardType & DashBoardActions; + +export const dashBoardInitState:DashBoardType = { + DashboardMain: { + isInit: false, + topLabelArray: DashBoardTopLabelArray, + }, + + FcltyMain : { + isInit : false, + upHeadListFclty : upHeadListFclty, + fcltyList : fcltyList, + }, + + PlanFclty : { + isInit : true, + planYearList : planYearList, + upHeadListPlanFclty : upHeadListPlanFclty, + planFcltyList : planFcltyList + }, + + OperationStatus : { + isInit : false, + upHeadListOperationStatus : upHeadListOperationStatus, + operationalStatusList : operationalStatusList + }, + + MainGraph : { + isInit : false, + chart_inflowRain : chart_inflowRain, + chart_bodRain : chart_bodRain, + chart_inflTp : chart_inflTp, + chart_chongInTp : chart_chongInTp, + chart_efflTp : chart_efflTp, + chart_inflTn : chart_inflTn, + chart_efflTn : chart_efflTn, + chart_inflBod : chart_inflBod, + chart_efflBod : chart_efflBod + }, + + RainManage : { + isInit : false, + upHeadListRainManage : upHeadListRainManage, + rainManageList : rainManageList, + excelList : [] + }, + + DrainageEquipment : { + isInit : false, + upHeadListDrainageEquipment : upHeadListDrainageEquipment, + drainageEquipmentList : drainageEquipmentList, + drainageEquipmentExcelList : [] + } +} + +/** + * 데이터 폼 포맷설정 + */ +export const dataFormSettingPlan=(item : planFcltyListProps) => { + DataForm({ type: 'float2', column: 'provisionalApproval', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float2', column: 'now', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'stepOne', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'stepTwo', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'stepThree', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'stepFour', item: item as unknown as Record<string, unknown>}); +} + +export const dataFormSetting=(item : OperationalStatusListProps)=> { + DataForm({ type: 'float2', column: 'yearAvg', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float2', column: 'yearMax', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'yearMin', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'summerAll', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'summerThree', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'summerRain', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'winterAll', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'winterSunnyAvg', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'winterSunnyMax', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'winterSunnyMin', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'groundWaterAvg', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'groundWaterSummer', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'groundWaterWinter', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'rainRate', item: item as unknown as Record<string, unknown>}); +}; + +export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) => void)=>DashBoardActions=(set: (fn: (state: DashBoardType) => DashBoardType) => void) => { + return { + initializeDashBoardData: async () => { + set((state: DashBoardType) => ({ + ...state, + DashboardMain: { + ...state.DashboardMain, + isInit: true, + } + })); + }, + + /** 시설정보 및 운영현황 - 계획정보 */ + searchPlanInfo : async (targetYear: string, fcltyCd: string) => { + const res = await fetchFacilityPlan(targetYear, fcltyCd); + + planFcltyList.map((el, index) => { + planYearList = []; + planYearList.push(res[0]?res[0].yrDt4.toString():""); + planYearList.push(res[0]?res[0].yrDc1.toString():""); + planYearList.push(res[0]?res[0].yrDc2.toString():""); + planYearList.push(res[0]?res[0].yrDc3.toString():""); + planYearList.push(res[0]?res[0].yrDc4.toString():""); + + if(index === 0){ + el.provisionalApproval = res&&res.length>0?res[0].planSwageQntSumDt4:''; + el.now = res&&res.length>0?res[0].planSwageQntSum:''; + el.stepOne = res&&res.length>0?res[0].planSwageQntSumDc1:''; + el.stepTwo = res&&res.length>0?res[0].planSwageQntSumDc2:''; + el.stepThree = res&&res.length>0?res[0].planSwageQntSumDc3:''; + el.stepFour = res&&res.length>0?res[0].planSwageQntSumDc4:''; + } + if(index === 1){ + el.provisionalApproval = res&&res.length>0?res[0].swtrmtPpltnCntDt4:''; + el.now = res&&res.length>0?res[0].swtrmtPpltnCnt:''; + el.stepOne = res&&res.length>0?res[0].swtrmtPpltnCntDc1:''; + el.stepTwo = res&&res.length>0?res[0].swtrmtPpltnCntDc2:''; + el.stepThree = res&&res.length>0?res[0].swtrmtPpltnCntDc3:''; + el.stepFour = res&&res.length>0?res[0].swtrmtPpltnCntDc4:''; + } + if(index === 2){ + el.provisionalApproval = res&&res.length>0?res[0].swtrmtZoneAreaDt4:''; + el.now = res&&res.length>0?res[0].swtrmtZoneArea:''; + el.stepOne = res&&res.length>0?res[0].swtrmtZoneAreaDc1:''; + el.stepTwo = res&&res.length>0?res[0].swtrmtZoneAreaDc2:''; + el.stepThree = res&&res.length>0?res[0].swtrmtZoneAreaDc3:''; + el.stepFour = res&&res.length>0?res[0].swtrmtZoneAreaDc4:''; + } + if(index === 4){ + el.provisionalApproval = res&&res.length>0?res[0].ugwtUseSum:''; + } + + // 임시시 + dataFormSettingPlan(el); + }); + + set((state: DashBoardType) => ({ + ...state, + PlanFclty : { + ...state.PlanFclty, + planFcltyList : planFcltyList + } + })); + }, + + /** 시설정보 및 운영현황 - 운영실태 */ + searchOperationStatus: async (isExcel: boolean, searchYear: string, fcltyCd: string) => { + const res = await fetchOperationStatus(searchYear, fcltyCd); + const data = res.data; + if(data && operationalStatusList){ + // 유량 - 유입 + if (operationalStatusList?.[0]) { + // 연간 + operationalStatusList[0].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_TP_INFL_WQLT_TP?data.yearData.AVG_TP_INFL_WQLT_TP:0; + operationalStatusList[0].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_TP_INFL_WQLT_TP?data.yearData.MAX_TP_INFL_WQLT_TP:0; + operationalStatusList[0].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_TP_INFL_WQLT_TP?data.yearData.MIN_TP_INFL_WQLT_TP:0; + // 여름 + operationalStatusList[0].summerAll = data.summer===null?'':data.summer.AVG_INFL_AMNT?data.summer.AVG_INFL_AMNT:0+data.summer.AVG_SUM_FLOW?data.summer.AVG_SUM_FLOW:0+data.summer.AVG_TP_INFL_WQLT_TP?data.summer.AVG_TP_INFL_WQLT_TP:0; + operationalStatusList[0].summerThree = data.summerRainy===null?'':data.summerRainy.AVG_INFL_AMNT?data.summerRainy.AVG_INFL_AMNT:0+data.summerRainy.AVG_SUM_FLOW?data.summerRainy.AVG_SUM_FLOW:0+data.summerRainy.AVG_TP_INFL_WQLT_TP?data.summerRainy.AVG_TP_INFL_WQLT_TP:0; + operationalStatusList[0].summerRain = data.summerSunny===null?'':data.summerSunny.AVG_INFL_AMNT?data.summerSunny.AVG_INFL_AMNT:0+data.summerSunny.AVG_SUM_FLOW?data.summerSunny.AVG_SUM_FLOW:0+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].groundWaterAvg = 10; + operationalStatusList[0].groundWaterSummer = 11; + operationalStatusList[0].groundWaterWinter = 12; + // 오염물질 + // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; + 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].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[0]); + } + + // 유량 - 순수하수 + if (operationalStatusList?.[1]) { + // 연간 + operationalStatusList[1].yearAvg = data.yearData?data.yearData.AVG_INFL_AMNT:''; + operationalStatusList[1].yearMax = data.yearData?data.yearData.MAX_INFL_AMNT:''; + operationalStatusList[1].yearMin = data.yearData?data.yearData.MIN_INFL_AMNT:''; + // 여름 + operationalStatusList[1].summerAll = data.summer?data.summer.AVG_INFL_AMNT:''; + operationalStatusList[1].summerThree = data.summerRainy?data.summerRainy.AVG_INFL_AMNT:''; + operationalStatusList[1].summerRain = data.summerSunny?data.summerSunny.AVG_INFL_AMNT:''; + // 겨울 + operationalStatusList[1].winterAll = data.winter?data.winter.AVG_INFL_AMNT:''; + operationalStatusList[1].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_INFL_AMNT:''; + operationalStatusList[1].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_INFL_AMNT:''; + operationalStatusList[1].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_INFL_AMNT:''; + // 지하수 + operationalStatusList[1].groundWaterAvg = 10; + operationalStatusList[1].groundWaterSummer = 11; + operationalStatusList[1].groundWaterWinter = 12; + // 오염물질 + // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; + 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[1].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatusList[1].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[1]); + } + + // 유량 - 연계처리수 + if (operationalStatusList?.[2]) { + // 연간 + operationalStatusList[2].yearAvg = data.yearData?data.yearData.AVG_SUM_FLOW:''; + operationalStatusList[2].yearMax = data.yearData?data.yearData.MAX_SUM_FLOW:''; + operationalStatusList[2].yearMin = data.yearData?data.yearData.MIN_SUM_FLOW:''; + // 여름 + operationalStatusList[2].summerAll = data.summer?data.summer.AVG_SUM_FLOW:''; + operationalStatusList[2].summerThree = data.summerRainy?data.summerRainy.AVG_SUM_FLOW:''; + operationalStatusList[2].summerRain = data.summerSunny?data.summerSunny.AVG_SUM_FLOW:''; + // 겨울 + operationalStatusList[2].winterAll = data.winter?data.winter.AVG_SUM_FLOW:''; + operationalStatusList[2].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_SUM_FLOW:''; + operationalStatusList[2].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_SUM_FLOW:''; + operationalStatusList[2].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_SUM_FLOW:''; + // 지하수 + operationalStatusList[2].groundWaterAvg = 10; + operationalStatusList[2].groundWaterSummer = 11; + operationalStatusList[2].groundWaterWinter = 12; + // 오염물질 + // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; + 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[2].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatusList[2].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[2]); + } + + // 유량 - 총인설비방류수 + if (operationalStatusList?.[3]) { + // 연간 + operationalStatusList[3].yearAvg = data.yearData?data.yearData.AVG_TP_INFL_WQLT_TP:''; + operationalStatusList[3].yearMax = data.yearData?data.yearData.MAX_TP_INFL_WQLT_TP:''; + operationalStatusList[3].yearMin = data.yearData?data.yearData.MIN_TP_INFL_WQLT_TP:''; + // 여름 + operationalStatusList[3].summerAll = data.summer?data.summer.AVG_TP_INFL_WQLT_TP:''; + operationalStatusList[3].summerThree = data.summerRainy?data.summerRainy.AVG_TP_INFL_WQLT_TP:''; + operationalStatusList[3].summerRain = data.summerSunny?data.summerSunny.AVG_TP_INFL_WQLT_TP:''; + // 겨울 + operationalStatusList[3].winterAll = data.winter?data.winter.AVG_TP_INFL_WQLT_TP:''; + operationalStatusList[3].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_TP_INFL_WQLT_TP:''; + operationalStatusList[3].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_TP_INFL_WQLT_TP:''; + operationalStatusList[3].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_TP_INFL_WQLT_TP:''; + // 지하수 + operationalStatusList[3].groundWaterAvg = 10; + operationalStatusList[3].groundWaterSummer = 11; + operationalStatusList[3].groundWaterWinter = 12; + // 오염물질 + // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; + 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[3].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatusList[3].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[3]); + } + + // 유량 - 방류수 + if (operationalStatusList?.[4]) { + // 연간 + operationalStatusList[4].yearAvg = data.yearData?data.yearData.AVG_EFFL_AMNT:''; + operationalStatusList[4].yearMax = data.yearData?data.yearData.MAX_EFFL_AMNT:''; + operationalStatusList[4].yearMin = data.yearData?data.yearData.MIN_EFFL_AMNT:''; + // 여름 + operationalStatusList[4].summerAll = data.summer?data.summer.AVG_EFFL_AMNT:''; + operationalStatusList[4].summerThree = data.summerRainy?data.summerRainy.AVG_EFFL_AMNT:''; + operationalStatusList[4].summerRain = data.summerSunny?data.summerSunny.AVG_EFFL_AMNT:''; + // 겨울 + operationalStatusList[4].winterAll = data.winter?data.winter.AVG_EFFL_AMNT:''; + operationalStatusList[4].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_EFFL_AMNT:''; + operationalStatusList[4].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_EFFL_AMNT:''; + operationalStatusList[4].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_EFFL_AMNT:''; + // 지하수 + operationalStatusList[4].groundWaterAvg = 10; + operationalStatusList[4].groundWaterSummer = 11; + operationalStatusList[4].groundWaterWinter = 12; + // 오염물질 + // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; + 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[4].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatusList[4].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[4]); + } + + /* + * 수질 - 유입(BOD) + */ + if (operationalStatusList?.[5]) { + // 연간 + operationalStatusList[5].yearAvg = data.yearData?data.yearData.AVG_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].yearMax = data.yearData?data.yearData.MAX_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].yearMin = data.yearData?data.yearData.MIN_INFL_ASSCT_BFR_WQLT_BOD:''; + dataFormSetting(operationalStatusList[5]); + // 여름 + operationalStatusList[5].summerAll = data.summer?data.summer.AVG_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].summerThree = data.summerRainy?data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].summerRain = data.summerSunny?data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD:''; + // 겨울 + operationalStatusList[5].winterAll = data.winter?data.winter.AVG_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD:''; + operationalStatusList[5].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_BOD:''; + // 오염물질 + // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; + 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[5].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod); + operationalStatusList[5].rdiRain = summerLowRain; + dataFormSetting(operationalStatusList[5]); + } + + /** + * 수질-방류(BOD) + */ + if (operationalStatusList?.[6]) { + // 연간 + operationalStatusList[6].yearAvg = data.yearData?data.yearData.AVG_EFFL_WQLT_BOD:''; + operationalStatusList[6].yearMax = data.yearData?data.yearData.MAX_EFFL_WQLT_BOD:''; + operationalStatusList[6].yearMin = data.yearData?data.yearData.MIN_EFFL_WQLT_BOD:''; + dataFormSetting(operationalStatusList[6]); + // 여름 + operationalStatusList[6].summerAll = data.summer?data.summer.AVG_EFFL_WQLT_BOD:''; + operationalStatusList[6].summerThree = data.summerRainy?data.summerRainy.AVG_EFFL_WQLT_BOD:''; + operationalStatusList[6].summerRain = data.summerSunny?data.summerSunny.AVG_EFFL_WQLT_BOD:''; + // 겨울 + operationalStatusList[6].winterAll = data.winter?data.winter.AVG_EFFL_WQLT_BOD:''; + operationalStatusList[6].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_EFFL_WQLT_BOD:''; + operationalStatusList[6].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_EFFL_WQLT_BOD:''; + operationalStatusList[6].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_EFFL_WQLT_BOD:''; + dataFormSetting(operationalStatusList[6]); + } + + // 수질-유입(T-N) + if (operationalStatusList?.[7]) { + // 연간 + operationalStatusList[7].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + // 여름 + operationalStatusList[7].summerAll = data.summer?Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + // 겨울 + operationalStatusList[7].winterAll = data.winter?Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[7].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + } + + // 수질-방류(T-N) + if (operationalStatusList?.[8]) { + // 연간 + operationalStatusList[8].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + // 여름 + operationalStatusList[8].summerAll = data.summer?Number(Math.round(data.summer.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + // 겨울 + operationalStatusList[8].winterAll = data.winter?Number(Math.round(data.winter.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[8].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():''; + } + + // 수질-유입(T-P) + if (operationalStatusList?.[9]) { + // 연간 + operationalStatusList[9].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 여름 + operationalStatusList[9].summerAll = data.summer?Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 겨울 + operationalStatusList[9].winterAll = data.winter?Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[9].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + } + + // 수질-방류(T-P) + if (operationalStatusList?.[10]) { + // 연간 + operationalStatusList[10].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 여름 + operationalStatusList[10].summerAll = data.summer?Number(Math.round(data.summer.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 겨울 + operationalStatusList[10].winterAll = data.winter?Number(Math.round(data.winter.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[10].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + } + + /** + * 총인설비 + */ + // + // 유입 + if (operationalStatusList?.[11]) { + // 연간 + operationalStatusList[11].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 여름 + operationalStatusList[11].summerAll = data.summer?Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 겨울 + operationalStatusList[11].winterAll = data.winter?Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[11].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + } + + // 방류 + if (operationalStatusList?.[12]) { + // 연간 + operationalStatusList[12].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 여름 + operationalStatusList[12].summerAll = data.summer?Number(Math.round(data.summer.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + // 겨울 + operationalStatusList[12].winterAll = data.winter?Number(Math.round(data.winter.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + operationalStatusList[12].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():''; + } + } + //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.inflAssctAftWqltBod, + 유입TN: item.inflAssctAftWqltTn, + 유입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: { + ...state.OperationStatus, + operationalStatusList: operationalStatusList, + operationalStatusCount: res.count + } + })) + }, + + searchMainGraph: async (searchYear: string, fcltyCd: string) => { + const res = await fetchMainGraph(searchYear, fcltyCd); + if(res == null || res.data == null || res.data.length === 0) { + set((state: DashBoardType) => ({ + ...state, + MainGraph: { + ...state.MainGraph, + chart_inflowRain: [], + chart_bodRain: [], + chart_inflTp: [], + chart_chongInTp: [], + chart_efflTp: [], + chart_inflTn: [], + chart_efflTn: [], + chart_inflBod: [], + chart_efflBod: [], + } + })); + return; + } + + const chart_inflowRain = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + barVal0: item.infoRnDay ?? 0, + lineVal0: item.fctCpc ?? 0, + lineVal1: item.inflAmnt ?? 0, + })); + + const chart_bodRain = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + barVal0: item.infoRnDay ?? 0, + lineVal0: item.inflAssctAftWqltBod ?? 0, + })); + + const chart_inflTp = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.inflAssctBfrWqltTp ?? 0 + })); + + const chart_chongInTp = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.tpInflWqltTp ?? 0 + })); + + const chart_efflTp = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.efflWqltTp ?? 0 + })); + + const chart_inflTn = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.inflAssctAftWqltTn ?? 0 + })); + + const chart_efflTn = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.efflWqltTn ?? 0 + })); + + const chart_inflBod = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.inflAssctAftWqltBod ?? 0 + })); + + const chart_efflBod = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ + label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + val : item.efflWqltBod ?? 0 + })); + + set((state: DashBoardType) => ({ + ...state, + MainGraph: { + ...state.MainGraph, + chart_inflowRain: chart_inflowRain, + chart_bodRain: chart_bodRain, + chart_inflTp: chart_inflTp, + chart_chongInTp : chart_chongInTp, + chart_efflTp: chart_efflTp, + chart_inflTn: chart_inflTn, + chart_efflTn: chart_efflTn, + chart_inflBod: chart_inflBod, + chart_efflBod: chart_efflBod + } + })) + }, + + searchRainManage: async (searchYear: string, fcltyCd: string, isExcel : boolean) => { + const res = await fetchMainGraph(searchYear, fcltyCd); + if(res == null || res.data.wqltObjs == null || res.data.wqltObjs.length === 0) { + set((state: DashBoardType) => ({ + ...state, + RainManage: { + ...state.RainManage, + rainManageList: [], + } + })); + return; + } + const fcltyCpcValue = res.data.wqltObjs[0].fctCpc; + + const itemList = ['계', '3mm 미만', '3~10mm 미만', '10~20mm 미만', '20~30mm 미만', '30~40mm 미만', '40~50mm 미만', '50mm~60mm 미만', '60mm 이상']; + const rainRate = ['0', '0', '0', '0', '0', '0', '0', '0', '0']; + const rainDate = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAmtCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAmnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const fcltyCpct = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const dsInflWqltBod: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const dsInflWqltCod: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const dsInflWqltSs: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const dsInflWqltTn: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const dsInflWqltTp: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltBod = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltTocCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltToc = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltSs = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltTn = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const inflAssctAftWqltTp = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltBod = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltCodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltCod = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltSs = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltTn = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const efflWqltTp = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + // Process the data and create rain management list + res.data.wqltObjs.forEach((item: RainManageListProps) => { + // 강우일 + if(Number(item.infoRnDay) < 3){ + rainDate[1] = Number(rainDate[1] || 0) + 1; + inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1; + inflAmnt[1] = Number(inflAmnt[1] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[1] = 0; + inflAssctAftWqltBodCnt[1] = Number(inflAssctAftWqltBodCnt[1] || 0) + 1; + inflAssctAftWqltBod[1] = Number(inflAssctAftWqltBod[1] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[1] = Number(inflAssctAftWqltTocCnt[1] || 0) + 1; + inflAssctAftWqltToc[1] = Number(inflAssctAftWqltToc[1] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[1] = Number(inflAssctAftWqltSsCnt[1] || 0) + 1; + inflAssctAftWqltSs[1] = Number(inflAssctAftWqltSs[1] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[1] = Number(inflAssctAftWqltTnCnt[1] || 0) + 1; + inflAssctAftWqltTn[1] = Number(inflAssctAftWqltTn[1] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[1] = Number(inflAssctAftWqltTpCnt[1] || 0) + 1; + inflAssctAftWqltTp[1] = Number(inflAssctAftWqltTp[1] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[1] = Number(efflWqltBodCnt[1] || 0) + 1; + efflWqltBod[1] = Number(efflWqltBod[1] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[1] = Number(efflWqltCodCnt[1] || 0) + 1; + efflWqltCod[1] = Number(efflWqltCod[1] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[1] = Number(efflWqltSsCnt[1] || 0) + 1; + efflWqltSs[1] = Number(efflWqltSs[1] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[1] = Number(efflWqltTnCnt[1] || 0) + 1; + efflWqltTn[1] = Number(efflWqltTn[1] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[1] = Number(efflWqltTpCnt[1] || 0) + 1; + efflWqltTp[1] = Number(efflWqltTp[1] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 3 && Number(item.infoRnDay) < 10){ + rainDate[2] = Number(rainDate[2] || 0) + 1; + inflAmtCnt[2] = Number(inflAmtCnt[2] || 0) + 1; + inflAmnt[2] = Number(inflAmnt[2] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[2] = 0; + inflAssctAftWqltBodCnt[2] = Number(inflAssctAftWqltBodCnt[2] || 0) + 1; + inflAssctAftWqltBod[2] = Number(inflAssctAftWqltBod[2] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[2] = Number(inflAssctAftWqltTocCnt[2] || 0) + 1; + inflAssctAftWqltToc[2] = Number(inflAssctAftWqltToc[2] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[2] = Number(inflAssctAftWqltSsCnt[2] || 0) + 1; + inflAssctAftWqltSs[2] = Number(inflAssctAftWqltSs[2] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[2] = Number(inflAssctAftWqltTnCnt[2] || 0) + 1; + inflAssctAftWqltTn[2] = Number(inflAssctAftWqltTn[2] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[2] = Number(inflAssctAftWqltTpCnt[2] || 0) + 1; + inflAssctAftWqltTp[2] = Number(inflAssctAftWqltTp[2] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[2] = Number(efflWqltBodCnt[2] || 0) + 1; + efflWqltBod[2] = Number(efflWqltBod[2] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[2] = Number(efflWqltCodCnt[2] || 0) + 1; + efflWqltCod[2] = Number(efflWqltCod[2] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[2] = Number(efflWqltSsCnt[2] || 0) + 1; + efflWqltSs[2] = Number(efflWqltSs[2] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[2] = Number(efflWqltTnCnt[2] || 0) + 1; + efflWqltTn[2] = Number(efflWqltTn[2] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[2] = Number(efflWqltTpCnt[2] || 0) + 1; + efflWqltTp[2] = Number(efflWqltTp[2] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 10 && Number(item.infoRnDay) < 20){ + rainDate[3] = Number(rainDate[3] || 0) + 1; + inflAmtCnt[3] = Number(inflAmtCnt[3] || 0) + 1; + inflAmnt[3] = Number(inflAmnt[3] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[3] = 0; + inflAssctAftWqltBodCnt[3] = Number(inflAssctAftWqltBodCnt[3] || 0) + 1; + inflAssctAftWqltBod[3] = Number(inflAssctAftWqltBod[3] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[3] = Number(inflAssctAftWqltTocCnt[3] || 0) + 1; + inflAssctAftWqltToc[3] = Number(inflAssctAftWqltToc[3] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[3] = Number(inflAssctAftWqltSsCnt[3] || 0) + 1; + inflAssctAftWqltSs[3] = Number(inflAssctAftWqltSs[3] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[3] = Number(inflAssctAftWqltTnCnt[3] || 0) + 1; + inflAssctAftWqltTn[3] = Number(inflAssctAftWqltTn[3] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[3] = Number(inflAssctAftWqltTpCnt[3] || 0) + 1; + inflAssctAftWqltTp[3] = Number(inflAssctAftWqltTp[3] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[3] = Number(efflWqltBodCnt[3] || 0) + 1; + efflWqltBod[3] = Number(efflWqltBod[3] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[3] = Number(efflWqltCodCnt[3] || 0) + 1; + efflWqltCod[3] = Number(efflWqltCod[3] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[3] = Number(efflWqltSsCnt[3] || 0) + 1; + efflWqltSs[3] = Number(efflWqltSs[3] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[3] = Number(efflWqltTnCnt[3] || 0) + 1; + efflWqltTn[3] = Number(efflWqltTn[3] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[3] = Number(efflWqltTpCnt[3] || 0) + 1; + efflWqltTp[3] = Number(efflWqltTp[3] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 20 && Number(item.infoRnDay) < 30){ + rainDate[4] = Number(rainDate[4] || 0) + 1; + inflAmtCnt[4] = Number(inflAmtCnt[4] || 0) + 1; + inflAmnt[4] = Number(inflAmnt[4] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[4] = 0; + inflAssctAftWqltBodCnt[4] = Number(inflAssctAftWqltBodCnt[4] || 0) + 1; + inflAssctAftWqltBod[4] = Number(inflAssctAftWqltBod[4] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[4] = Number(inflAssctAftWqltTocCnt[4] || 0) + 1; + inflAssctAftWqltToc[4] = Number(inflAssctAftWqltToc[4] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[4] = Number(inflAssctAftWqltSsCnt[4] || 0) + 1; + inflAssctAftWqltSs[4] = Number(inflAssctAftWqltSs[4] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[4] = Number(inflAssctAftWqltTnCnt[4] || 0) + 1; + inflAssctAftWqltTn[4] = Number(inflAssctAftWqltTn[4] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[4] = Number(inflAssctAftWqltTpCnt[4] || 0) + 1; + inflAssctAftWqltTp[4] = Number(inflAssctAftWqltTp[4] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[4] = Number(efflWqltBodCnt[4] || 0) + 1; + efflWqltBod[4] = Number(efflWqltBod[4] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[4] = Number(efflWqltCodCnt[4] || 0) + 1; + efflWqltCod[4] = Number(efflWqltCod[4] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[4] = Number(efflWqltSsCnt[4] || 0) + 1; + efflWqltSs[4] = Number(efflWqltSs[4] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[4] = Number(efflWqltTnCnt[4] || 0) + 1; + efflWqltTn[4] = Number(efflWqltTn[4] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[4] = Number(efflWqltTpCnt[4] || 0) + 1; + efflWqltTp[4] = Number(efflWqltTp[4] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 30 && Number(item.infoRnDay) < 40){ + rainDate[5] = Number(rainDate[5] || 0) + 1; + inflAmtCnt[5] = Number(inflAmtCnt[5] || 0) + 1; + inflAmnt[5] = Number(inflAmnt[5] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[5] = item.inflAmnt&&Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0; + inflAssctAftWqltBodCnt[5] = Number(inflAssctAftWqltBodCnt[5] || 0) + 1; + inflAssctAftWqltBod[5] = Number(inflAssctAftWqltBod[5] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[5] = Number(inflAssctAftWqltTocCnt[5] || 0) + 1; + inflAssctAftWqltToc[5] = Number(inflAssctAftWqltToc[5] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[5] = Number(inflAssctAftWqltSsCnt[5] || 0) + 1; + inflAssctAftWqltSs[5] = Number(inflAssctAftWqltSs[5] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[5] = Number(inflAssctAftWqltTnCnt[5] || 0) + 1; + inflAssctAftWqltTn[5] = Number(inflAssctAftWqltTn[5] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[5] = Number(inflAssctAftWqltTpCnt[5] || 0) + 1; + inflAssctAftWqltTp[5] = Number(inflAssctAftWqltTp[5] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[5] = Number(efflWqltBodCnt[5] || 0) + 1; + efflWqltBod[5] = Number(efflWqltBod[5] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[5] = Number(efflWqltCodCnt[5] || 0) + 1; + efflWqltCod[5] = Number(efflWqltCod[5] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[5] = Number(efflWqltSsCnt[5] || 0) + 1; + efflWqltSs[5] = Number(efflWqltSs[5] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[5] = Number(efflWqltTnCnt[5] || 0) + 1; + efflWqltTn[5] = Number(efflWqltTn[5] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[5] = Number(efflWqltTpCnt[5] || 0) + 1; + efflWqltTp[5] = Number(efflWqltTp[5] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 40 && Number(item.infoRnDay) < 50){ + rainDate[6] = Number(rainDate[6] || 0) + 1; + inflAmtCnt[6] = Number(inflAmtCnt[6] || 0) + 1; + inflAmnt[6] = Number(inflAmnt[6] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[6] = 0; + inflAssctAftWqltBodCnt[6] = Number(inflAssctAftWqltBodCnt[6] || 0) + 1; + inflAssctAftWqltBod[6] = Number(inflAssctAftWqltBod[6] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[6] = Number(inflAssctAftWqltTocCnt[6] || 0) + 1; + inflAssctAftWqltToc[6] = Number(inflAssctAftWqltToc[6] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[6] = Number(inflAssctAftWqltSsCnt[6] || 0) + 1; + inflAssctAftWqltSs[6] = Number(inflAssctAftWqltSs[6] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[6] = Number(inflAssctAftWqltTnCnt[6] || 0) + 1; + inflAssctAftWqltTn[6] = Number(inflAssctAftWqltTn[6] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[6] = Number(inflAssctAftWqltTpCnt[6] || 0) + 1; + inflAssctAftWqltTp[6] = Number(inflAssctAftWqltTp[6] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[6] = Number(efflWqltBodCnt[6] || 0) + 1; + efflWqltBod[6] = Number(efflWqltBod[6] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[6] = Number(efflWqltCodCnt[6] || 0) + 1; + efflWqltCod[6] = Number(efflWqltCod[6] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[6] = Number(efflWqltSsCnt[6] || 0) + 1; + efflWqltSs[6] = Number(efflWqltSs[6] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[6] = Number(efflWqltTnCnt[6] || 0) + 1; + efflWqltTn[6] = Number(efflWqltTn[6] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[6] = Number(efflWqltTpCnt[6] || 0) + 1; + efflWqltTp[6] = Number(efflWqltTp[6] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 50 && Number(item.infoRnDay) < 60){ + rainDate[7] = Number(rainDate[7] || 0) + 1; + inflAmtCnt[7] = Number(inflAmtCnt[7] || 0) + 1; + inflAmnt[7] = Number(inflAmnt[7] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[7] = 0; + inflAssctAftWqltBodCnt[7] = Number(inflAssctAftWqltBodCnt[7] || 0) + 1; + inflAssctAftWqltBod[7] = Number(inflAssctAftWqltBod[7] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[7] = Number(inflAssctAftWqltTocCnt[7] || 0) + 1; + inflAssctAftWqltToc[7] = Number(inflAssctAftWqltToc[7] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[7] = Number(inflAssctAftWqltSsCnt[7] || 0) + 1; + inflAssctAftWqltSs[7] = Number(inflAssctAftWqltSs[7] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[7] = Number(inflAssctAftWqltTnCnt[7] || 0) + 1; + inflAssctAftWqltTn[7] = Number(inflAssctAftWqltTn[7] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[7] = Number(inflAssctAftWqltTpCnt[7] || 0) + 1; + inflAssctAftWqltTp[7] = Number(inflAssctAftWqltTp[7] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[7] = Number(efflWqltBodCnt[7] || 0) + 1; + efflWqltBod[7] = Number(efflWqltBod[7] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[7] = Number(efflWqltCodCnt[7] || 0) + 1; + efflWqltCod[7] = Number(efflWqltCod[7] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[7] = Number(efflWqltSsCnt[7] || 0) + 1; + efflWqltSs[7] = Number(efflWqltSs[7] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[7] = Number(efflWqltTnCnt[7] || 0) + 1; + efflWqltTn[7] = Number(efflWqltTn[7] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[7] = Number(efflWqltTpCnt[7] || 0) + 1; + efflWqltTp[7] = Number(efflWqltTp[7] || 0) + Number(item.efflWqltTp || 0); + }else if(Number(item.infoRnDay) >= 60){ + rainDate[8] = Number(rainDate[8] || 0) + 1; + inflAmtCnt[8] = Number(inflAmtCnt[8] || 0) + 1; + inflAmnt[8] = Number(inflAmnt[8] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[8] = 0; + inflAssctAftWqltBodCnt[8] = Number(inflAssctAftWqltBodCnt[8] || 0) + 1; + inflAssctAftWqltBod[8] = Number(inflAssctAftWqltBod[8] || 0) + Number(item.inflAssctAftWqltBod || 0); + inflAssctAftWqltTocCnt[8] = Number(inflAssctAftWqltTocCnt[8] || 0) + 1; + inflAssctAftWqltToc[8] = Number(inflAssctAftWqltToc[8] || 0) + Number(item.inflAssctAftWqltToc || 0); + inflAssctAftWqltSsCnt[8] = Number(inflAssctAftWqltSsCnt[8] || 0) + 1; + inflAssctAftWqltSs[8] = Number(inflAssctAftWqltSs[8] || 0) + Number(item.inflAssctAftWqltSs || 0); + inflAssctAftWqltTnCnt[8] = Number(inflAssctAftWqltTnCnt[8] || 0) + 1; + inflAssctAftWqltTn[8] = Number(inflAssctAftWqltTn[8] || 0) + Number(item.inflAssctAftWqltTn || 0); + inflAssctAftWqltTpCnt[8] = Number(inflAssctAftWqltTpCnt[8] || 0) + 1; + inflAssctAftWqltTp[8] = Number(inflAssctAftWqltTp[8] || 0) + Number(item.inflAssctAftWqltTp || 0); + efflWqltBodCnt[8] = Number(efflWqltBodCnt[8] || 0) + 1; + efflWqltBod[8] = Number(efflWqltBod[8] || 0) + Number(item.efflWqltBod || 0); + efflWqltCodCnt[8] = Number(efflWqltCodCnt[8] || 0) + 1; + efflWqltCod[8] = Number(efflWqltCod[8] || 0) + Number(item.efflWqltCod || 0); + efflWqltSsCnt[8] = Number(efflWqltSsCnt[8] || 0) + 1; + efflWqltSs[8] = Number(efflWqltSs[8] || 0) + Number(item.efflWqltSs || 0); + efflWqltTnCnt[8] = Number(efflWqltTnCnt[8] || 0) + 1; + efflWqltTn[8] = Number(efflWqltTn[8] || 0) + Number(item.efflWqltTn || 0); + efflWqltTpCnt[8] = Number(efflWqltTpCnt[8] || 0) + 1; + efflWqltTp[8] = Number(efflWqltTp[8] || 0) + Number(item.efflWqltTp || 0); + } + }); + + // Process the data and create rain management list + if(res.data&&res.data.dsWqltObjs&&res.data.dsWqltObjs.length > 0){ + // 설계 유입수질 데이터 가져오기 - 0값이 있으면 이전 차수에서 가져오기 + const getDesignWaterQualityData = (stepIndex: number, maxPreviousSteps: number = 3) => { + for (let i = 0; i <= maxPreviousSteps && stepIndex - i >= 0; i++) { + const currentStep = res.data.dsWqltObjs[stepIndex - i]; + if (currentStep && + currentStep.dsInflWqltBod > 0 && + currentStep.dsInflWqltCod > 0 && + currentStep.dsInflWqltSs > 0 && + currentStep.dsInflWqltTn > 0 && + currentStep.dsInflWqltTp > 0) { + return { + dsInflWqltBod: currentStep.dsInflWqltBod, + dsInflWqltCod: currentStep.dsInflWqltCod, + dsInflWqltSs: currentStep.dsInflWqltSs, + dsInflWqltTn: currentStep.dsInflWqltTn, + dsInflWqltTp: currentStep.dsInflWqltTp + }; + } + } + // 모든 차수에서 유효한 데이터를 찾지 못한 경우 기본값 반환 + return { + dsInflWqltBod: 0, + dsInflWqltCod: 0, + dsInflWqltSs: 0, + dsInflWqltTn: 0, + dsInflWqltTp: 0 + }; + }; + + const lastStepIndex = res.data.dsWqltObjs.length - 1; + const designData = getDesignWaterQualityData(lastStepIndex); + + dsInflWqltBod[0] = designData.dsInflWqltBod; + dsInflWqltCod[0] = designData.dsInflWqltCod; + dsInflWqltSs[0] = designData.dsInflWqltSs; + dsInflWqltTn[0] = designData.dsInflWqltTn; + dsInflWqltTp[0] = designData.dsInflWqltTp; + } + + rainDate[0] = (Number(rainDate[1]) || 0) + (Number(rainDate[2]) || 0) + (Number(rainDate[3]) || 0) + + (Number(rainDate[4]) || 0) + (Number(rainDate[5]) || 0) + (Number(rainDate[6]) || 0) + + (Number(rainDate[7]) || 0) + (Number(rainDate[8]) || 0); + + // 강수비율 + for(let idx=0; idx<rainDate.length; idx++){ + if(Number(rainDate[idx]) > 0){ + rainRate[0] = '100'; + rainRate[idx] = (Math.round(Number(rainDate[idx])/Number(rainDate[0])*100*100)/100).toString(); + } + } + + // 평균 및 합계 계산 + const calculateAverage = (value: number, count: number) => { + return count > 0 ? Number((value / count).toFixed(1)) : 0; + }; + + // 데이터 있는 항목만 계산 + let dataIsCount = 0; + for(let idx=0; idx<inflAmnt.length; idx++){ + if(Number(inflAmnt[idx]) > 0){ + dataIsCount++; + } + } + + for(let idx=0; idx<inflAmnt.length; idx++){ + inflAmnt[idx] = calculateAverage(Number(inflAmnt[idx]), Number(inflAmtCnt[idx])); + inflAssctAftWqltBod[idx] = calculateAverage(Number(inflAssctAftWqltBod[idx]), Number(inflAssctAftWqltBodCnt[idx])); + inflAssctAftWqltToc[idx] = calculateAverage(Number(inflAssctAftWqltToc[idx]), Number(inflAssctAftWqltTocCnt[idx])); + inflAssctAftWqltSs[idx] = calculateAverage(Number(inflAssctAftWqltSs[idx]), Number(inflAssctAftWqltSsCnt[idx])); + inflAssctAftWqltTn[idx] = calculateAverage(Number(inflAssctAftWqltTn[idx]), Number(inflAssctAftWqltTnCnt[idx])); + inflAssctAftWqltTp[idx] = calculateAverage(Number(inflAssctAftWqltTp[idx]), Number(inflAssctAftWqltTpCnt[idx])); + efflWqltBod[idx] = calculateAverage(Number(efflWqltBod[idx]), Number(efflWqltBodCnt[idx])); + efflWqltCod[idx] = calculateAverage(Number(efflWqltCod[idx]), Number(efflWqltCodCnt[idx])); + efflWqltSs[idx] = calculateAverage(Number(efflWqltSs[idx]), Number(efflWqltSsCnt[idx])); + efflWqltTn[idx] = calculateAverage(Number(efflWqltTn[idx]), Number(efflWqltTnCnt[idx])); + efflWqltTp[idx] = calculateAverage(Number(efflWqltTp[idx]), Number(efflWqltTpCnt[idx])); + } + + // 합계 계산 (index 0) + const calculateTotal = (arr: number[]) => { + return Number((arr.slice(1).reduce((sum, val) => sum + Number(val), 0)).toFixed(1)); + }; + + inflAmnt[0] = calculateAverage(calculateTotal(inflAmnt), dataIsCount); + fcltyCpct[0] = calculateTotal(fcltyCpct); + inflAssctAftWqltBod[0] = Number((calculateTotal(inflAssctAftWqltBod)/8).toFixed(1)); + inflAssctAftWqltToc[0] = Number((calculateTotal(inflAssctAftWqltToc)/8).toFixed(1)); + inflAssctAftWqltSs[0] = Number((calculateTotal(inflAssctAftWqltSs)/8).toFixed(1)); + inflAssctAftWqltTn[0] = Number((calculateTotal(inflAssctAftWqltTn)/8).toFixed(3)); + inflAssctAftWqltTp[0] = Number((calculateTotal(inflAssctAftWqltTp)/8).toFixed(3)); + efflWqltBod[0] = Number((calculateTotal(efflWqltBod)/8).toFixed(1)); + efflWqltCod[0] = Number((calculateTotal(efflWqltCod)/8).toFixed(1)); + efflWqltSs[0] = Number((calculateTotal(efflWqltSs as number[])/8).toFixed(1)); + efflWqltTn[0] = Number((calculateTotal(efflWqltTn as number[])/8).toFixed(3)); + efflWqltTp[0] = Number((calculateTotal(efflWqltTp as number[])/8).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) : '', + inflAssctAftWqltBod: inflAssctAftWqltBod[index] ? Number(inflAssctAftWqltBod[index].toFixed(1)) : '', + inflAssctAftWqltToc: inflAssctAftWqltToc[index] ? Number(inflAssctAftWqltToc[index].toFixed(1)) : '', + inflAssctAftWqltSs: inflAssctAftWqltSs[index] ? Number(inflAssctAftWqltSs[index].toFixed(1)) : '', + inflAssctAftWqltTn: inflAssctAftWqltTn[index] ? inflAssctAftWqltTn[index].toFixed(3) : '', + inflAssctAftWqltTp: inflAssctAftWqltTp[index] ? inflAssctAftWqltTp[index].toFixed(3) : '', + efflWqltBod: efflWqltBod[index] ? Number(efflWqltBod[index].toFixed(1)) : '', + efflWqltCod: efflWqltCod[index] ? Number(efflWqltCod[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) : '' + })); + + const excelList : RainManageExcelType[] = []; + if(isExcel){ + filteredData.forEach((item) => { + 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.inflAssctAftWqltBod ? Number(Number(item.inflAssctAftWqltBod).toFixed(1)) : '', + 유입수질TOC: item.inflAssctAftWqltToc ? Number(Number(item.inflAssctAftWqltToc).toFixed(1)) : '', + 유입수질SS: item.inflAssctAftWqltSs ? Number(Number(item.inflAssctAftWqltSs).toFixed(1)) : '', + 유입수질TN: item.inflAssctAftWqltTn ? Number(item.inflAssctAftWqltTn).toFixed(3) : '', + 유입수질TP: item.inflAssctAftWqltTp ? Number(item.inflAssctAftWqltTp).toFixed(3) : '', + 방류수질BOD: item.efflWqltBod ? Number(Number(item.efflWqltBod).toFixed(1)) : '', + 방류수질TOC: item.efflWqltCod ? Number(Number(item.efflWqltCod).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) : '', + }); + }); + } + + set((state: DashBoardType) => ({ + ...state, + RainManage: isExcel ? { + ...state.RainManage, + rainManageList: filteredData as RainManageListProps[], + excelList: excelList, + } : { + ...state.RainManage, + rainManageList: filteredData as RainManageListProps[], + } + })); + }, + + searchDrainageEquipment: async (searchYear: string, fcltyCd: string, isExcel : boolean) => { + await fetchDrainageEquipment(searchYear, fcltyCd); + /* if(res == null || res.data.wqltObjs == null || res.data.wqltObjs.length === 0) { + set((state: DashBoardType) => ({ + ...state.RainManage, + rainManageList: [], + })); + return; + } */ + + /* if(isExcel){ + res.data.wqltObjs.forEach((item) => { + excelList.push({ + 강우량: item.rainItem, + 강우조건비율: item.rainRate || 0, + 일수: item.rainDay || 0, + 유입유량: item.inflAmnt || 0, + 시설용량대비: item.fctCpc || 0, + 설계유입수질BOD: item.dsInflWqltBod || 0, + 설계유입수질TOC: item.dsInflWqltCod || 0, + 설계유입수질SS: item.dsInflWqltSs || 0, + 설계유입수질TN: item.dsInflWqltTn || 0, + 설계유입수질TP: item.dsInflWqltTp || 0, + 유입수질BOD: item.inflAssctAftWqltBod || 0, + 유입수질COD: item.inflAssctAftWqltToc || 0, + 유입수질SS: item.inflAssctAftWqltSs || 0, + 유입수질TN: item.inflAssctAftWqltTn || 0, + 유입수질TP: item.inflAssctAftWqltTp || 0, + 방류수질BOD: item.efflWqltBod || 0, + 방류수질COD: item.efflWqltCod || 0, + 방류수질SS: item.efflWqltSs || 0, + 방류수질TN: item.efflWqltTn || 0, + 방류수질TP: item.efflWqltTp || 0, + }); + }); + } */ + + set((state: DashBoardType) => ({ + ...state, + DrainageEquipment: isExcel ? { + ...state.DrainageEquipment, + drainageEquipmentList: drainageEquipmentList, + drainageEquipmentExcelList: [], + } : { + ...state.DrainageEquipment, + drainageEquipmentList: drainageEquipmentList, + } + })); + } + } +} \ No newline at end of file diff --git a/apps/fo/app/store/facilityPrecipitation/index.tsx b/apps/fo/app/store/facilityPrecipitation/index.tsx new file mode 100644 index 000000000..0c94cf553 --- /dev/null +++ b/apps/fo/app/store/facilityPrecipitation/index.tsx @@ -0,0 +1,307 @@ +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +import { getFetch } from "@/app/components/api"; +export type { TableUpperProps }; +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 시설별 강수현황 +type FacilityPrecipitationProps = { + bscFctCd : string, + sido : string, + gugun : string, + bscFctNm : string, + addr : string, + fctCpc : number, + stnKo : string, + infoRnDay : number, + rainEffectDay : number, + totalYearRain : number, + rvbsn : string, + lgbsn : string, + mdmbsn : string, + rgnNm : string, +} + +/* +* (2) 강수현황 팝업 +*/ +// 강수현황 +export type PrecipitationPropsDetailProps = { + bscFctCd : string, + rainDt : string, + infoRnDay : number, + rainEffectDay : number, + rainDay : number +} + +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string, + 시군구: string, + 시설이름: string, + 주소: string, + 시설용량: number, + 관측소정보 : string, + 강우일 : number, + 강우영향일 : number, + 연간총강수량 : number, +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +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: "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} +]; + +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: "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} +]; + +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}, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row: 1}, + { id: "rgnNm", title: "환경청", row: 0, rowSpan: 2}, + { id: "B", title: "행정구역", upName:"upChangeRe2", row: 0, colSpan: 2}, + { id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row: 1}, + { 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: "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} +]; + +const facilityPrecipitation:FacilityPrecipitationProps[] = [ +] + +/** + * 강수현황 상세팝업 + */ +// 강수현황 (헤더) +const upHeadListPrecipitationDetail:TableUpperProps[] = [ + { id: "rainDt", title: "날짜"}, + { id: "infoRnDay", title: "강수량(mm/일)"}, + { id: "rainDay", title: "강우일(일)"}, + { id: "rainEffectDay", title: "강우영향일(일)"} +] +// 강수현황 (리스트) +const gridListPrecipitationDetail:PrecipitationPropsDetailProps[] = [ +] + +/**==================================== + * Store - api + ====================================*/ + +const fetchFacilityPrecipitationList = async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const tailStr = '/'+pageNo + '/' + pageSize + '/' + searchYear + (part?'/' + part:'' ) + (nameCd?'/' + nameCd:'' ) + (fcltCpct?'/' + fcltCpct:'' ) + (operationValue?'/' + operationValue:'' ) + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') ; + console.log('tailStr', tailStr); + const url = '/fclt/rain' + tailStr; + const res = await getFetch('fo', url); + const tmpData:PrecipitationPropsDetailProps[] = []; + if(res!= null && res.data.length > 0){ + for(let idx=0; idx<res.data.length; idx++){ + for(let idx2=0; idx2<res.data[idx].rainyDayObjList.length; idx2++){ + const bscFctCd = res.data[idx].rainyDayObjList[idx2].bscFctCd; + const rainDt = res.data[idx].rainyDayObjList[idx2].rainDt; + const infoRnDay = res.data[idx].rainyDayObjList[idx2].infoRnDay; + const rainDay = res.data[idx].rainyDayObjList[idx2].infoRnDay>=3?1:0; + let rainEffectDay = 0; + if(idx2 < res.data[idx].rainyDayObjList.length-2){ + if(rainDay==1){ + rainEffectDay = 1; + } else { + if(idx2 >= 2){ + if(res.data[idx].rainyDayObjList[idx2-1].infoRnDay>=3 || res.data[idx].rainyDayObjList[idx2-2].infoRnDay>=3){ + rainEffectDay = 1; + } + } + } + } else if(idx2 < res.data[idx].rainyDayObjList.length-1){ + if(rainDay==1 || res.data[idx].rainyDayObjList[idx2+1].infoRnDay>=3){ + rainEffectDay = 1; + } + } else { + rainEffectDay = rainDay; + } + + tmpData.push({ + bscFctCd : bscFctCd, + rainDt : rainDt, + infoRnDay : infoRnDay, + rainDay : rainDay, + rainEffectDay : rainEffectDay + }) + } + } + + res.data.map((item: FacilityPrecipitationProps) => { + const filteredList = tmpData.filter((gridItem) => { + return gridItem.bscFctCd === item.bscFctCd; + }); + + item.rainEffectDay = filteredList.reduce((acc, curr) => acc + curr.rainEffectDay, 0); + item.totalYearRain = filteredList.reduce((acc, curr) => acc + curr.infoRnDay, 0); + item.infoRnDay = filteredList.reduce((acc, curr) => acc + curr.rainDay, 0); + }) + } + return { data: res.data, count: res.count, detailData: tmpData }; +} + +/**==================================== + * Store - expaort + ====================================*/ +export type FacilityPrecipitationActions = { + searchFacilityPrecipitationList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; +} + +const dataFormSetting=(item : FacilityPrecipitationProps)=> { + DataForm({ type: 'number', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'totalYearRain', item: item as unknown as Record<string, unknown>}); +}; + +export type FacilityPrecipitationType = { + FacilityPrecipitationSearch: { + isInit: boolean; + labelArray:string[]; + }, + + FacilityPrecipitation : { + isInit: boolean; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + facilityPrecipitation: FacilityPrecipitationProps[]; + excelList: ExcelType[]; + facilityPrecipitationCount: number; + }, + + PrecipitationDetail : { + isInit : boolean; + upHeadListPrecipitationDetail : TableUpperProps[]; + gridListPrecipitationDetail : PrecipitationPropsDetailProps[]; + } +} + +export const facilityPrecipitationInitState: FacilityPrecipitationType = { + FacilityPrecipitationSearch: { + isInit: false, + labelArray: LabelArray, + }, + + FacilityPrecipitation : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + facilityPrecipitation : facilityPrecipitation, + excelList : [], + facilityPrecipitationCount : 0 + }, + + PrecipitationDetail : { + isInit : false, + upHeadListPrecipitationDetail : upHeadListPrecipitationDetail, + gridListPrecipitationDetail : gridListPrecipitationDetail + } +} + +export type FacilityPrecipitationStore = FacilityPrecipitationType & FacilityPrecipitationActions; + +export const facilityPrecipitationReducer:(set:(fn: (state: FacilityPrecipitationType) => FacilityPrecipitationType) => void)=>FacilityPrecipitationActions=(set: (fn: (state: FacilityPrecipitationType) => FacilityPrecipitationType) => void) => { + return { + searchFacilityPrecipitationList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchFacilityPrecipitationList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + const data = res.data; + const excelDownList: ExcelType[] = []; + + if(pageSize>5000){ + data.forEach((item: FacilityPrecipitationProps) => { + dataFormSetting(item) + item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : 0; + item.totalYearRain = item.totalYearRain ? parseFloat(item.totalYearRain.toString().replace(/,/g, '')) : 0; + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시도: item.sido, + 시군구: item.gugun, + 시설이름: item.bscFctNm, + 주소: item.addr, + 시설용량: item.fctCpc, + 관측소정보: item.stnKo, + 강우일: item.infoRnDay, + 강우영향일: item.rainEffectDay, + 연간총강수량: item.totalYearRain, + }) + }) + }else{ + data.forEach((item: FacilityPrecipitationProps) => { + dataFormSetting(item) + item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : 0; + item.totalYearRain = item.totalYearRain ? parseFloat(item.totalYearRain.toString().replace(/,/g, '')) : 0; + }) + } + set((state:FacilityPrecipitationType) => ({ + ...state, + FacilityPrecipitation: pageSize<5000?{ + ...state.FacilityPrecipitation, + facilityPrecipitation: res.data, + facilityPrecipitationCount: res.count, + }:{ + ...state.FacilityPrecipitation, + excelList: excelDownList, + }, + PrecipitationDetail:{ + ...state.PrecipitationDetail, + gridListPrecipitationDetail: res.detailData + } + })) + } + } +} \ No newline at end of file diff --git a/apps/fo/app/store/facilityWaterQlty/index.tsx b/apps/fo/app/store/facilityWaterQlty/index.tsx new file mode 100644 index 000000000..d1bff7723 --- /dev/null +++ b/apps/fo/app/store/facilityWaterQlty/index.tsx @@ -0,0 +1,644 @@ +import { getFetch } from "@/app/components/api"; +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +export type { TableUpperProps }; + +/**==================================== + * Store - 타입 + * + * tn, tp는 소수점 3자리까지, 나머지는 소수점 1자리까지 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 시설수질리스트 타입 +export interface FacilityWaterQltyProps { + bscFctCd: string, + bscFctNm: string, + msrDt: string + sidoNm: string, + sggNm: string, + addr: string, + fctCpc: number, + inflAssctAftWqltBod: string | '', + inflAssctAftWqltToc: string | '', + inflAssctAftWqltSs: string | '', + inflAssctAftWqltTn: string | '', + inflAssctAftWqltTp: string | '', + inflAssctBfrWqltTp: string | '', + efflWqltBod: string | '', + efflWqltCod: string | '', + efflWqltSs: string | '', + efflWqltTn: string | '', + efflWqltTp: string | '', + rgnNm:string|'', + rvbsn:string|'', + lgbsn:string|'', + mdmbsn:string|'', + inflAmnt: number | '', + lclgvrnmntNm: string, + infoRnDay: number | '', + rainEffectDay: number | '', + tpInflWqltTmprtr: number | '', + tpInflWqltPh: string | '', + tpInflWqltBod: string | '', + tpInflWqltCod: string | '', + tpInflWqltSs: string | '', + tpInflWqltTn: string | '', + tpInflWqltTp: string | '', + tpInflWqltEclgyTox: string | '', + tpInflWqltClfgrpcnt: string | '', +} +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string, + 시군구: string, + 시설이름: string, + 주소: string, + 용량: number, + 유입BOD: string | '', + 유입TOC: string | '', + 유입SS: string | '', + 유입TN: string | '', + 유입TP: string | '', + 유출BOD: string | '', + 유출TOC: string | '', + 유출SS: string | '', + 유출TN: string | '', + 유출TP: string | '', +} + +/** + * (2) 모달페이지 + */ +// 시설수질리스트 타입 +export type FacilityWaterQltyTmsProps = { + part: string, + toc: number, + ss: number, + tn: number, + tp: number, +} + +// API 응답 데이터 타입 +export interface WaterQualityHistoryResponse { + data: { + wqltObjs: FacilityWaterQltyProps[]; + }; +} + +/**==================================== + * Store - data + ====================================*/ +/** + * (1) 메인페이지 + */ +// 메인 라벨 배열 +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 }, + { id: "bscFctNm", title: "시설명", rowSpan: 2, row: 0 }, + { id: "addr", title: "주소", rowSpan: 2, row: 0 }, + { id: "fctCpc", title: "시설용량(m³/일)", align: "right", rowSpan: 2, row: 0 }, + { id: "A", title: "유입", colSpan: 5, row: 0 }, + { id: "B", title: "방류", colSpan: 5, row: 0 }, + { id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1 }, + { id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1 }, + { id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 }, + { id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 4, align: "right", row: 1 }, + { id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1 }, + { id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1 }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1 }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 4, align: "right", row: 1 }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1 }, +]; + +// 시설수질리스트 헤더(권역별) +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 }, + { id: "mdmbsn", title: "중권역", upName: "upChangeRe1", upSequnce: 3, row:1 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "addr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2 }, + { id: "A", title: "유입", colSpan: 5, row: 0 }, + { id: "B", title: "방류", colSpan: 5, row: 0 }, + { id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1 }, + { id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 }, + { id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 }, + { id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 }, + { id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 }, + { id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 }, +]; + +// 시설수질리스트 헤더(환경청별) +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 }, + { id: "mdmbsn", title: "중권역", row:1 }, + { id: "rgnNm", title: "환경청", row:0, rowSpan:2 }, + { id: "D", title: "행정명", colSpan: 2, row: 0 }, + { id: "sidoNm", title: "시도", upName: "upChangeRe2", row:1 }, + { id: "sggNm", title: "시군구", upName: "upChangeRe2", row:1 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "addr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2 }, + { id: "A", title: "유입", colSpan: 5, row: 0 }, + { id: "B", title: "방류", colSpan: 5, row: 0 }, + { id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1 }, + { id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 }, + { id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 }, + { id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 }, + { id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 }, + { id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 }, +]; + +/** + * (2) 모달페이지 + */ +// 처리시설 수질현황 헤더(모달-연간) +const upHeadListModalYear: TableUpperProps[] = [ + { id: "msrDt", title: "날짜" }, + { id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe1", upSequnce: 1, align: "right" }, + { id: "inflAssctAftWqltToc", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" }, + { id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" }, + { id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" }, + { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" }, + { id: "efflWqltCod", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe2", upSequnce: 3, align: "right" }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe2", upSequnce: 3, align: "right" }, + { id: "infoRnDay", title: "강우량", align: "right" }, +]; + +// 처리시설 수질현황 헤더(모달-수질TMS) +const upHeadListModalTms: TableUpperProps[] = [ + { id: "part", title: "구분" }, + { id: "toc", title: "TOC(mg/L)", upName: "upChangeRe", upSequnce: 1, align: "right" }, + { id: "ss", title: "SS(mg/L)", upName: "upChangeRe", upSequnce: 2, align: "right" }, + { id: "tn", title: "T-N(mg/L)", upName: "upChangeRe", upSequnce: 3, align: "right" }, + { id: "tp", title: "T-P(mg/L)", upName: "upChangeRe", upSequnce: 4, align: "right" }, +]; + +// 처리시설 수질현황 데이터(모달-연간) +const facilityWaterQltyYearList: FacilityWaterQltyProps[] = [ +]; + +// 처리시설 수질현황 데이터(모달-수질TMS) +const facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[] = [ +]; + +// 총인설비 수질현황 헤더 데이터 +const upHeadListModalChongIn: TableUpperProps[] = [ + { id: "msrDt", title: "날짜" }, + { id: "tpInflWqltBod", title: "BOD(mg/L)", upName: "upChangeRe1", upSequnce: 1, align: "right" }, + { id: "tpInflWqltTp", title: "T-P(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" }, + { id: "efflWqltBod", title: "BOD(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" }, + { id: "efflWqltTp", title: "T-P(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" }, +]; + +/**==================================== +* Store - api (데이터 호출) +====================================*/ +// 메인페이지 +// 시설정보 수질 리스트 데이터 호출 +const fetchFacilityWaterQltyList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string|undefined, nameCd?: string|undefined, fcltCpct?: string|undefined, operationValue?: string|undefined, fcltyNm?: string | undefined): Promise<{ data: FacilityWaterQltyProps[], count: number }> => { + try { + const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } + } catch (error) { + console.error('시설정보 수질현황 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } +} + +// 시설정보 수질 리스트 데이터 호출(모달-수질TMS) +const fetchFacilityWaterQltyTmsList = async (facilityCd: string|undefined) => { + try { + const res = await getFetch('wr', '/il/getTms/' + facilityCd); + return { data: res.data, count: res.count }; + } catch (error) { + console.error('시설정보 수질현황 TMS 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } +}; + +// 시설정보 수질 리스트 데이터 호출(모달-이력) +const fetchFacilityWaterQltyListHist = async (searchYear: string|undefined, facilityCd: string|undefined): Promise<WaterQualityHistoryResponse | null> => { + try {// + const res = await getFetch('fo', '/fclt/qlty/hist/' + searchYear + '/' + facilityCd); + return res; + } catch (error) { + console.error('시설정보 수질현황 이력 데이터를 가져오는데 실패했습니다:', error); + return null; + } +}; + +/**==================================== +* Store - export +====================================*/ +export type FacilityWaterQltyType = { + FacilityWaterQltyMain: { + facilityWaterQltyList: FacilityWaterQltyProps[]; + excelList: ExcelType[]; + facilityWaterQltyCount: number; + isInit: boolean; + mainLabelArray: string[]; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + }, + + FacilityWaterQltyModal: { + // 처리시설 수질현황 + upHeadListModalYear: TableUpperProps[]; + facilityWaterQltyYearList: FacilityWaterQltyProps[]; + upHeadListModalTms: TableUpperProps[]; + facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[]; + + // 수질tms + tmsList: FacilityWaterQltyTmsProps[]; + msrmtDate: string; + + // 총인시설 수질현황 + upHeadListModalChongIn: TableUpperProps[]; + } +} + +export const facilityWaterQltyInitState: FacilityWaterQltyType = { + FacilityWaterQltyMain: { + facilityWaterQltyCount: 0, + facilityWaterQltyList: [], + excelList: [], + isInit: false, + mainLabelArray: mainLabelArray, + upHeadList1: upHeadList1, + upHeadList2: upHeadList2, + upHeadList3: upHeadList3, + }, + + FacilityWaterQltyModal: { + // 처리시설 수질현황 + upHeadListModalYear: upHeadListModalYear, + facilityWaterQltyYearList: facilityWaterQltyYearList, + upHeadListModalTms: upHeadListModalTms, + facilityWaterQltyTmsList: facilityWaterQltyTmsList, + + // 수질tms + tmsList: [], + msrmtDate: '', + + // 총인시설 수질현황 + upHeadListModalChongIn: upHeadListModalChongIn, + } +} + +export type FacilityWaterQltyActions = { + /** + * 시설정보 수질현황 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + * @param searchYear : 검색 년도 + * @param part : 구분 + * @param nameCd : 시설명 + * @param fcltCpct : 시설용량 + * @param operationValue : 운영상태코드 + * @param fcltyNm : 시설명 + */ + searchFacilityWaterQltyList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => Promise<void>; + + /** + * 시설정보 수질현황 이력 데이터 호출 + * @param searchYear : 검색 년도 + * @param facilityCd : 시설코드 + */ + searchFacilityWaterQltyListHist: (searchYear: string, facilityCd: string) => Promise<void>; +} + +/** + * @param item : 시설수질리스트 타입 + * @returns : 없음 + * @description : 시설수질리스트 타입의 데이터를 표현 방식으로 바꿔주는 함수 + * - msrDt : YYYY-MM-DD 형식으로 변환 + * - inflAssctAftWqltBod : 소수점 1자리까지 표현 + * - inflAssctAftWqltTn : 소수점 1자리까지 표현 + * - inflAssctBfrWqltTp : 소수점 1자리까지 표현 + * - efflWqltBod : 소수점 2자리까지 표현 + * - efflWqltTn : 소수점 2자리까지 표현 + * - efflWqltTp : 소수점 2자리까지 표현 + * @author : 박진우 + * @date : 2025-04-24 + */ +const dataFormSetting = (item: FacilityWaterQltyProps) => { + DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> }); +} + +export type FacilityWaterQltyStore = FacilityWaterQltyType & FacilityWaterQltyActions; + +export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType) => FacilityWaterQltyType) => void): FacilityWaterQltyActions => { + return { + // 메인페이지 + /** + * 시설정보 수질현황 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + * @param searchYear : 검색 년도 + * @param part : 구분 + * @param nameCd : 시설명 + * @param fcltCpct : 시설용량 + * @param operationValue : 운영상태코드 + * @param fcltyNm : 시설명 + */ + searchFacilityWaterQltyList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchFacilityWaterQltyList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + const rtnData = res.data.map(item => { + dataFormSetting(item) + if(item.inflAssctAftWqltTn) { + item.inflAssctAftWqltTn = Number(Math.round(Number(item.inflAssctAftWqltTn) * 1000)/1000).toFixed(3); + } + if(item.efflWqltTn){ + item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3); + } + if(item.inflAssctAftWqltTp){ + item.inflAssctAftWqltTp = Number(Math.round(Number(item.inflAssctAftWqltTp) * 1000)/1000).toFixed(3); + } + if(item.efflWqltTp){ + item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3); + } + if(item.inflAssctAftWqltToc){ + item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1); + } + if(item.efflWqltCod){ + item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1); + } + if(item.inflAssctAftWqltBod){ + item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1); + } + if(item.efflWqltBod){ + item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1); + } + if(item.inflAssctAftWqltSs){ + item.inflAssctAftWqltSs = Number(Math.round(Number(item.inflAssctAftWqltSs) * 10)/10).toFixed(1); + } + if(item.efflWqltSs){ + item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1); + } + if(item.inflAssctAftWqltToc){ + item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1); + } + if(item.efflWqltCod){ + item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1); + } + return item; + }); + const excelDownList: ExcelType[] = []; + if(pageSize>5000){ + 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.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시도: item.sidoNm, + 시군구: item.sggNm, + 시설이름: item.bscFctNm, + 주소: item.addr, + 용량: item.fctCpc, + 유입BOD: item.inflAssctAftWqltBod, + 유입TOC: item.inflAssctAftWqltToc, + 유입SS: item.inflAssctAftWqltSs, + 유입TN: item.inflAssctAftWqltTn, + 유입TP: item.inflAssctAftWqltTp, + 유출BOD: item.efflWqltBod, + 유출TOC: item.efflWqltCod, + 유출SS: item.efflWqltSs, + 유출TN: item.efflWqltTn, + 유출TP: item.efflWqltTp, + }); + }); + }else{ + res.data.forEach((item: FacilityWaterQltyProps) => { + dataFormSetting(item) + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + }); + } + set((state: FacilityWaterQltyType) => ({ + ...state, + FacilityWaterQltyMain: pageSize<5000?{ + ...state.FacilityWaterQltyMain, + facilityWaterQltyList: rtnData, + facilityWaterQltyCount: res.count, + }:{ + ...state.FacilityWaterQltyMain, + excelList: excelDownList, + } + })) + }, + + searchFacilityWaterQltyListHist: async (searchYear: string, facilityCd: string) => { + const res = await fetchFacilityWaterQltyListHist(searchYear, facilityCd); + const res1 = await fetchFacilityWaterQltyTmsList(facilityCd); + + let rtnMsrmtDate = ''; + const rtnTmsList: FacilityWaterQltyTmsProps[] = []; + if(res1.data){ + rtnTmsList.push({ + part: '평균', + toc: res1.data.tocAvgVl, + ss: res1.data.ssAvgVl, + tn: res1.data.tnAvgVl, + tp: res1.data.tpAvgVl, + }); + + rtnTmsList.push({ + part: '최대', + toc: res1.data.tocMaxVl, + ss: res1.data.ssMaxVl, + tn: res1.data.tnMaxVl, + tp: res1.data.tpMaxVl, + }); + + rtnTmsList.push({ + part: '최소', + toc: res1.data.tocMinVl, + ss: res1.data.ssMinVl, + tn: res1.data.tnMinVl, + tp: res1.data.tpMinVl, + }); + + rtnMsrmtDate = res1.data.msrmtDate; + }else{ + rtnTmsList.push({ + part: '평균', + toc: 0, + ss: 0, + tn: 0, + tp: 0, + }); + + rtnTmsList.push({ + part: '최대', + toc: 0, + ss: 0, + tn: 0, + tp: 0, + }); + + rtnTmsList.push({ + part: '최소', + toc: 0, + ss: 0, + tn: 0, + tp: 0, + }); + + rtnMsrmtDate = ''; + } + + if(Array.isArray(res) || !res || !('data' in res) || !res.data || !res.data.wqltObjs || res.data.wqltObjs.length === 0) { + set((state: FacilityWaterQltyType) => ({ + ...state, + FacilityWaterQltyModal: { + ...state.FacilityWaterQltyModal, + facilityWaterQltyYearList: [], + tmsList : rtnTmsList, + msrmtDate : rtnMsrmtDate + } + })) + return; + } + + const tmpData:FacilityWaterQltyProps[] = []; + + // facilityPrecipitation의 rainEffectDay 계산 로직을 적용 + for(let idx = 0; idx < res.data.wqltObjs.length; idx++) { + const item = res.data.wqltObjs[idx]; + if (!item) continue; + + const rainDay = Number(item.infoRnDay) >= 3 ? 1 : 0; + let rainEffectDay = 0; + + if(idx < res.data.wqltObjs.length - 2) { + if(rainDay == 1) { + rainEffectDay = 1; + } else { + if(idx >= 2) { + const prevItem1 = res.data.wqltObjs[idx-1]; + const prevItem2 = res.data.wqltObjs[idx-2]; + if((prevItem1 && Number(prevItem1.infoRnDay) >= 3) || (prevItem2 && Number(prevItem2.infoRnDay) >= 3)) { + rainEffectDay = 1; + } + } + } + } else if(idx < res.data.wqltObjs.length - 1) { + const nextItem = res.data.wqltObjs[idx+1]; + if(rainDay == 1 || (nextItem && Number(nextItem.infoRnDay) >= 3)) { + rainEffectDay = 1; + } + } else { + rainEffectDay = rainDay; + } + + tmpData.push({ + ...item, + rainEffectDay: rainEffectDay, + } as FacilityWaterQltyProps); + } + + const rtnData = tmpData.map((item: FacilityWaterQltyProps) => { + dataFormSetting(item) + if(item.inflAssctAftWqltTn) { + item.inflAssctAftWqltTn = Number(Math.round(Number(item.inflAssctAftWqltTn) * 1000)/1000).toFixed(3); + } + if(item.efflWqltTn){ + item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3); + } + if(item.inflAssctBfrWqltTp){ + item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000)/1000).toFixed(3); + } + if(item.efflWqltTp){ + item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3); + } + if(item.inflAssctAftWqltToc){ + item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1); + } + if(item.efflWqltCod){ + item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1); + } + if(item.inflAssctAftWqltBod){ + item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1); + } + if(item.efflWqltBod){ + item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1); + } + if(item.inflAssctAftWqltSs){ + item.inflAssctAftWqltSs = Number(Math.round(Number(item.inflAssctAftWqltSs) * 10)/10).toFixed(1); + } + if(item.efflWqltSs){ + item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1); + } + if(item.inflAssctAftWqltToc){ + item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1); + } + if(item.efflWqltCod){ + item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1); + } + + if(item.tpInflWqltBod){ + item.tpInflWqltBod = item.tpInflWqltBod?Number(Math.round(Number(item.tpInflWqltBod) * 10)/10).toFixed(1):Number(item.tpInflWqltBod)===0?'0.0':''; + }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{ + item.tpInflWqltTp = ''; + } + return item; + }) + set((state: FacilityWaterQltyType) => ({ + ...state, + FacilityWaterQltyModal: { + ...state.FacilityWaterQltyModal, + facilityWaterQltyYearList: rtnData, + tmsList : rtnTmsList, + msrmtDate : rtnMsrmtDate + } + })) + }, + } +} \ No newline at end of file diff --git a/apps/fo/app/store/flowRate/index.tsx b/apps/fo/app/store/flowRate/index.tsx new file mode 100644 index 000000000..2ef192c0b --- /dev/null +++ b/apps/fo/app/store/flowRate/index.tsx @@ -0,0 +1,355 @@ +import { getFetch } from "@/app/components/api"; +import type { TableUpperProps } from "../dashboard"; +import DataForm from "@/app/components/common/DataForm"; + +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +const FlowRateSearchLabelArray:string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; + +export type flowRateListProps = { + bscFctCd : string, + sidoNm : string, + sggNm : string, + bscFctNm : string, + daddr : string, + fctCpc : number, + lclgvrnmntNm : string, + totInflAmnt : number | '', + inflAmnt : number | '', + sumFlow : number | '', + returnAmnt : number | '', + efflAmnt : number | '', + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, + infoRnDay : number | '', +} + +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 시도: string, + 시군구: string, + 시설이름: string, + 주소: string, + 용량: number, + 총유입량: number | '', + 하수유입량: number | '', + 연계처리량: number | '', + 총인반류수량: number | '', + 방류량: number | '', + 환경청:string|'', + +} + +/** + * (2) 모달 + */ +export interface FlowRateModalInterface { + msrDt : string, + totInflAmnt : number | '', + inflAmnt : number | '', + sumFlow : number | '', + returnAmnt : number | '', + efflAmnt : number | '', +} +export type FlowRateModalProps = { + msrDt : string, + totInflAmnt : number, + inflAmnt : number, + sumFlow : number, + returnAmnt : number, + efflAmnt : number, + infoRnDay : number | '', + rainEffectDay : number | '', +} + +/**==================================== + * Store - data + ====================================*/ + + /** + * (1) 메인페이지 + */ +// 리스트 (행정별) +const upHeadList1:TableUpperProps[] = [ + { id: "bscFctCd", title: "시설코드", row:0, rowSpan:2}, + { id: "A", title: "행정명", row: 0 ,colSpan: 2 }, + { id: "sidoNm", title: "시도", upName:"upChangeRe", row:1 }, + { id: "sggNm", title: "시군구", upName:"upChangeRe", row:1 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2}, + { id: "daddr", title: "주소", row: 0, rowSpan: 2}, + { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2}, + { id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2}, + { id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2}, + { id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2}, + { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2}, + { id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2} +]; + +// 리스트 (권역별) +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 }, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe", row:1 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2}, + { id: "daddr", title: "주소", row: 0, rowSpan: 2}, + { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2}, + { id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2}, + { id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2}, + { id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2}, + { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2}, + { id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2} +]; + +// 리스트 (환경청별별) +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 }, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", row:1 }, + { id: "rgnNm", title: "환경청", row:0, rowSpan:2 }, + { id: "B", title: "행정명", row: 0 ,colSpan: 2 }, + { id: "ctpvNm", title: "시도", upName:"upChangeRe2", row:1 }, + { id: "sggNm", title: "시군구", upName:"upChangeRe2", row:1 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2 }, + { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, + { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2 }, + { id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2 }, + { id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2 }, + { id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2 }, + { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2 }, + { id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2 } +]; + +const flowRateList:flowRateListProps[] = [ +] + +/** + * (2) 모달 + */ + +const upHeadListFlowRateModal:TableUpperProps[] = [ + { id: "msrDt", title: "날짜"}, + { id: "totInflAmnt", title: "총 유입량(m³/일)", align:"right"}, + { id: "inflAmnt", title: "하수유입량(m³/일)", align:"right"}, + { id: "sumFlow", title: "연계처리량(m³/일)", align:"right"}, + { id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"}, + { id: "efflAmnt", title: "방류량(m³/일)", align:"right"} +] + +const modalFlowRateListYear:FlowRateModalProps[] = [ +] + +/**==================================== + * Store - API + ====================================*/ +const fetchFlowRateList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + try { + 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(url); + return { data: res.data, count: res.count } + } catch (error) { + console.error('시설정보 유량현황 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } +} + +const fetchFlowRateModalList = async (bscFctCd: string, searchYear: string) => { + try { + const url = '/fclt/flow/' + bscFctCd + '/' + searchYear; + const res = await getFetch('fo', url); + + // msrDt 오름차순으로 정렬 + const sortedData = res.data.sort((a: FlowRateModalInterface, b: FlowRateModalInterface) => { + return new Date(a.msrDt).getTime() - new Date(b.msrDt).getTime(); + }); + + return { data: sortedData } + } catch (error) { + console.error('시설정보 유량현황 모달 데이터를 가져오는데 실패했습니다:', error); + return { data: [] } + } +} + + +/**==================================== + * Store - expaort + ====================================*/ + +export type FlowRateSearchActions = { + searchFlowRateList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; + + searchFlowRateModalList : (bscFctCd: string, searchYear: string) => void; +} + +export type FlowRateSearchType = { + FlowRateSearch: { + isInit: boolean; + flowRateSearchLabelArray:string[]; + }, + + FlowRateList : { + isInit : boolean; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + flowRateList:flowRateListProps[]; + flowRateCount: number; + excelList: ExcelType[]; + }, + + FlowRateModal : { + isInit : boolean; + modalFlowRateListYear : FlowRateModalProps[]; + upHeadListFlowRateModalYear : TableUpperProps[]; + } +} + +export type FlowRateSearchStore = FlowRateSearchType & FlowRateSearchActions; + +const dataFormSetting=(item : flowRateListProps | FlowRateModalProps)=> { + DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'totInflAmnt', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'inflAmnt', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'sumFlow', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'efflWqltBod', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'returnAmnt', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'efflAmnt', item: item as unknown as Record<string, unknown>}); +} + +export const flowRateSearchInitState:FlowRateSearchType = { + FlowRateSearch: { + isInit: false, + flowRateSearchLabelArray: FlowRateSearchLabelArray, + }, + FlowRateList : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + flowRateList : flowRateList, + flowRateCount : 0, + excelList: [] + }, + FlowRateModal : { + isInit : false, + upHeadListFlowRateModalYear : upHeadListFlowRateModal, + modalFlowRateListYear : modalFlowRateListYear, + } +} + +export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => FlowRateSearchType) => void)=>FlowRateSearchActions=(set: (fn: (state: FlowRateSearchType) => FlowRateSearchType) => void) => { + return { + searchFlowRateList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + const data = res.data; + const excelDownList: ExcelType[] = []; + + const rtnData = data.map((item: flowRateListProps) => { + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0) + dataFormSetting(item); + return item; + }); + + if(pageSize>5000){ + rtnData.forEach((item: flowRateListProps) => { + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시도: item.sidoNm, + 시군구: item.sggNm, + 시설이름: item.bscFctNm, + 주소: item.daddr, + 용량: item.fctCpc, + 총유입량: item.totInflAmnt, + 하수유입량: item.inflAmnt, + 연계처리량: item.sumFlow, + 총인반류수량: item.returnAmnt, + 방류량: item.efflAmnt, + }); + }); + } + + console.log('excelDownList' , excelDownList); + set((state: FlowRateSearchType) => ({ + ...state, + FlowRateList: pageSize<5000?{ + ...state.FlowRateList, + flowRateList: rtnData, + flowRateCount: res.count, + }:{ + ...state.FlowRateList, + excelList: excelDownList, + } + })) + }, + searchFlowRateModalList: async (bscFctCd: string, searchYear: string) => { + const res = await fetchFlowRateModalList(bscFctCd, searchYear); + const data = res.data; + + const tmpData:FlowRateModalProps[] = []; + // facilityPrecipitation의 rainEffectDay 계산 로직을 적용 + for(let idx = 0; idx < data.length; idx++) { + const item = data[idx]; + const rainDay = Number(item.infoRnDay) >= 3 ? 1 : 0; + let rainEffectDay = 0; + + if(idx < data.length - 2) { + if(rainDay == 1) { + rainEffectDay = 1; + } else { + if(idx >= 2) { + if(Number(data[idx-1].infoRnDay) >= 3 || Number(data[idx-2].infoRnDay) >= 3) { + rainEffectDay = 1; + } + } + } + } else if(idx < data.length - 1) { + if(rainDay == 1 || Number(data[idx+1].infoRnDay) >= 3) { + rainEffectDay = 1; + } + } else { + rainEffectDay = rainDay; + } + + tmpData.push({ + ...item, + rainEffectDay: rainEffectDay, + }); + } + + const rtnData = tmpData.map((item: FlowRateModalProps) => { + item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0) + dataFormSetting(item); + return item; + }); + + set((state: FlowRateSearchType) => ({ + ...state, + FlowRateModal: { + ...state.FlowRateModal, + modalFlowRateListYear: rtnData || [], + } + })) + } + } +} \ No newline at end of file diff --git a/apps/fo/app/store/index.tsx b/apps/fo/app/store/index.tsx new file mode 100644 index 000000000..bd684542b --- /dev/null +++ b/apps/fo/app/store/index.tsx @@ -0,0 +1,45 @@ +'use client' + +import { type ReactNode, createContext, useContext, useRef } from 'react' +import { useStore } from 'zustand' +import { type FoStore, StoreZus } from './store' + + +export type StoreApi = ReturnType<typeof StoreZus> + +export const StoreContext = createContext<StoreApi | undefined>( + undefined, +) + +export interface StoreProviderProps { + children: ReactNode +} + +export const StoreProvider = ({ + children, +}: StoreProviderProps) => { + const storeRef = useRef<StoreApi>() + + if (!storeRef.current) { + const store = StoreZus() + storeRef.current = store + } + + return ( + <StoreContext.Provider value={storeRef.current}> + {children} + </StoreContext.Provider> + ) +} + +export const useFoStore = <T,>( + selector: (store: FoStore) => T, +): T => { + const storeContext = useContext(StoreContext) + + if (!storeContext) { + throw new Error(`useStore must be used within StoreProvider`) + } + + return useStore(storeContext, selector) +} \ No newline at end of file diff --git a/apps/fo/app/store/planSewage/index.tsx b/apps/fo/app/store/planSewage/index.tsx new file mode 100644 index 000000000..e9153305a --- /dev/null +++ b/apps/fo/app/store/planSewage/index.tsx @@ -0,0 +1,226 @@ +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +import { getFetch } from "@/app/components/api"; +export type { TableUpperProps }; +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 시설별 강수현황 +type PlanSewageProps = { + bscFctCd : string, + sido : string, + gugun : string, + bscFctNm : string, + daddr : string, + fctCpc : number, + spareCapacity : number, + variableLoadPercent : number, + classifiedSewerPercent : number, + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, + sprtProcZoneAr : number, + procZoneSm : number, + avgInflAmnt : number, + avgInflAmntSunny : number, + maxInflAmntSunny : number, +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +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 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { 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: "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}, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1}, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { 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: "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}, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1}, + { id: "rgnNm", title: "환경청", row:0, rowSpan:2 }, + { id: "B", title: "행정구역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:2 }, + { id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 }, + { id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { 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: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' }, +]; + +const planSewageList:PlanSewageProps[] = [ +] + +/**==================================== + * Store - api + ====================================*/ + const fetchPlanSewageList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const tailStr = partCd ? '/'+ partCd + '/' + (nameCd || '0') + '/' + (fcltCpct || '0') + '/' + (operationValue || '0') +'/0'+ (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const url = '/prcsFclt/info/plan/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; + console.log(url); + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } + } + +/**==================================== + * Store - expaort + ====================================*/ +export type ExcelType = { + 시설코드: string; + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string; + 시군구: string; + 시설이름: string; + 주소: string; + 시설용량: number; + 여유용량: number; + 변동부하율 : number; + 분류식화율 : number; +} + +const dataFormSetting=(item : PlanSewageProps)=> { + DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'spareCapacity', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'variableLoadPercent', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'classifiedSewerPercent', item: item as unknown as Record<string, unknown>}); +}; + +export type PlanSewageActions = { + searchPlanSewageList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; +} + +export type PlanSewageType = { + PlanSewageSearch: { + isInit: boolean; + labelArray:string[]; + }, + + PlanSewage : { + isInit: boolean; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + planSewageList: PlanSewageProps[]; + planSewageCount: number; + excelList: ExcelType[]; + }, +} + +export const planSewageInitState: PlanSewageType = { + PlanSewageSearch: { + isInit: false, + labelArray: LabelArray, + }, + + PlanSewage : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + planSewageList : planSewageList, + planSewageCount : 0, + excelList : [] + }, +} + +export type PlanSewageStore = PlanSewageType & PlanSewageActions; + +export const planSewageReducer:(set:(fn: (state: PlanSewageType) => PlanSewageType) => void)=>PlanSewageActions=(set: (fn: (state: PlanSewageType) => PlanSewageType) => void) => { + return { + searchPlanSewageList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchPlanSewageList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + const excelDownList: ExcelType[] = []; + console.log('res' , res.data); + /** + * 결과데이터 계산 + * (1) 여유용량 = 시설용량 - 건기시 평균유량 + * (2) 변동부하율 = 건기시 일최대 유량/일평균유량(기준 최근 3개년) + * (3) 분류식화율 = 분류식지역 면적(㎢)/처리구역 면적합계(㎢) ×100 + */ + const resultData = res.data.map((item:PlanSewageProps) => { + item.spareCapacity = Number(item.fctCpc) - Number(item.avgInflAmntSunny); + item.variableLoadPercent = item.maxInflAmntSunny && item.maxInflAmntSunny!==0 ? Number(item.maxInflAmntSunny) / Number(item.avgInflAmntSunny) : 0; + item.classifiedSewerPercent = item.sprtProcZoneAr && item.sprtProcZoneAr!==0 ? Number(item.sprtProcZoneAr) / Number(item.procZoneSm) * 100 : 0; + return item; + }); + + resultData.forEach((item:PlanSewageProps) => { + dataFormSetting(item) + }) + + if(pageSize>5000){ + resultData.forEach((item:PlanSewageProps) => { + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.lgbsn, + 단위유역: item.rvbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시설이름: item.bscFctNm, + 시도: item.sido, + 시군구: item.gugun, + 주소: item.daddr, + 시설용량: item.fctCpc, + 여유용량: item.spareCapacity, + 변동부하율: item.variableLoadPercent, + 분류식화율: item.classifiedSewerPercent, + }) + }) + } + + set((state:PlanSewageType) => ({ + ...state, + PlanSewage: pageSize<5000?{ + ...state.PlanSewage, + planSewageList: resultData, + planSewageCount: res.count, + }:{ + ...state.PlanSewage, + excelList: excelDownList, + } + })) + } + } +} \ No newline at end of file diff --git a/apps/fo/app/store/processArea/index.tsx b/apps/fo/app/store/processArea/index.tsx new file mode 100644 index 000000000..a43777b1c --- /dev/null +++ b/apps/fo/app/store/processArea/index.tsx @@ -0,0 +1,507 @@ +import { getFetchAreaList, postFetch } from "@/app/components/api"; +import type { TableUpperProps } from "../dashboard"; +import DataForm from "@/app/components/common/DataForm"; +export type { TableUpperProps }; +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 처리구역 리스트 +export type ProcessAreaProps = { + swtrmtFcltCd : string, + sido : string, + gugun : string, + swtrmtFcltNm : string, + location : string, + fctCpc : number | '', + ruse : boolean, + yrDt4 : string, + yr : string, + yrDc1 : string, + yrDc2 : string, + yrDc3 : string, + yrDc4 : string, + /** + * 증설계획 + */ + // 기승인(당초4단계), 현재, 변경1~4 + planSwageQntSumDt4 : number | '', + planSwageQntSum : number | '', + planSwageQntSumDc1 : number | '', + planSwageQntSumDc2 : number | '', + planSwageQntSumDc3 : number | '', + planSwageQntSumDc4 : number | '', + /** + * 처리인구 + */ + // 기승인(당초4단계), 변경1~4 + swtrmtPpltnCntDt4 : number | '', + swtrmtPpltnCnt : number | '', + swtrmtPpltnCntDc1 : number | '', + swtrmtPpltnCntDc2 : number | '', + swtrmtPpltnCntDc3 : number | '', + swtrmtPpltnCntDc4 : number | '', + /** + * 처리구역면적 + */ + // 기승인(당초4단계), 변경1~4 + swtrmtZoneAreaDt4 : number | '', + swtrmtZoneArea : number | '', + swtrmtZoneAreaDc1 : number | '', + swtrmtZoneAreaDc2 : number | '', + swtrmtZoneAreaDc3 : number | '', + swtrmtZoneAreaDc4 : number | '', + // 처리구역 + trtDstr : string | '', +} + +export interface ExcelType { + 시설코드: string, + 시설명: string, + 주소: string, + 시설용량: number | '', + 증설계획기존: number | '', + 증설계획현재: number | '', + 증설계획1단계: number | '', + 증설계획2단계: number | '', + 증설계획3단계: number | '', + 증설계획4단계: number | '', + 처리인구기존: number | '', + 처리인구현재: number | '', + 처리인구1단계: number | '', + 처리인구2단계: number | '', + 처리인구3단계: number | '', + 처리인구4단계: number | '', + 처리구역기존: number | '', + 처리구역현재: number | '', + 처리구역1단계: number | '', + 처리구역2단계: number | '', + 처리구역3단계: number | '', + 처리구역4단계: number | '', + 처리구역: string | '' +} + +// (2) 처리구역 팝업 +// 시설증설 계획 +export type FcltyImproveInfoProps = { + planSwageQntSumDt4 : number | '', + planSwageQntSum : number | '', + planSwageQntSumDc1 : number | '', + planSwageQntSumDc2 : number | '', + planSwageQntSumDc3 : number | '', + planSwageQntSumDc4 : number | '', +} + +// 처리인구 계획 +export type ProcessPopulationProps = { + swtrmtPpltnCntDt4 : number | '', + swtrmtPpltnCnt : number | '', + swtrmtPpltnCntDc1 : number | '', + swtrmtPpltnCntDc2 : number | '', + swtrmtPpltnCntDc3 : number | '', + swtrmtPpltnCntDc4 : number | '', +} +// 처리구역 계획 +export type ProcessAreaPlanProps = { + swtrmtZoneAreaDt4 : number | '', + swtrmtZoneArea : number | '', + swtrmtZoneAreaDc1 : number | '', + swtrmtZoneAreaDc2 : number | '', + swtrmtZoneAreaDc3 : number | '', + swtrmtZoneAreaDc4 : number | '', + trtDstr : string, +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +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 }, + { id: "swtrmtFcltNm", title: "시설명", rowSpan:3, row:0}, + { id: "location", title: "주소", rowSpan:3, row:0, }, + { 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: "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: "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: "trtDstr", title: "처리구역", row:2} +]; + +const processAreaList:ProcessAreaProps[] = [ +] + +/** + * 처리구역정보 상세팝업 + */ +// 시설증설 계획 (헤더) +const upHeadListFcltyImproveInfo:TableUpperProps[] = [ + { id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan:2, row:0}, + { 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}, +]; + +// 시설증설 계획 (리스트) +const gridListFcltyImproveInfo:FcltyImproveInfoProps[] = [ +]; + +// 처리인구 계획 (헤더) +const upHeadListProcessPopulation:TableUpperProps[] = [ + { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan:2, row:0}, + { 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}, +]; + +// 처리인구 계획 (리스트) +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: "trtDstr", title: "처리구역", row:1} +]; + +// 시설증설 계획 (리스트) +const gridListProcessAreaPlan:ProcessAreaPlanProps[] = [ +]; + +/**==================================== + * Store - API +=====================================*/ +// 시설 상세 정보 조회 API +const fetchFacilityDetailInfo = async (swtrmtFcltCd: string) => { + try { + const tailUrl = '/1/5/A/0/0/0/SW'; + const res = await postFetch('fo', '/prcsFclt/ruseInfo'+tailUrl, [swtrmtFcltCd]); + return { data:res.data, count:res.count }; + } catch (error) { + console.error('시설정보 데이터를 가져오는데 실패했습니다:', error); + return {data: [], count: 0}; + } +}; + +// 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) +const fetchProcessAreaList = async (pageNo : number, pageSize : number, ctpvNm : string | undefined, sggNm : string | undefined, targetYear : string | undefined ): Promise<{data:ProcessAreaProps[], count:number}> => { + try { + targetYear = targetYear ? targetYear : "2024"; + const baseUrl = 'https://apigateway.rsonesoft.com/dc-service/api/v1/nowDmgMtncGnsvy'; + const queryParams = new URLSearchParams(); + + if (ctpvNm && ctpvNm !== '전체') queryParams.append('ctpvNm', ctpvNm); + if (sggNm && sggNm !== '전체') queryParams.append('sggNm', sggNm); + if (targetYear) queryParams.append('targetYear',targetYear); + + const queryString = queryParams.toString(); + const url = queryString ? `${baseUrl}?${queryString}` : baseUrl; + const res = await getFetchAreaList(url); + + if(res && res.length > 0){ + const enrichedData = await Promise.all( + res.map(async (item: ProcessAreaProps) => { + upHeadList1.forEach((head:TableUpperProps) => { + if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = '현재\n(' + item.yr + '년)'; + } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = '1단계\n(' + item.yrDc1 + '년)'; + } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = '2단계\n(' + item.yrDc2 + '년)'; + } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = '3단계\n(' + item.yrDc3 + '년)'; + } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = '4단계\n(' + item.yrDc4 + '년)'; + } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = '기승인\n4단계(' + item.yrDt4 + '년)'; + } + }); + const res = await fetchFacilityDetailInfo(item.swtrmtFcltCd); + const detailInfo = res.data.filter((item:ProcessAreaProps) => item.ruse === true); + return { + ...item, + sido: detailInfo.length > 0 ? detailInfo[0].sido||'' : '', + sigungo: detailInfo.length > 0 ? detailInfo[0].gugun||'' : '', + location: detailInfo.length > 0 ? detailInfo[0].addr||'' : '', + fctCpc: detailInfo.length > 0 ? detailInfo[0].fctCpc||'' : 0, + trtDstr: detailInfo.length > 0 ? detailInfo[0].trtDstr||'' : '', + }; + }) + ); + const startIndex = (pageNo - 1) * pageSize; + const endIndex = startIndex + pageSize; + const paginatedData = enrichedData.slice(startIndex, endIndex); + return {data: paginatedData, count: enrichedData.length}; + }else{ + return {data: [], count: 0}; + } + } catch (error) { + console.error('시설계획정보를 가져오는데 실패했습니다:', error); + return {data: [], count: 0}; + } +}; + +/**==================================== + * Store - expaort +=====================================*/ +export type ProcessAreaActions = { + searchProcessAreaList : (pageNo: number, pageSize: number, ctpvNm?:string, sggNm?:string, targetYear?:string) => void; + initModalData : (swtrmtFcltCd:string) => void; +} + +export type ProcessAreaType = { + ProcessAreaSearch: { + isInit: boolean; + processFacilityLabelArray:string[]; + }, + + ProcessArea : { + isInit: boolean; + upHeadList1: TableUpperProps[]; + processAreaList: ProcessAreaProps[]; + processAreaCount: number; + excelList: ExcelType[]; + }, + + ProcessAreaDetail : { + isInit: boolean; + upHeadListFcltyImproveInfo: TableUpperProps[]; + gridListFcltyImproveInfo: FcltyImproveInfoProps[]; // Changed from facilityAreaProps to ProcessAreaProps + upHeadListProcessPopulation: TableUpperProps[]; + gridListProcessPopulation: ProcessPopulationProps[]; // Changed from facilityAreaProps to ProcessAreaProps + upHeadListProcessAreaPlan: TableUpperProps[]; + gridListProcessAreaPlan: ProcessAreaPlanProps[]; // Changed from facilityAreaProps to ProcessAreaProps + }, +} + +export const processAreaInitState: ProcessAreaType = { + ProcessAreaSearch: { + isInit: false, + processFacilityLabelArray: ProcessFacilityLabelArray, + }, + + ProcessArea : { + isInit : false, + upHeadList1 : upHeadList1, + processAreaList : processAreaList, + processAreaCount : 0, + excelList : [] + }, + + ProcessAreaDetail : { + isInit : false, + upHeadListFcltyImproveInfo : upHeadListFcltyImproveInfo, + gridListFcltyImproveInfo : gridListFcltyImproveInfo, + upHeadListProcessPopulation : upHeadListProcessPopulation, + gridListProcessPopulation : gridListProcessPopulation, + upHeadListProcessAreaPlan : upHeadListProcessAreaPlan, + gridListProcessAreaPlan : gridListProcessAreaPlan, + }, +} + +// 상태 설정 함수 타입 정의 +type SetStateFunction = (state: ProcessAreaType | ((state: ProcessAreaType) => ProcessAreaType)) => void; + +export type ProcessAreaStore = ProcessAreaType & ProcessAreaActions; + +export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: SetStateFunction) => { + return { + /** + * 시설정보 리스트 데이터 호출 + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + */ + searchProcessAreaList: async ( pageNo:number, pageSize:number, ctpvNm?:string, sggNm?:string, targetYear?:string) => { + const res = await fetchProcessAreaList(pageNo, pageSize, ctpvNm, sggNm, targetYear); + res.data.forEach((item:ProcessAreaProps) => { + DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>}); + }) + const excelDownList: ExcelType[] = []; + if(pageSize>5000){ + res.data.forEach(item => { + item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; + excelDownList.push({ + 시설코드: item.swtrmtFcltCd, + 시설명: item.swtrmtFcltNm, + 주소: item.location, + 시설용량: item.fctCpc, + 증설계획기존: item.planSwageQntSumDt4, + 증설계획현재: item.planSwageQntSum, + 증설계획1단계: item.planSwageQntSumDc1, + 증설계획2단계: item.planSwageQntSumDc2, + 증설계획3단계: item.planSwageQntSumDc3, + 증설계획4단계: item.planSwageQntSumDc4, + 처리인구기존: item.swtrmtPpltnCntDt4, + 처리인구현재: item.swtrmtPpltnCnt, + 처리인구1단계: item.swtrmtPpltnCntDc1, + 처리인구2단계: item.swtrmtPpltnCntDc2, + 처리인구3단계: item.swtrmtPpltnCntDc3, + 처리인구4단계: item.swtrmtPpltnCntDc4, + 처리구역기존: item.swtrmtZoneAreaDt4, + 처리구역현재: item.swtrmtZoneArea, + 처리구역1단계: item.swtrmtZoneAreaDc1, + 처리구역2단계: item.swtrmtZoneAreaDc2, + 처리구역3단계: item.swtrmtZoneAreaDc3, + 처리구역4단계: item.swtrmtZoneAreaDc4, + 처리구역: item.trtDstr + }); + }); + }else{ + res.data.forEach(item => { + item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; + }) + } + set((state:ProcessAreaType) => ({ + ...state, + ProcessArea: pageSize<5000?{ + ...state.ProcessArea, + processAreaList: res.data, + processAreaCount: res.count, + }:{ + ...state.ProcessArea, + excelList: excelDownList, + }, + })) + }, + + initModalData: async (swtrmtFcltCd:string) => { + 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 = '현재\n(' + rtnData.yr + '년)'; + } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = '1단계\n(' + rtnData.yrDc1 + '년)'; + } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = '2단계\n(' + rtnData.yrDc2 + '년)'; + } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = '3단계\n(' + rtnData.yrDc3 + '년)'; + } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = '4단계\n(' + rtnData.yrDc4 + '년)'; + } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)'; + } + }); + + upHeadListProcessAreaPlan.forEach((head:TableUpperProps) => { + if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = '현재\n(' + rtnData.yr + '년)'; + } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = '1단계\n(' + rtnData.yrDc1 + '년)'; + } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = '2단계\n(' + rtnData.yrDc2 + '년)'; + } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = '3단계\n(' + rtnData.yrDc3 + '년)'; + } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = '4단계\n(' + rtnData.yrDc4 + '년)'; + } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)'; + } + }); + + upHeadListProcessPopulation.forEach((head:TableUpperProps) => { + if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = '현재\n(' + rtnData.yr + '년)'; + } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = '1단계\n(' + rtnData.yrDc1 + '년)'; + } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = '2단계\n(' + rtnData.yrDc2 + '년)'; + } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = '3단계\n(' + rtnData.yrDc3 + '년)'; + } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = '4단계\n(' + rtnData.yrDc4 + '년)'; + } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)'; + } + }); + + // 시설증설 계획 데이터 생성 + const fcltyImproveInfo: FcltyImproveInfoProps = { + planSwageQntSumDt4: rtnData.planSwageQntSumDt4, + planSwageQntSum: rtnData.planSwageQntSum, + planSwageQntSumDc1: rtnData.planSwageQntSumDc1, + planSwageQntSumDc2: rtnData.planSwageQntSumDc2, + planSwageQntSumDc3: rtnData.planSwageQntSumDc3, + planSwageQntSumDc4: rtnData.planSwageQntSumDc4 + }; + + // 처리인구 계획 데이터 생성 + const processPopulation: ProcessPopulationProps = { + swtrmtPpltnCntDt4: rtnData.swtrmtPpltnCntDt4, + swtrmtPpltnCnt: rtnData.swtrmtPpltnCnt, + swtrmtPpltnCntDc1: rtnData.swtrmtPpltnCntDc1, + swtrmtPpltnCntDc2: rtnData.swtrmtPpltnCntDc2, + swtrmtPpltnCntDc3: rtnData.swtrmtPpltnCntDc3, + swtrmtPpltnCntDc4: rtnData.swtrmtPpltnCntDc4 + }; + + // 처리구역 계획 데이터 생성 + const processAreaPlan: ProcessAreaPlanProps = { + swtrmtZoneAreaDt4: rtnData.swtrmtZoneAreaDt4, + swtrmtZoneArea: rtnData.swtrmtZoneArea, + swtrmtZoneAreaDc1: rtnData.swtrmtZoneAreaDc1, + swtrmtZoneAreaDc2: rtnData.swtrmtZoneAreaDc2, + swtrmtZoneAreaDc3: rtnData.swtrmtZoneAreaDc3, + swtrmtZoneAreaDc4: rtnData.swtrmtZoneAreaDc4, + trtDstr: rtnData.trtDstr + }; + + return { + ...state, + ProcessAreaDetail: { + ...state.ProcessAreaDetail, + gridListFcltyImproveInfo: [fcltyImproveInfo], + gridListProcessPopulation: [processPopulation], + gridListProcessAreaPlan: [processAreaPlan] + } + }; + } + return state; + }); + } + } +} \ No newline at end of file diff --git a/apps/fo/app/store/processFacility/index.tsx b/apps/fo/app/store/processFacility/index.tsx new file mode 100644 index 000000000..a8c04930d --- /dev/null +++ b/apps/fo/app/store/processFacility/index.tsx @@ -0,0 +1,832 @@ +import { getFetch } from "@/app/components/api"; +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +export type { TableUpperProps }; + +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 처리시설 리스트 +export type facilityListProps = { + bscFctCd : string, + bscFctNm : string, + bscFctClsNm : string, + sido : string, + gugun : string, + daddr : string, + fctCpc : number | string | '', + plInflWqltBod : number | string | '', + dsInflWqltBod : number | string | '', + trtFrfNm : string, + rnDt : string, + cmplDt : string, + trstOprEtpNm : string, + rvbsn : string, + mdmbsn : string, + lgbsn : string, + rgnNm : string, + mnstr : string, + efflWtrrgn : string, + efflWqltStndrdNm : string +} + +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string, + 시군구: string, + 시설이름: string, + 주소: string, + 시설용량: number | '', + 계획유입수질: number | '', + 설계유입수질: number | '', + 공법: string, + 가동개시일: string, + 준공일: string, + 관리대행업자: string, +} + +// (2) 처리시설 팝업 +// 시설 기본정보 +export type BasicInfoProps = { + bscFctNm : string, + fctCpc : number | '', + sido : string, + gugun : string, + startDay : string, + addr : string, + cmplDt : string, + trtFrfNm : string, + trstOprEtpNm : string, +} + +// 시설 계획정보 +export type PlanInfoProps = { + plInflWqltBod? : string, + plInflWqltToc? : string, + plInflWqltCod? : string, + plInflWqltSs? : string, + plInflWqltTn? : string, + plInflWqltTp? : string, + dsInflWqltBod? : string, + dsInflWqltToc? : string, + dsInflWqltCod? : string, + dsInflWqltSs? : string, + dsInflWqltTn? : string, + dsInflWqltTp? : string, + sitArea? : string | number, + trtDstrArea? : string | number, + swrTrtPopl? : string | number +} + +// 시설 계획정보 +export type LegalWaterQltyProps = { + totClfgrpcnt : number, + bod : number, + cod : number, + ss : number, + tn : number, + tp : number +} + +// 시설 수역 및 구역정보 +export type fcltyAreaInfoProps = { + rvbsn : string, + efflWtrrgn : string, + lgbsn : string, + efflWqltStndrdNm : string, + rgnNm : string, +} + +// 시설 이력관리 +export type fcltyHistInfoProps = { + extnsSeqno : string, + extnsNm : string, + bzStrtYrMnth : string, + bzEdYrMnth : string, + eqpPrmsDt : string, + cmplDt : string, + trlrnCmplDt : string, + rnDt : string, + sitArea : number, + trtDstrArea : number, + swrTrtPopl : number +} + +// 시설 수질정보 +export type fcltyWaterQltyInfoProps = { + extnsSeqno : string, + plInflWqltBod : string, + plInflWqltToc : string, + plInflWqltCod : string, + plInflWqltSs : string, + plInflWqltTn : string, + plInflWqltTp : string, + dsInflWqltBod : string, + dsInflWqltToc : string, + dsInflWqltCod : string, + dsInflWqltSs : string, + dsInflWqltTn : string, + dsInflWqltTp : string, + plEfflWqltBod : string, + plEfflWqltToc : string, + plEfflWqltCod : string, + plEfflWqltSs : string, + plEfflWqltTn : string, + plEfflWqltTp : string, + bod : number, + cod : number, + ss : number, + tn : number, + tp : number, + totClfgrpcnt : number, +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +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: "시설명"}, + { id: "daddr", title: "주소"}, + { id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'}, + { id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:"right"}, + { id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:"right"}, + { id: "trtFrfNm", title: '공법'}, + { id: "rnDt", title: '가동개시일'}, + { id: "cmplDt", title: '준공일' }, + { id: "trstOprEtpNm", title: '관리대행업자'} +]; + +// 처리시설 리스트 (유역) +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}, + { id: "bscFctNm", title: "시설명"}, + { id: "daddr", title: "주소"}, + { id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'}, + { id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:'right'}, + { id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:'right'}, + { id: "trtFrfNm", title: '공법'}, + { id: "rnDt", title: '가동개시일'}, + { id: "cmplDt", title: '준공일' }, + { id: "trstOprEtpNm", title: '관리대행업자'} +]; + +// 처리시설 리스트 (환경청청) +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}, + { id: "rgnNm", title: "환경청"}, + { id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1 }, + { id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2 }, + { id: "bscFctNm", title: "시설명"}, + { id: "daddr", title: "주소"}, + { id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'}, + { id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:'right'}, + { id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:'right'}, + { id: "trtFrfNm", title: '공법'}, + { id: "rnDt", title: '가동개시일'}, + { id: "cmplDt", title: '준공일' }, + { id: "trstOprEtpNm", title: '관리대행업자'} +]; + +const processFacilityList:facilityListProps[] = [ +] + +/** + * 처리시설 상세팝업 + */ +// 시설 기본정보 (헤더) +const upHeadListBasicInfo:TableUpperProps[] = [ + { id: "bscFctNm", title: "시설명" }, + { id: "fctCpc", title: "시설용량n(m³/일)", align: 'right'}, + { id: "sido", title: "시도"}, + { id: "gugun", title: "시군구"}, + { id: "rnDt", title: "가동개시일"}, + { id: "daddr", title: "주소"}, + { id: "cmplDt", title: "준공일"}, + { id: "trtFrfNm", title: "공법"}, + { id: "trstOprEtpNm", title: "관리대행업자"}, +]; + +// 시설 기본정보 (리스트) +const gridListBasicInfo:BasicInfoProps[] = [ + { + bscFctNm : "난지", + fctCpc : 860000, + sido : "서울특별시", + gugun : "", + startDay : "1987-06-30", + addr : "경기도 고양시 덕양구 대덕로 426 (현천동)", + cmplDt : "2013-04-30", + trtFrfNm : "MLE, A2O", + trstOprEtpNm : "자체", + } +]; + +// 시설 법적방류수질 (헤더) +const upHeadListLegalWaterQlty:TableUpperProps[] = [ + { id: "bod", title: "BOD(mg/L)", align: 'right'}, + { id: "cod", title: "TOC(COD)(mg/L)", align: 'right'}, + { id: "ss", title: "SS(mg/L)", align: 'right'}, + { id: "tn", title: "T-N(mg/L)", align: 'right'}, + { id: "tp", title: "T-P(mg/L) ", align: 'right'}, + { id: "totClfgrpcnt", title: "대장균군(개/100ml)"}, +] + +// 시설 법적방류수질 (리스트) +let gridListLegalWaterQlty:LegalWaterQltyProps[] = [ +] + +// 시설 계획정보 (헤더) +const upHeadListPlanInfo:TableUpperProps[] = [ + { id: "plInflWqltBod", title: "BOD", upName:"upChangeRe1", upSequnce:1, align: 'right' }, + { id: "plInflWqltToc", title: "TOC", upName:"upChangeRe1", upSequnce:2, align: 'right' }, + { id: "plInflWqltCod", title: "COD", upName:"upChangeRe1", upSequnce:3, align: 'right' }, + { id: "plInflWqltSs", title: "SS", upName:"upChangeRe1", upSequnce:4, align: 'right' }, + { id: "plInflWqltTn", title: "T-N", upName:"upChangeRe1", upSequnce:5, align: 'right' }, + { id: "plInflWqltTp", title: "T-P", upName:"upChangeRe1", upSequnce:6, align: 'right' }, + { id: "dsInflWqltBod", title: "BOD", upName:"upChangeRe2", upSequnce:1, align: 'right' }, + { id: "dsInflWqltToc", title: "TOC", upName:"upChangeRe2", upSequnce:2, align: 'right' }, + { id: "dsInflWqltCod", title: "COD", upName:"upChangeRe2", upSequnce:3, align: 'right' }, + { id: "dsInflWqltSs", title: "SS", upName:"upChangeRe2", upSequnce:4, align: 'right' }, + { id: "dsInflWqltTn", title: "T-N", upName:"upChangeRe2", upSequnce:5, align: 'right' }, + { id: "dsInflWqltTp", title: "T-P", upName:"upChangeRe2", upSequnce:6, align: 'right' }, + { id: "sitArea", title: "부지면적\n(m²)", align: 'right'}, + { id: "trtDstrArea", title: "처리구역면적\n(ha)", align: 'right'}, + { id: "swrTrtPopl", title: "하수처리인구\n(인)", align: 'right'}, +]; + +// 시설 계획정보 (리스트) +const gridListPlanInfo:PlanInfoProps[] = [ + { + plInflWqltBod : "179.0", + plInflWqltToc : "90.0", + plInflWqltCod : "0.0", + plInflWqltSs : "132.0", + plInflWqltTn : "38.000", + plInflWqltTp : "4.300", + dsInflWqltBod : "178.0", + dsInflWqltToc : "91.0", + dsInflWqltCod : "0.0", + dsInflWqltSs : "131.0", + dsInflWqltTn : "40.200", + dsInflWqltTp : "4.500", + sitArea : "928,574", + trtDstrArea : "7,994.0", + swrTrtPopl : "1,573,277", + } +]; + +// 시설 수역 및 구역정보 (헤더) +const upHeadListFcltyAreaInfo:TableUpperProps[] = [ + { id: "rvbsn", title: "하천명" }, + { id: "efflWtrrgn", title: "방류수역"}, + { id: "lgbsn", title: "단위유역"}, + { id: "efflWqltStndrdNm", title: "지역구분"}, + { id: "rgnNm", title: "관할 유역(지방) 환경청"} +]; + +// 시설 수역 및 구역정보 (리스트) +const gridListFcltyAreaInfo:fcltyAreaInfoProps[] = [ + { + rvbsn : "한강", + efflWtrrgn : "한강수계", + lgbsn : "한강본류", + efflWqltStndrdNm : "500톤 이상\n(Ⅲ지역)", + rgnNm : "한강유역환경청" + } +]; + +// 시설 이력관리 (헤더) +const upHeadListFcltyHistInfo:TableUpperProps[] = [ + { id: "extnsSeqno", title: "차수", align: 'center' }, + { id: "extnsNm", title: "구분"}, + { id: "bzStrtYrMnth", title: "사업기간", upName:"upChangeRe1", upSequnce:1}, + { id: "bzEdYrMnth", title: "사업기간", upName:"upChangeRe1", upSequnce:2}, + { id: "eqpPrmsDt", title: "설치인가일"}, + { id: "cmplDt", title: "준공일"}, + { id: "trlrnCmplDt", title: "시운전완료일"}, + { id: "rnDt", title: "가동개시일"}, +]; + +// 시설 이력관리 (리스트) +const gridListFcltyHistInfo:fcltyHistInfoProps[] = [ + { + extnsSeqno : "최초", + extnsNm : "최초", + bzStrtYrMnth : "1984.12.01", + bzEdYrMnth : "1987.06.01", + eqpPrmsDt : "1985.09.13", + cmplDt : "1987.06.30", + trlrnCmplDt : "1987.06.30", + rnDt : "1987.06.30", + sitArea : 0, + trtDstrArea : 0, + swrTrtPopl : 0, + } +]; + +// 시설 수질정보 (헤더) +const upHeadListFcltyWaterQltyInfo:TableUpperProps[] = [ + { id: "extnsSeqno", title: "차수" }, + { id: "plInflWqltBod", title: "BOD", upName:"upChangeRe1", upSequnce:1, align: 'right' }, + { id: "plInflWqltToc", title: "TOC", upName:"upChangeRe1", upSequnce:2, align: 'right' }, + { id: "plInflWqltCod", title: "COD", upName:"upChangeRe1", upSequnce:3, align: 'right' }, + { id: "plInflWqltSs", title: "SS", upName:"upChangeRe1", upSequnce:4, align: 'right' }, + { id: "plInflWqltTn", title: "T-N", upName:"upChangeRe1", upSequnce:5, align: 'right' }, + { id: "plInflWqltTp", title: "T-P", upName:"upChangeRe1", upSequnce:6, align: 'right' }, + { id: "dsInflWqltBod", title: "BOD", upName:"upChangeRe2", upSequnce:1, align: 'right' }, + { id: "dsInflWqltToc", title: "TOC", upName:"upChangeRe2", upSequnce:2, align: 'right' }, + { id: "dsInflWqltCod", title: "COD", upName:"upChangeRe2", upSequnce:3, align: 'right' }, + { id: "dsInflWqltSs", title: "SS", upName:"upChangeRe2", upSequnce:4, align: 'right' }, + { id: "dsInflWqltTn", title: "T-N", upName:"upChangeRe2", upSequnce:5, align: 'right' }, + { id: "dsInflWqltTp", title: "T-P", upName:"upChangeRe2", upSequnce:6, align: 'right' }, + { id: "plEfflWqltBod", title: "BOD", upName:"upChangeRe3", upSequnce:1, align: 'right' }, + { id: "plEfflWqltToc", title: "TOC", upName:"upChangeRe3", upSequnce:2, align: 'right' }, + { id: "plEfflWqltCod", title: "COD", upName:"upChangeRe3", upSequnce:3, align: 'right' }, + { id: "plEfflWqltSs", title: "SS", upName:"upChangeRe3", upSequnce:4, align: 'right' }, + { id: "plEfflWqltTn", title: "T-N", upName:"upChangeRe3", upSequnce:5, align: 'right' }, + { id: "plEfflWqltTp", title: "T-P", upName:"upChangeRe3", upSequnce:6, align: 'right' }, +]; + +// 시설 수질정보 (리스트) +const gridListFcltyWaterQltyInfo:fcltyWaterQltyInfoProps[] = [ + { + extnsSeqno: "최초", + plInflWqltBod: "111.0", + plInflWqltToc: "(0.0)", + plInflWqltCod: "(0.0)", + plInflWqltSs: "122.0", + plInflWqltTn: "0.000", + plInflWqltTp: "0.000", + dsInflWqltBod: "111.0", + dsInflWqltToc: "(0.0)", + dsInflWqltCod: "(0.0)", + dsInflWqltSs: "122.0", + dsInflWqltTn: "0.000", + dsInflWqltTp: "0.000", + plEfflWqltBod: "20.0", + plEfflWqltCod: "(0.0)", + plEfflWqltToc: "(0.0)", + plEfflWqltSs: "12.0", + plEfflWqltTn: "0.000", + plEfflWqltTp: "0.000", + bod: 0, + cod: 0, + ss: 0, + tn: 0, + tp: 0, + totClfgrpcnt: 0 + } +]; + +/**==================================== + * Store - api (데이터 호출) + ====================================*/ + +// 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) +const fetchFacilityList = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => { + try { + const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : ''; + const url = '/prcsFclt/info/history/'+pageNo+'/'+pageSize+tailStr; + //const res = await getFetch('fo', '/prcsFclt/info/'+pageNo+'/'+pageSize+tailUrl); + const res = await getFetch('fo', url); + return res; + } catch (error) { + console.error('시설정보 데이터를 가져오는데 실패했습니다:', error); + return {data: [], count: 0}; + } +}; + +// 시설정보 리스트 데이터 호출 (대시보드용) +const fetchFacilityListSearching = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => { + try { + const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : ''; + const url = '/prcsFclt/searching/'+pageNo+'/'+pageSize+tailStr; + //const res = await getFetch('fo', '/prcsFclt/info/'+pageNo+'/'+pageSize+tailUrl); + const res = await getFetch('fo', url); + return res; + } catch (error) { + console.error('시설정보 데이터를 가져오는데 실패했습니다:', error); + return {data: [], count: 0}; + } +}; + +// 모달 +// 시설정보 이력정보 데이터 호출 +const fetchFacilityHistInfo = async (facilityCd:string ): Promise<fcltyHistInfoProps[]> => { + try { + const res = await getFetch('fo', '/prcsFclt/hist/'+facilityCd); + const data = res.data; + DataForm({ type: 'date', column: 'trlrnCmplDt', item: data as unknown as Record<string, unknown> }); + DataForm({ type: 'date', column: 'eqpPrmsDt', item: data as unknown as Record<string, unknown> }); + DataForm({ type: 'date', column: 'cmplDt', item: data as unknown as Record<string, unknown> }); + DataForm({ type: 'date', column: 'rnDt', item: data as unknown as Record<string, unknown> }); + DataForm({ type: 'number', column: 'sitArea', item: data as unknown as Record<string, unknown> }); + DataForm({ type: 'number', column: 'trtDstrArea', item: data as unknown as Record<string, unknown> }); + DataForm({ type: 'number', column: 'swrTrtPopl', item: data as unknown as Record<string, unknown> }); + return data; + } catch (error) { + console.error('시설정보 이력정보 데이터를 가져오는데 실패했습니다:', error); + return []; + } +}; + +// 시설정보 수질정보 데이터 호출 +const fetchFacilityWaterQltyInfo = async (facilityCd:string ): Promise<fcltyWaterQltyInfoProps[]> => { + try { + const res = await getFetch('fo', '/prcsFclt/info/history/'+facilityCd); + const data = res.data; + return data; + } catch (error) { + console.error('시설정보 수질정보 데이터를 가져오는데 실패했습니다:', error); + return []; + } +}; + +/**==================================== + * Store - expaort + ====================================*/ +export type ProcessFacilityActions = { + /** + * 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + * @param partValue : 구분값(행정명, 대유역, 환경청) + * @param nameCd : 행정코드(시도명, 시군구명, 단위유역) + * @param fcltCpct : 시설용량 + * @param oprtngSttsCd : 운영 상태 코드 + * @param facilityName : 시설명 + */ + searchProcessFacility : (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => void; + + /** + * 시설정보 이력정보 데이터 호출 + * @param facilityCd : 시설코드 + */ + searchProcessFacilityHist : (facilityCd:string) => void; + + /** + * 시설정보 수질정보 데이터 호출 + * @param facilityCd : 시설코드 + */ + searchProcessFacilityWaterQlty : (facilityCd:string) => void; + + /** + * 시설정보 리스트 데이터 호출 (대시보드용) + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + */ + searchProcessFacilitySearching : (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => void; +} + +export type ProcessFacilityType = { + ProcessFacility: { + isInit: boolean; + processFacilityLabelArray:string[]; + }, + + ProcessFacilityList : { + isInit: boolean; + upHeadList1:TableUpperProps[]; + upHeadList2:TableUpperProps[]; + upHeadList3:TableUpperProps[]; + processFacilityList:facilityListProps[]; + processFacilityCount:number; + excelList : ExcelType[]; + }, + + ProcessFacilityDetail : { + isInit : boolean; + upHeadListBasicInfo : TableUpperProps[]; + gridListBasicInfo : BasicInfoProps[]; + upHeadListPlanInfo : TableUpperProps[]; + gridListPlanInfo : PlanInfoProps[]; + upHeadListFcltyAreaInfo : TableUpperProps[]; + gridListFcltyAreaInfo : fcltyAreaInfoProps[]; + + upHeadListFcltyHistInfo : TableUpperProps[]; + gridListFcltyHistInfo : fcltyHistInfoProps[]; + upHeadListFcltyWaterQltyInfo : TableUpperProps[]; + gridListFcltyWaterQltyInfo : fcltyWaterQltyInfoProps[]; + + upHeadListLegalWaterQlty : TableUpperProps[]; + gridListLegalWaterQlty : LegalWaterQltyProps[]; + }, +} + +export const processFacilityInitState:ProcessFacilityType = { + ProcessFacility: { + isInit: false, + processFacilityLabelArray: ProcessFacilityLabelArray, + }, + + ProcessFacilityList : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + processFacilityList : processFacilityList, + processFacilityCount:1, + excelList : [] + }, + + ProcessFacilityDetail : { + isInit : false, + upHeadListBasicInfo : upHeadListBasicInfo, + gridListBasicInfo : gridListBasicInfo, + upHeadListPlanInfo : upHeadListPlanInfo, + gridListPlanInfo : gridListPlanInfo, + upHeadListFcltyAreaInfo : upHeadListFcltyAreaInfo, + gridListFcltyAreaInfo : gridListFcltyAreaInfo, + + upHeadListFcltyHistInfo : upHeadListFcltyHistInfo, + gridListFcltyHistInfo : gridListFcltyHistInfo, + upHeadListFcltyWaterQltyInfo : upHeadListFcltyWaterQltyInfo, + gridListFcltyWaterQltyInfo : gridListFcltyWaterQltyInfo, + + upHeadListLegalWaterQlty : upHeadListLegalWaterQlty, + gridListLegalWaterQlty : gridListLegalWaterQlty, + } +} + +// 상태 설정 함수 타입 정의 +type SetStateFunction = (state: ProcessFacilityType | ((state: ProcessFacilityType) => ProcessFacilityType)) => void; + +export type ProcessFacilityStore = ProcessFacilityType & ProcessFacilityActions; + +export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActions=(set: SetStateFunction) => { + return{ + // 메인페이지 + + /** + * 시설정보 리스트 데이터 호출 + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + */ + searchProcessFacility: async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => { + const res = await fetchFacilityList(pageNo, pageSize, partValue, nameCd, fcltCpct, oprtngSttsCd, facilityPartValue, facilityName); + res.data.forEach((item:facilityListProps) => { + DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record<string, unknown>}); + DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>}); + }) + + res.data.forEach(item => { + item.plInflWqltBod = item.plInflWqltBod ? Number(item.plInflWqltBod).toFixed(1) : ''; + item.dsInflWqltBod = item.dsInflWqltBod ? Number(item.dsInflWqltBod).toFixed(1) : ''; + }) + + const excelDownList: ExcelType[] = []; + + if(pageSize>5000){ + res.data.forEach(item => { + item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시도: item.sido, + 시군구: item.gugun, + 시설이름: item.bscFctNm, + 주소: item.daddr, + 시설용량: item.fctCpc ? Number(item.fctCpc) : '', + 계획유입수질: item.plInflWqltBod ? Number(item.plInflWqltBod) : '', + 설계유입수질: item.dsInflWqltBod ? Number(item.dsInflWqltBod) : '', + 공법: item.trtFrfNm, + 가동개시일: item.rnDt, + 준공일: item.cmplDt, + 관리대행업자: item.mnstr, + }) + }) + }else{ + res.data.forEach(item => { + item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; + }) + } + set((state:ProcessFacilityType) => ({ + ...state, + ProcessFacilityList: pageSize<5000?{ + ...state.ProcessFacilityList, + processFacilityList: res.data, + processFacilityCount: res.count, + }:{ + ...state.ProcessFacilityList, + excelList: excelDownList, + }, + })) + }, + + /** + * 시설정보 리스트 데이터 호출 + * @param pageNo : 페이지 번호 + * @param pageSize : 페이지 크기 + */ + searchProcessFacilitySearching: async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => { + const res = await fetchFacilityListSearching(pageNo, pageSize, partValue, nameCd, fcltCpct, oprtngSttsCd, facilityPartValue, facilityName); + /* res.data.forEach((item:facilityListProps) => { + DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record<string, unknown>}); + DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record<string, unknown>}); + }) + + res.data.forEach(item => { + 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: { + ...state.ProcessFacilityList, + processFacilityList: res.data, + processFacilityCount: res.count, + }, + })) + }, + + + // 모달 이력정보 + /** + * 시설정보 이력정보 데이터 호출 + * @param facilityCd : 시설코드 + */ + searchProcessFacilityHist: async (facilityCd:string) => { + const promiseSet = await Promise.all([fetchFacilityHistInfo(facilityCd)]); + const [facilityHistInfo] = promiseSet; + // extnsSeqno를 기준으로 오름차순 정렬 + const sortedHistInfo = [...facilityHistInfo].sort((a, b) => { + // 숫자로 변환하여 비교 + const numA = Number(a.extnsSeqno); + const numB = Number(b.extnsSeqno); + if (!isNaN(numA) && !isNaN(numB)) { + return numA - numB; + } + // 숫자 변환이 실패하면 문자열로 비교 + return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno)); + }); + + sortedHistInfo.forEach(item => { + if(item.extnsSeqno == '0'){ + item.extnsSeqno = '최초'; + item.extnsNm = '신설'; + } + }); + + // yyyy-MM-dd로 변환 + sortedHistInfo.forEach(item => { + item.eqpPrmsDt = item.eqpPrmsDt ? item.eqpPrmsDt.substring(0,4) + '-' + item.eqpPrmsDt.substring(4,6) + '-' + item.eqpPrmsDt.substring(6,8) : ''; + item.trlrnCmplDt = item.trlrnCmplDt ? item.trlrnCmplDt.substring(0,4) + '-' + item.trlrnCmplDt.substring(4,6) + '-' + item.trlrnCmplDt.substring(6,8) : ''; + item.cmplDt = item.cmplDt ? item.cmplDt.substring(0,4) + '-' + item.cmplDt.substring(4,6) + '-' + item.cmplDt.substring(6,8) : ''; + item.rnDt = item.rnDt ? item.rnDt.substring(0,4) + '-' + item.rnDt.substring(4,6) + '-' + item.rnDt.substring(6,8) : ''; + }); + + // 마지막 이력 데이터의 값을 gridListPlanInfo에 업데이트 + if (sortedHistInfo.length > 0) { + const lastHistInfo = sortedHistInfo[sortedHistInfo.length - 1]; + set((state: ProcessFacilityType) => ({ + ...state, + ProcessFacilityDetail: { + ...state.ProcessFacilityDetail, + gridListFcltyHistInfo: sortedHistInfo, + gridListPlanInfo: [{ + ...state.ProcessFacilityDetail.gridListPlanInfo[0], + swrTrtPopl: lastHistInfo?.swrTrtPopl ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.swrTrtPopl, + trtDstrArea: lastHistInfo?.trtDstrArea ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.trtDstrArea, + sitArea: lastHistInfo?.sitArea ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.sitArea + }] + } + })); + } else { + set((state: ProcessFacilityType) => ({ + ...state, + ProcessFacilityDetail: { + ...state.ProcessFacilityDetail, + gridListFcltyHistInfo: sortedHistInfo, + gridListPlanInfo: [{ + ...state.ProcessFacilityDetail.gridListPlanInfo[0], + swrTrtPopl: '', + trtDstrArea: '', + sitArea: '' + }] + } + })); + } + }, + + // 모달 수질정보 + /** + * 시설정보 수질정보 데이터 호출 + * @param facilityCd : 시설코드 + */ + searchProcessFacilityWaterQlty: async (facilityCd:string) => { + const promiseSet = await Promise.all([fetchFacilityWaterQltyInfo(facilityCd)]); + const [facilityWaterQltyInfo] = promiseSet; + // extnsSeqno를 기준으로 오름차순 정렬 + const sortedWaterQltyInfo = [...facilityWaterQltyInfo].sort((a, b) => { + // 숫자로 변환하여 비교 + const numA = Number(a.extnsSeqno); + const numB = Number(b.extnsSeqno); + if (!isNaN(numA) && !isNaN(numB)) { + return numA - numB; + } + // 숫자 변환이 실패하면 문자열로 비교 + return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno)); + }); + + sortedWaterQltyInfo.forEach(item => { + if(item.extnsSeqno == '0'){ + item.extnsSeqno = '최초'; + } + }); + + // 법적수질정보 초기화 및 push + gridListLegalWaterQlty = []; + + gridListLegalWaterQlty.push({ + bod: facilityWaterQltyInfo[0]?.bod ?? 0, + cod: Number(facilityWaterQltyInfo[0]?.cod ?? 0), + ss: Number(facilityWaterQltyInfo[0]?.ss ?? 0), + tn: Number(facilityWaterQltyInfo[0]?.tn ?? 0), + tp: Number(facilityWaterQltyInfo[0]?.tp ?? 0), + totClfgrpcnt : Number(facilityWaterQltyInfo[0]?.totClfgrpcnt ?? 0), + }); + + // 마지막 수질 데이터의 값을 gridListPlanInfo에 업데이트 + if (sortedWaterQltyInfo.length > 0) { + const lastWaterQltyInfo = sortedWaterQltyInfo[sortedWaterQltyInfo.length - 1]; + set((state: ProcessFacilityType) => ({ + ...state, + ProcessFacilityDetail: { + ...state.ProcessFacilityDetail, + gridListFcltyWaterQltyInfo: sortedWaterQltyInfo, + gridListLegalWaterQlty: gridListLegalWaterQlty, + gridListPlanInfo: [{ + ...state.ProcessFacilityDetail.gridListPlanInfo[0], + dsInflWqltBod: lastWaterQltyInfo?.dsInflWqltBod === 'NULL' || lastWaterQltyInfo?.dsInflWqltBod === undefined ? '' : lastWaterQltyInfo.dsInflWqltBod, + dsInflWqltToc: lastWaterQltyInfo?.dsInflWqltToc === 'NULL' || lastWaterQltyInfo?.dsInflWqltToc === undefined ? '' : lastWaterQltyInfo.dsInflWqltToc, + dsInflWqltCod: lastWaterQltyInfo?.dsInflWqltCod === 'NULL' || lastWaterQltyInfo?.dsInflWqltCod === undefined ? '' : lastWaterQltyInfo.dsInflWqltCod, + dsInflWqltSs: lastWaterQltyInfo?.dsInflWqltSs === 'NULL' || lastWaterQltyInfo?.dsInflWqltSs === undefined ? '' : lastWaterQltyInfo.dsInflWqltSs, + dsInflWqltTn: lastWaterQltyInfo?.dsInflWqltTn === 'NULL' || lastWaterQltyInfo?.dsInflWqltTn === undefined ? '' : lastWaterQltyInfo.dsInflWqltTn, + dsInflWqltTp: lastWaterQltyInfo?.dsInflWqltTp === 'NULL' || lastWaterQltyInfo?.dsInflWqltTp === undefined ? '' : lastWaterQltyInfo.dsInflWqltTp, + plInflWqltBod: lastWaterQltyInfo?.plInflWqltBod === 'NULL' || lastWaterQltyInfo?.plInflWqltBod === undefined ? '' : lastWaterQltyInfo.plInflWqltBod, + plInflWqltToc: lastWaterQltyInfo?.plInflWqltToc === 'NULL' || lastWaterQltyInfo?.plInflWqltToc === undefined ? '' : lastWaterQltyInfo.plInflWqltToc, + plInflWqltCod: lastWaterQltyInfo?.plInflWqltCod === 'NULL' || lastWaterQltyInfo?.plInflWqltCod === undefined ? '' : lastWaterQltyInfo.plInflWqltCod, + 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 { + set((state: ProcessFacilityType) => ({ + ...state, + ProcessFacilityDetail: { + ...state.ProcessFacilityDetail, + gridListFcltyWaterQltyInfo: sortedWaterQltyInfo, + gridListLegalWaterQlty: gridListLegalWaterQlty, + gridListPlanInfo: [{ + ...state.ProcessFacilityDetail.gridListPlanInfo[0], + dsInflWqltBod: '', + dsInflWqltToc: '', + dsInflWqltCod: '', + dsInflWqltSs: '', + dsInflWqltTn: '', + dsInflWqltTp: '', + plEfflWqltBod: '', + plEfflWqltToc: '', + plEfflWqltCod: '', + plEfflWqltSs: '', + plEfflWqltTn: '', + plEfflWqltTp: '' + }] + } + })); + } + } + } +} \ No newline at end of file diff --git a/apps/fo/app/store/publicReuseFaciltyInsert/index.tsx b/apps/fo/app/store/publicReuseFaciltyInsert/index.tsx new file mode 100644 index 000000000..645cae5dc --- /dev/null +++ b/apps/fo/app/store/publicReuseFaciltyInsert/index.tsx @@ -0,0 +1,97 @@ +export type TableUpperProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; +} +type PublicReuseFacility = { + sido: string, + sigungo: string, + facilityName: string, + location: string, + locationgubun: string, + facilityCapacity: string, + yn?: string, + authoDay?: string, + area?: string, + reuse?: string, + register?: string +} +const upHeadList:TableUpperProps[] = [ + { id: "sido", title: "시도"}, + { id: "sigungo", title: "시군구"}, + { id: "facilityName", title: "시설명"}, + { id: "location", title: "위치"}, + { id: "locationgubun", title: "지역구분"}, + { id: "facilityCapacity", title: "시설용량"}, + { id: "yn", title: '여부', upName:'upChangeRe' , upSequnce:1 }, + { id: "authoDay", title: '허가일',upName:'upChangeRe', upSequnce:2 }, + { id: "area", title: '면적' ,upName:'upChangeRe',upSequnce:3 }]; + +const publicReuseFacilityUper:PublicReuseFacility[] = [ + { + sido: "서울", + sigungo: "강남구", + facilityName: "난지", + location: "경기도 고양시 덕양구 대차로 4가", + locationgubun: "500", + facilityCapacity: "800,000", + reuse: "Y", + register: "Y" + }, + { + sido: "서울", + sigungo: "강남구", + facilityName: "난지", + location: "경기도 고양시 덕양구 대차로 4가", + locationgubun: "500", + facilityCapacity: "800,000", + reuse: "Y", + register: "Y" + }, + { + sido: "서울", + sigungo: "강남구", + facilityName: "난지", + location: "경기도 고양시 덕양구 대차로 4가", + locationgubun: "500", + facilityCapacity: "800,000", + yn: "Y", + authoDay: "2012", + area:"123" + }, +] + +export type tableState = { + isInit: boolean; + upHeadList: TableUpperProps[]; + publicReuseFacilityUper: PublicReuseFacility[]; +} +type tableActions = { + decrementList: () => void +} + +export type tableType = tableState & tableActions; + +export const tableStateExport:tableState= { + isInit:false, + upHeadList: upHeadList, + publicReuseFacilityUper: publicReuseFacilityUper, +} + +// 상태 설정 함수 타입 정의 +type SetStateFunction = (state: tableState | ((state: tableState) => tableState)) => void; + +export const tableActionsExport:(set:SetStateFunction)=>tableActions=(set: SetStateFunction) => { + + return { + decrementList:()=>set( + (state:tableState) => { + return { + ...state, + publicReuseFacilityUper: + state.publicReuseFacilityUper.slice(0, state.publicReuseFacilityUper.length - 1) + }} + ) + } +} diff --git a/apps/fo/app/store/rdi/index.tsx b/apps/fo/app/store/rdi/index.tsx new file mode 100644 index 000000000..e7acb2231 --- /dev/null +++ b/apps/fo/app/store/rdi/index.tsx @@ -0,0 +1,274 @@ +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +import { getFetch } from "@/app/components/api"; +export type { TableUpperProps }; +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 시설별 강수현황 +export type RdiProps = { + bscFctCd : string, + sido : string, + sigungo : string, + bscFctNm : string, + lclgvrnmntNm : string, + daddr : string, + fctCpc : number, + summerMinBodValue : number, + inflAmnt : number, + winterAvgBodLowRainfall : number, + rdiiValue : number, + infoRnDay : number, + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, +} + +export interface RdiInterface { + bscFctCd : string, + sido : string, + sigungo : string, + bscFctNm : string, + lclgvrnmntNm : string, + daddr : string, + fctCpc : number, + summerMinBodValue : number, + inflAmnt : number, + winterAvgBodLowRainfall : number, + rdiiValue : number, + infoRnDay : number, + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, +} + +// 모달 +export interface RdiInputModalProps { + msrDt : string, + realSewageInput : number | '', + sewageOccur : number | '', + underWaterInput : number | '', +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; + +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string, + 시군구: string, + 시설명: string, + 주소: string, + 시설용량: number, + 하절기강우일최저유입BOD: number | '', + 하절기최저유입BOD유입유량: number | '', + 동절기청천시유입BOD: number | '', + RDII추정 : number | '', + RDII추정시일강수량 : number | '', +} + +// 리스트 헤더 +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 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "daddr", title: "주소", row:0, rowSpan:2 }, + { 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: "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 }, +]; + +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: "daddr", title: "주소", row:0, rowSpan:2 }, + { 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: "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 }, +]; + +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 }, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 }, + { id: "rgnNm", title: "환경청", row:0, rowSpan:2 }, + { id: "B", title: "행정구역", row:0, colSpan:2 }, + { id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 }, + { id: "sigungo", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "daddr", title: "주소", row:0, rowSpan:2 }, + { 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: "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 }, +]; + +const rdiList:RdiProps[] = [ +] + +/**==================================== + * Store - API + ====================================*/ + +const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + try{ + const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const url = '/rdii/list/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; + console.log(url); + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } + }catch(error){ + console.log(error); + return { data: rdiList, count: 0 }; + } +} + +/**==================================== + * Store - expaort + ====================================*/ +export type RdiActions = { + searchRdiList: (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; +} + +export type RdiType = { + RdiSearch: { + isInit: boolean; + labelArray:string[]; + }, + + Rdi : { + isInit: boolean; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + rdiList: RdiProps[]; // Changed from facilityAreaProps to ProcessAreaProps + rdiCount: number; + excelList: ExcelType[]; + }, +} + +export const rdiInitState: RdiType = { + RdiSearch: { + isInit: false, + labelArray: LabelArray, + }, + + Rdi : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + rdiList : rdiList, + rdiCount : 2, + excelList : [] + } +} + +const dataFormSetting=(item : RdiProps)=> { + DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'summerMinBodValue', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'winterAvgBodLowRainfall', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'rdiiValue', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'infoRnDay', item: item as unknown as Record<string, unknown>}); +} + +// Add type definitions for state setter function +type SetStateFunction = (state: RdiType | ((state: RdiType) => RdiType)) => void; + +export type RdiStore = RdiType & RdiActions; + +export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunction) => { + console.log("tableActionsExport"); + return { + searchRdiList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchRdiList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + const data = res.data; + const excelDownList: ExcelType[] = []; + + const rtnData = data.map((item: RdiProps) => { + return { + ...item, + sido: item.lclgvrnmntNm.split(' ')[0]?.toString() || '', + sigungo: item.lclgvrnmntNm.split(' ')[1]?.toString() || '', + inflAmnt: item.inflAmnt?item.inflAmnt.toFixed(1) : '' + } + }) + + rtnData.forEach((item: RdiProps) => { + dataFormSetting(item) + }) + + if(pageSize>5000){ + rtnData.forEach((item: RdiProps) => { + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시설명: item.bscFctNm, + 시도: item.sido, + 시군구: item.sigungo, + 주소: item.daddr, + 시설용량: item.fctCpc, + 하절기강우일최저유입BOD: item.summerMinBodValue, + 하절기최저유입BOD유입유량: item.inflAmnt, + 동절기청천시유입BOD: item.winterAvgBodLowRainfall, + RDII추정 : item.rdiiValue, + RDII추정시일강수량 : item.infoRnDay, + }); + }); + } + set((state: RdiType) => ({ + ...state, + Rdi: pageSize<5000?{ + ...state.Rdi, + rdiList: rtnData, + rdiCount: res.count, + }:{ + ...state.Rdi, + excelList: excelDownList, + } + })) + }, + } +} \ No newline at end of file diff --git a/apps/fo/app/store/sewageOccur/index.tsx b/apps/fo/app/store/sewageOccur/index.tsx new file mode 100644 index 000000000..93de7ca94 --- /dev/null +++ b/apps/fo/app/store/sewageOccur/index.tsx @@ -0,0 +1,464 @@ +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +import { getFetch } from "@/app/components/api"; +export type { TableUpperProps }; +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 시설별 강수현황 +export type SewageOccurProps = { + bscFctCd : string, + sidoNm : string, + sggNm : string, + lclgvrnmntNm : string, + bscFctNm : string, + daddr : string, + fctCpc : number, + waterUsage : number, + swageChangePercent : number, + estimationSewage : number, + sewageInputYear : number, + sewageInputChungchun : number, + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, + msrDt : string, +} + +export type SewageOccurDetailProps = { + msrDt : string, + waterUsage : number, + swageChangePercent : number, + estimationSewage : number, +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; + +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string, + 시군구: string, + 시설명: string, + 주소: string, + 시설용량: number, + 물사용량: number | '', + 오수전환율: number | '', + 오수발생량추정: number | '', + 연간하수유입량: number | '', + 청천시하수유입량: number | '', +} + +// 리스트 헤더 +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 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "daddr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "waterUsage", title: "물사용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "swageChangePercent", title: "오수전환율\n(%)", align:"right", row:0, rowSpan:2 }, + { id: "estimationSewage", title: "오수발생량 추정\n(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "sewageInputYear", title: "연간 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "sewageInputChungchun", title: "청천시 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 }, +]; + +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: "daddr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "waterUsage", title: "물사용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "swageChangePercent", title: "오수전환율", align:"right", row:0, rowSpan:2 }, + { id: "estimationSewage", title: "오수발생량 추정\n(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "sewageInputYear", title: "연간 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "sewageInputChungchun", title: "청천시 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 }, +]; + +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 }, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 }, + { id: "rgnNm", title: "환경청", row:0, rowSpan:2 }, + { id: "B", title: "행정구역", row:0, colSpan:2 }, + { id: "sidoNm", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 }, + { id: "sggNm", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "daddr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "waterUsage", title: "물사용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "swageChangePercent", title: "오수전환율", align:"right", row:0, rowSpan:2 }, + { id: "estimationSewage", title: "오수발생량 추정\n(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "sewageInputYear", title: "연간 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "sewageInputChungchun", title: "청천시 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 }, +]; + +const upHeadListSewageOccurDetail:TableUpperProps[] = [ + { id: "msrDt", title: "년월" }, + { id: "waterUsage", title: "물사용량(m³/일)" }, + { id: "swageChangePercent", title: "오수전환율" }, + { id: "estimationSewage", title: "오수발생량 추정(m³/일)", row:0, rowSpan:2 }, +]; + +const sewageOccurList:SewageOccurProps[] = [ +] + +const tempSewageOccurList:SewageOccurDetailProps[] = [ + { + msrDt: '2024-01', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 60, + }, + { + msrDt: '2024-02', + waterUsage: 106, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-03', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-04', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-05', + waterUsage: 100, + swageChangePercent: 10, + estimationSewage: 80, + }, + { + msrDt: '2024-06', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-07', + waterUsage: 100, + swageChangePercent: 580, + estimationSewage: 80, + }, + { + msrDt: '2024-08', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-09', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-10', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-11', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2024-12', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-01', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-02', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-03', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-04', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-05', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-06', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-07', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-08', + waterUsage: 100, + swageChangePercent: 0.8, + estimationSewage: 80, + }, + { + msrDt: '2025-09', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-10', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-11', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, + { + msrDt: '2025-12', + waterUsage: 100, + swageChangePercent: 80, + estimationSewage: 80, + }, +] + +/**==================================== + * Store - API + ====================================*/ + +const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + // 20250701임시시 + console.log("partCd", partCd); + console.log("nameCd", nameCd); + console.log("fcltCpct", fcltCpct); + console.log("operationValue", operationValue); + console.log("fcltyNm", fcltyNm); + console.log("searchYear", 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 } + + /* + const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } */ +} + +/**==================================== + * Store - expaort + ====================================*/ +export type SewageOccurActions = { + searchSewageOccurList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; +} + +export type SewageOccurType = { + SewageOccurSearch: { + isInit: boolean; + labelArray:string[]; + }, + + SewageOccur : { + isInit: boolean; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + sewageOccurList: SewageOccurProps[]; // Changed from facilityAreaProps to ProcessAreaProps + sewageOccurCount: number; + excelList: ExcelType[]; + }, + + SewageOccurDetail : { + isInit: boolean; + upHeadListSewageOccurDetail: TableUpperProps[]; + gridListSewageOccurDetail: SewageOccurDetailProps[]; + } +} + +export const sewageOccurInitState: SewageOccurType = { + SewageOccurSearch: { + isInit: false, + labelArray: LabelArray, + }, + + SewageOccur : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + sewageOccurList : sewageOccurList, + sewageOccurCount : 2, + excelList : [] + }, + + SewageOccurDetail : { + isInit: false, + upHeadListSewageOccurDetail: upHeadListSewageOccurDetail, + gridListSewageOccurDetail: tempSewageOccurList, + } +} + +const dataFormSetting=(item : SewageOccurProps)=> { + DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'waterUsage', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'estimationSewage', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'sewageInputYear', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'sewageInputChungchun', item: item as unknown as Record<string, unknown>}); +} + +export type SewageOccurStore = SewageOccurType & SewageOccurActions; + +// Add type definitions for API responses +type FlowDataItem = { + inflAmnt: string | number; + infoRnDay: string | number; + bscFctCd: string; + msrDt: string; +} + +type FlowResponse = { + data: FlowDataItem[]; + count: number; +} + +type SetStateFunction = (state: SewageOccurType | ((state: SewageOccurType) => SewageOccurType)) => void; + +export const sewageOccurReducer:(set:SetStateFunction)=>SewageOccurActions=(set: SetStateFunction) => { + return { + searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + const data = res.data; + + // Promise.all을 사용하여 모든 비동기 작업이 완료될 때까지 기다림 + const calData = await Promise.all(data.map(async (item: SewageOccurProps) => { + item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + item.waterUsage = item.fctCpc * 0.6; + item.swageChangePercent = 80; + item.estimationSewage = item.waterUsage * (item.swageChangePercent/100); + + const bscFctCd = item.bscFctCd; + const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear; + const res2 = await getFetch('fo', url2) as FlowResponse; + const sumFlow = res2.data.reduce((acc: number, curr: FlowDataItem) => Number(acc) + Number(curr.inflAmnt), 0); + + let sumChungcunDay = 0; + let chungcunSumFlow = 0; + + res2.data.forEach((item: FlowDataItem) => { + if(Number(item.infoRnDay) ===0){ + chungcunSumFlow += Number(item.inflAmnt); + sumChungcunDay += 1; + }else{ + console.log(item.bscFctCd +' day : ' +' '+item.msrDt); + } + }); + + item.sewageInputYear = sumFlow/res2.count; + item.sewageInputChungchun = chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay); + return item; + })); + + console.log('calData : ', calData); + + const excelDownList: ExcelType[] = []; + if(pageSize>5000){ + calData.forEach(item => { + dataFormSetting(item) + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + /* item.ctpvNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */ + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시도: item.sidoNm, + 시군구: item.sggNm, + 시설명: item.bscFctNm, + 주소: item.daddr, + 시설용량: item.fctCpc, + 물사용량: item.waterUsage, + 오수전환율: item.swageChangePercent, + 오수발생량추정: item.estimationSewage, + 연간하수유입량: item.sewageInputYear, + 청천시하수유입량: item.sewageInputChungchun, + }); + }); + }else{ + calData.forEach((item: SewageOccurProps) => { + dataFormSetting(item) + /* item.sidoNmNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */ + }); + } + set((state: SewageOccurType) => ({ + ...state, + SewageOccur: pageSize<5000?{ + ...state.SewageOccur, + sewageOccurList: calData, + sewageOccurCount: res.count, + }:{ + ...state.SewageOccur, + excelList: excelDownList, + } + })) + }, + } +} \ No newline at end of file diff --git a/apps/fo/app/store/store.tsx b/apps/fo/app/store/store.tsx new file mode 100644 index 000000000..f17801d1f --- /dev/null +++ b/apps/fo/app/store/store.tsx @@ -0,0 +1,78 @@ +import { createStore } from 'zustand/vanilla'; +import { CommonReducer, commonState, type CommonStore, type CommonType } from './common'; +import { dashBoardReducer, dashBoardInitState, type DashBoardStore, type DashBoardType } from './dashboard'; +import { + facilityPrecipitationInitState, + facilityPrecipitationReducer, + type FacilityPrecipitationStore, type FacilityPrecipitationType +} from './facilityPrecipitation'; +import { planSewageInitState, planSewageReducer, type PlanSewageStore, type PlanSewageType } from './planSewage'; +import { flowRateSearchInitState, flowRateSearchReducer, type FlowRateSearchStore, type FlowRateSearchType } from './flowRate'; +import { + processAreaInitState, + processAreaReducer, + type ProcessAreaStore, type ProcessAreaType +} from './processArea'; +import { + processFacilityInitState, + processFacilityReducer, + type ProcessFacilityStore, type ProcessFacilityType +} from './processFacility'; +import { + facilityWaterQltyInitState, + facilityWaterQltyReducer, + type FacilityWaterQltyStore, type FacilityWaterQltyType +} from './facilityWaterQlty'; +import { + sewageOccurInitState, + sewageOccurReducer, + type SewageOccurStore, type SewageOccurType +} from './sewageOccur'; +import { + underWaterInitState, + underWaterReducer, + type UnderWaterStore, type UnderWaterType +} from './underWaterInput'; +import { + rdiInitState, + rdiReducer, + type RdiStore, type RdiType +} from './rdi'; +import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index'; +export type FoStore = CommonStore & tableType & DashBoardStore & ProcessFacilityStore & ProcessAreaStore & FlowRateSearchStore & FacilityPrecipitationStore & FacilityWaterQltyStore & PlanSewageStore & SewageOccurStore & UnderWaterStore & RdiStore; +export type FoState = CommonType & tableState & ProcessFacilityType & ProcessAreaType & FacilityPrecipitationType & FlowRateSearchType & DashBoardType & FacilityWaterQltyType & PlanSewageType & SewageOccurType & UnderWaterType & RdiType; + +export const StoreZus = + (initState: FoState = { ...commonState, + ...tableStateExport + , ...processFacilityInitState + , ...processAreaInitState + , ...facilityPrecipitationInitState + , ...dashBoardInitState + , ...flowRateSearchInitState + , ...facilityWaterQltyInitState + , ...planSewageInitState + , ...sewageOccurInitState + , ...underWaterInitState + , ...rdiInitState + } + ) => { + return createStore<FoStore>()((set) => { + return { + ...initState, + ...CommonReducer(set), + ...tableActionsExport(set), + ...dashBoardReducer(set), + ...processFacilityReducer(set), + ...processAreaReducer(set), + ...facilityPrecipitationReducer(set), + ...flowRateSearchReducer(set), + ...facilityWaterQltyReducer(set), + ...planSewageReducer(set), + ...sewageOccurReducer(set), + ...underWaterReducer(set), + ...rdiReducer(set), + } + }) +} +//store 등록하는 부분 \ No newline at end of file diff --git a/apps/fo/app/store/underWaterInput/index.tsx b/apps/fo/app/store/underWaterInput/index.tsx new file mode 100644 index 000000000..ea6f4575b --- /dev/null +++ b/apps/fo/app/store/underWaterInput/index.tsx @@ -0,0 +1,397 @@ +import DataForm from "@/app/components/common/DataForm"; +import type { TableUpperProps } from "../dashboard"; +import { getFetch } from "@/app/components/api"; +export type { TableUpperProps }; +/**==================================== + * Store - 타입 + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 시설별 강수현황 +export type UnderWaterProps = { + bscFctCd : string, + lclgvrnmntNm : string, + sidoNm : string, + sggNm : string, + bscFctNm : string, + daddr : string, + fctCpc : number, + underWaterInputYear : number, + underWaterInputSummer : number, + underWaterInputWinter : number, + infoRnDay : number, + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, +} +export interface UnderWaterInterface { + bscFctCd : string, + sidoNm : string, + sggNm : string, + bscFctNm : string, + daddr : string, + fctCpc : number, + underWaterInputYear : number, + underWaterInputSummer : number, + underWaterInputWinter : number, + infoRnDay : number, + rgnNm : string, + rvbsn : string, + lgbsn : string, + mdmbsn : string, +} + +// 모달 +export interface UnderWaterInputModalProps { + msrDt : string, + realSewageInput : number | '', + sewageOccur : number | '', + underWaterInput : number | '', +} + +/**==================================== + * Store - data + ====================================*/ + +/** + * (1) 메인페이지 + */ +// 검색단 +const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; + +export interface ExcelType { + 시설코드: string, + 권역:string|'', + 단위유역:string|'', + 중권역:string|'', + 환경청:string|'', + 시도: string, + 시군구: string, + 시설명: string, + 주소: string, + 시설용량: number, + 연간지하수유입량: number | '', + 하절기지하수유입량: number | '', + 동절기지하수유입량: number | '', +} + +// 리스트 헤더 +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 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "daddr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 }, + { id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1 }, + { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1 }, + { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1 }, +]; + +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: "daddr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 }, + { id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1 }, + { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1 }, + { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1 }, +]; + +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 }, + { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 }, + { id: "rgnNm", title: "환경청", row:0, rowSpan:2 }, + { id: "B", title: "행정구역", row:0, colSpan:2 }, + { id: "sidoNm", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 }, + { id: "sggNm", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 }, + { id: "bscFctNm", title: "시설명", row:0, rowSpan:2 }, + { id: "daddr", title: "주소", row:0, rowSpan:2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 }, + { id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 }, + { id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1 }, + { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1 }, + { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1 }, +]; + +// 모달 +const upHeadListUnderWaterDetail:TableUpperProps[] = [ + { id: "msrDt", title: "날짜" }, + { id: "realSewageInput", title: "실제 하수유입량\n(m³/일)" }, + { id: "sewageOccur", title: "오수발생량\n(m³/일)" }, + { id: "underWaterInput", title: "지하수 유입량\n(m³/일)" }, +]; + +const underWaterList:UnderWaterProps[] = [ +] + +const underWaterDetailList:UnderWaterInputModalProps[] = [ + { + msrDt : "2024-01", + realSewageInput : 178007, + sewageOccur : 10.5, + underWaterInput : 80.6, + }, + { + msrDt : "2024-02", + realSewageInput : 123007, + sewageOccur : 10.7, + underWaterInput : 80.6, + }, + { + msrDt : "2024-03", + realSewageInput : 213007, + sewageOccur : 10.1, + underWaterInput : 80.6, + }, + { + msrDt : "2024-04", + realSewageInput : 83007, + sewageOccur : 9.57, + underWaterInput : 80.6, + }, + { + msrDt : "2024-05", + realSewageInput : 15007, + sewageOccur : 3.57, + underWaterInput : 80.6, + }, + { + msrDt : "2024-06", + realSewageInput : 123007, + sewageOccur : '', + underWaterInput : 80.6, + }, + { + msrDt : "2024-07", + realSewageInput : 101007, + sewageOccur : 1, + underWaterInput : 80.6, + }, + { + msrDt : "2024-08", + realSewageInput : 103807, + sewageOccur : 10.57, + underWaterInput : '', + }, + { + msrDt : "2024-09", + realSewageInput : 9007, + sewageOccur : 10.57, + underWaterInput : 80.6, + }, + { + msrDt : "2024-10", + realSewageInput : 163007, + sewageOccur : 10.57, + underWaterInput : 80.6, + }, + { + msrDt : "2024-11", + realSewageInput : 123007, + sewageOccur : 10.57, + underWaterInput : 80.6, + }, + { + msrDt : "2024-12", + realSewageInput : 1007, + sewageOccur : 10.57, + underWaterInput : 80.6, + }, +] +/**==================================== + * Store - API + ====================================*/ + +// API response types +interface FlowDataItem { + inflAmnt: number; + infoRnDay: number; + msrDt: string; +} + +interface ApiResponse { + data: FlowDataItem[]; + count: number; +} + +const fetchUnderWaterList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } + /* + const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; + const res = await getFetch('fo', url); + return { data: res.data, count: res.count } */ + + //return { data: underWaterList, count: 2 }; +} + +/**==================================== + * Store - expaort + ====================================*/ +export type UnderWaterActions = { + searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; +} + +export type UnderWaterType = { + UnderWaterSearch: { + isInit: boolean; + labelArray:string[]; + }, + + UnderWater : { + isInit: boolean; + upHeadList1: TableUpperProps[]; + upHeadList2: TableUpperProps[]; + upHeadList3: TableUpperProps[]; + underWaterList: UnderWaterProps[]; // Changed from facilityAreaProps to ProcessAreaProps + underWaterCount: number; + excelList: ExcelType[]; + }, + + UnderWaterDetail : { + isInit: boolean; + upHeadListUnderWaterDetail: TableUpperProps[]; + gridListUnderWaterDetail: UnderWaterInputModalProps[]; + } +} + +export const underWaterInitState: UnderWaterType = { + UnderWaterSearch: { + isInit: false, + labelArray: LabelArray, + }, + + UnderWater : { + isInit : false, + upHeadList1 : upHeadList1, + upHeadList2 : upHeadList2, + upHeadList3 : upHeadList3, + underWaterList : underWaterList, + underWaterCount : 4, + excelList : [] + }, + + UnderWaterDetail : { + isInit: false, + upHeadListUnderWaterDetail: upHeadListUnderWaterDetail, + gridListUnderWaterDetail: underWaterDetailList, + } +} + +const dataFormSetting=(item : UnderWaterProps)=> { + DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'underWaterInputYear', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'underWaterInputSummer', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float2', column: 'underWaterInputWinter', item: item as unknown as Record<string, unknown>}); +} + +export type UnderWaterStore = UnderWaterType & UnderWaterActions; + +export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterType) => void)=>UnderWaterActions=(set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => { + return { + searchUnderWaterList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const res = await fetchUnderWaterList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + const data = res.data; + const calData = await Promise.all(data.map(async (item: UnderWaterProps) => { + item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + + const bscFctCd = item.bscFctCd; + const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear; + const res2 = await getFetch('fo', url2) as ApiResponse; + + let sumChungcunDay = 0; + let chungcunSumFlow = 0; + + let sumSummerChungcunDay = 0; + let sumSummerChungcunFlow = 0; + + let sumWinterChungcunDay = 0; + let sumWinterChungcunFlow = 0; + + res2.data.forEach((flowItem: FlowDataItem) => { + // 청천시 + if(Number(flowItem.infoRnDay) ===0){ + sumChungcunDay += 1; + chungcunSumFlow += Number(flowItem.inflAmnt); + + if(flowItem.msrDt.substring(4,6) ==='06' || flowItem.msrDt.substring(4,6) ==='07' || flowItem.msrDt.substring(4,6) ==='08'){ + sumSummerChungcunDay += 1; + sumSummerChungcunFlow += Number(flowItem.inflAmnt); + }else if(flowItem.msrDt.includes('01') || flowItem.msrDt.includes('02')){ + sumWinterChungcunDay += 1; + sumWinterChungcunFlow += Number(flowItem.inflAmnt); + } + } + }); + item.underWaterInputYear = Number(chungcunSumFlow==0?0:Math.round((chungcunSumFlow / sumChungcunDay) * 10) / 10) - Number(item.fctCpc * 0.6 * 0.8); + item.underWaterInputSummer = Number(sumSummerChungcunFlow==0?0:Math.round((sumSummerChungcunFlow / sumSummerChungcunDay) * 10) / 10) - Number(item.fctCpc * 0.6 * 0.8); + item.underWaterInputWinter = Number(sumWinterChungcunFlow==0?0:Math.round((sumWinterChungcunFlow / sumWinterChungcunDay) * 10) / 10) - Number(item.fctCpc * 0.6 * 0.8); + + return item; + })); + + const excelDownList: ExcelType[] = []; + if(pageSize>5000){ + calData.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.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시도: item.sidoNm, + 시군구: item.sggNm, + 시설명: item.bscFctNm, + 주소: item.daddr, + 시설용량: item.fctCpc, + 연간지하수유입량: item.underWaterInputYear, + 하절기지하수유입량: item.underWaterInputSummer, + 동절기지하수유입량: item.underWaterInputWinter, + + }); + }); + }else{ + data.forEach((item: UnderWaterProps) => { + dataFormSetting(item) + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + /* item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */ + }); + } + set((state: UnderWaterType) => ({ + ...state, + UnderWater: pageSize<5000?{ + ...state.UnderWater, + underWaterList: calData, + underWaterCount: res.count, + }:{ + ...state.UnderWater, + excelList: excelDownList, + } + })) + }, + } +} \ No newline at end of file diff --git a/apps/fo/components.json b/apps/fo/components.json new file mode 100644 index 000000000..dea737b85 --- /dev/null +++ b/apps/fo/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/apps/fo/eslint.config.mjs b/apps/fo/eslint.config.mjs new file mode 100644 index 000000000..c85fb67c4 --- /dev/null +++ b/apps/fo/eslint.config.mjs @@ -0,0 +1,16 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), +]; + +export default eslintConfig; diff --git a/apps/fo/middleware.ts b/apps/fo/middleware.ts new file mode 100644 index 000000000..c3e2a5bec --- /dev/null +++ b/apps/fo/middleware.ts @@ -0,0 +1,115 @@ +import { NextRequest, NextResponse } from "next/server"; + +const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id"; +const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization"; +const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL || ""; +const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || ""; +const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || ""; +const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ""; + +export function middleware(req: NextRequest) { + console.log("Middleware 진입:", req.nextUrl.pathname); + const pathname = req.nextUrl.pathname; + const searchParams = req.nextUrl.searchParams; + + if (pathname.startsWith("/server")) { + const queryParams = searchParams.toString(); + const newUrl = `${SERVER_API_URL}${pathname.replace("/server", "")}${ + queryParams ? `?${queryParams}` : "" + }`; + console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + + if (pathname.startsWith("/geoserver")) { + let queryParams = searchParams.toString(); + const typename = searchParams.get("TYPENAME"); + + let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`; + + if ( + typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설 + || typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역 + || typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도 + || typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도 + ) { + newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`; + } + else if ( + typename == "hasudo:tb_ms_swage_ppl" //관로 + || typename == "hasudo:tb_ms_swage_mhl" //맨홀 + || typename == "hasudo:tb_ms_swage_pmstn" //펌프장 + || typename == "hasudo:tb_ms_stofcbr" //우수토실 + ) { + newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`; + } + else if ( + typename == "hasudo:om_dt_untrt_swg_rain" //중점우수토실 + || typename == "hasudo:om_wqlt_station" //관측소 + || typename == "hasudo:tb_wr_bdst_ruse_fclt" //재이용시설 + || typename == "hasudo:tb_wr_rnwtr_utztn_fclt" //빗물이용시설 + || typename == "hasudo:tb_wr_rswtr_fclt" //중수도시설 + || typename == "hasudo:tb_wr_htdfl_fclt" //온배수시설 + || typename == "hasudo:tb_wr_ruse_fclt_dmd_co" //수요처 + || typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처 + ){ + const url = req.nextUrl.clone(); + url.searchParams.set('TYPENAME', typename.replace("hasudo:","")); + queryParams = url.searchParams.toString(); + newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`; + } + + console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + + if (pathname.startsWith("/sewerage-service/api/v1/map/geometry")) { + const queryParams = searchParams.toString(); + const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${ + queryParams ? `?${queryParams}` : "" + }`; + console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + + if (pathname.startsWith("/attach-service") || pathname.startsWith("/cms-service")) { + const requestHeaders = new Headers(req.headers); + const token = req.cookies.get(CLAIM_NAME)?.value; + const userId = req.cookies.get(AUTH_USER_ID)?.value; + const newUrl = `${SERVER_API_URL}${pathname}`; + + if (token) requestHeaders.set(CLAIM_NAME, token); + if (userId) requestHeaders.set(AUTH_USER_ID, userId); + + console.log( + `[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`, + ); + return NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + } + + if (pathname.startsWith("/user-service")) { + const requestHeaders = new Headers(req.headers); + const newUrl = `${SERVER_API_URL}${pathname}`; + + console.log( + `[Middleware] Rewriting from '/user-service' path to: ${newUrl}`, + ); + return NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + } + + return NextResponse.next(); +} + +export const config = { + matcher: [ + "/server/:path*", + "/geoserver/:path*", + "/attach-service/:path*", + "/user-service/:path*", + "/cms-service/:path*", + ], +}; diff --git a/apps/fo/next.config.ts b/apps/fo/next.config.ts new file mode 100644 index 000000000..d157358c9 --- /dev/null +++ b/apps/fo/next.config.ts @@ -0,0 +1,33 @@ +import type { NextConfig } from "next"; + +const subDomain = + process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13001"; + +const nextConfig: NextConfig = { + async rewrites() { + return { + beforeFiles: [ + { + source: "/_next/static/:path*", + destination: `${subDomain}/_next/static/:path*`, + has: [ + { + type: "header", + key: "referer", + value: "(.*)/pst(.*)", + }, + ], + }, + ], + afterFiles: [ + { + source: "/pst/:path*", + destination: `${subDomain}/pst/:path*`, + }, + ], + fallback: [], + }; + }, +}; + +export default nextConfig; diff --git a/apps/fo/package.json b/apps/fo/package.json new file mode 100644 index 000000000..447e7d7a7 --- /dev/null +++ b/apps/fo/package.json @@ -0,0 +1,29 @@ +{ + "name": "fo", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@common/business_components": "workspace:*", + "@common/components": "workspace:*", + "@tailwindcss/vite": "^4.0.10", + "html2canvas": "^1.4.1", + "next": "15.1.7", + "select": "^1.1.2", + "webpack": "^5.98.0", + "zustand": "^5.0.3" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "eslint": "^9", + "eslint-config-next": "15.1.7", + "postcss": "^8", + "tailwindcss": "^3.4.17", + "typescript": "^5.6.3" + } +} diff --git a/apps/fo/postcss.config.mjs b/apps/fo/postcss.config.mjs new file mode 100644 index 000000000..1a69fd2a4 --- /dev/null +++ b/apps/fo/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/apps/fo/public/file.svg b/apps/fo/public/file.svg new file mode 100644 index 000000000..004145cdd --- /dev/null +++ b/apps/fo/public/file.svg @@ -0,0 +1 @@ +<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg> \ No newline at end of file diff --git a/apps/fo/public/globe.svg b/apps/fo/public/globe.svg new file mode 100644 index 000000000..567f17b0d --- /dev/null +++ b/apps/fo/public/globe.svg @@ -0,0 +1 @@ +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg> \ No newline at end of file diff --git a/apps/fo/public/images/map/layericon/BML_CBAS_PS.png b/apps/fo/public/images/map/layericon/BML_CBAS_PS.png new file mode 100644 index 000000000..5ff4add3e Binary files /dev/null and b/apps/fo/public/images/map/layericon/BML_CBAS_PS.png differ diff --git a/apps/fo/public/images/map/layericon/BML_ELEV_PS.png b/apps/fo/public/images/map/layericon/BML_ELEV_PS.png new file mode 100644 index 000000000..83970a331 Binary files /dev/null and b/apps/fo/public/images/map/layericon/BML_ELEV_PS.png differ diff --git a/apps/fo/public/images/map/layericon/BML_ETCP_PS.png b/apps/fo/public/images/map/layericon/BML_ETCP_PS.png new file mode 100644 index 000000000..df9e4957b Binary files /dev/null and b/apps/fo/public/images/map/layericon/BML_ETCP_PS.png differ diff --git a/apps/fo/public/images/map/layericon/BML_STMS_PS.png b/apps/fo/public/images/map/layericon/BML_STMS_PS.png new file mode 100644 index 000000000..423f2e8fc Binary files /dev/null and b/apps/fo/public/images/map/layericon/BML_STMS_PS.png differ diff --git a/apps/fo/public/images/map/layericon/BML_SYMB_PS.png b/apps/fo/public/images/map/layericon/BML_SYMB_PS.png new file mode 100644 index 000000000..497589bea Binary files /dev/null and b/apps/fo/public/images/map/layericon/BML_SYMB_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_BYST_PS.png b/apps/fo/public/images/map/layericon/RDL_BYST_PS.png new file mode 100644 index 000000000..1e78cba68 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_BYST_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_CROS_PS.png b/apps/fo/public/images/map/layericon/RDL_CROS_PS.png new file mode 100644 index 000000000..57b5f2ca5 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_CROS_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_ETCT_PS.png b/apps/fo/public/images/map/layericon/RDL_ETCT_PS.png new file mode 100644 index 000000000..29c0b77b4 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_ETCT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_OCUP_PS.png b/apps/fo/public/images/map/layericon/RDL_OCUP_PS.png new file mode 100644 index 000000000..8a1ac0074 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_OCUP_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_RDSN_PS.png b/apps/fo/public/images/map/layericon/RDL_RDSN_PS.png new file mode 100644 index 000000000..5629de9fb Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_RDSN_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_SCLT_PS.png b/apps/fo/public/images/map/layericon/RDL_SCLT_PS.png new file mode 100644 index 000000000..7281dc61d Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_SCLT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_SEPC_PS.png b/apps/fo/public/images/map/layericon/RDL_SEPC_PS.png new file mode 100644 index 000000000..087b4e941 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_SEPC_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_STAT_PS.png b/apps/fo/public/images/map/layericon/RDL_STAT_PS.png new file mode 100644 index 000000000..13d8ea689 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_STAT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_STLT_PS.png b/apps/fo/public/images/map/layericon/RDL_STLT_PS.png new file mode 100644 index 000000000..489524b5c Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_STLT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_TFSN_PS.png b/apps/fo/public/images/map/layericon/RDL_TFSN_PS.png new file mode 100644 index 000000000..6dfff6696 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_TFSN_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_TRCT_PS.png b/apps/fo/public/images/map/layericon/RDL_TRCT_PS.png new file mode 100644 index 000000000..ed1a72d8a Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_TRCT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_TREE_PS.png b/apps/fo/public/images/map/layericon/RDL_TREE_PS.png new file mode 100644 index 000000000..0ee84dedc Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_TREE_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_TRSN_PS.png b/apps/fo/public/images/map/layericon/RDL_TRSN_PS.png new file mode 100644 index 000000000..15675cb31 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_TRSN_PS.png differ diff --git a/apps/fo/public/images/map/layericon/RDL_TSCT_PS.png b/apps/fo/public/images/map/layericon/RDL_TSCT_PS.png new file mode 100644 index 000000000..ffc225159 Binary files /dev/null and b/apps/fo/public/images/map/layericon/RDL_TSCT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_CLAY_PS.png b/apps/fo/public/images/map/layericon/SWL_CLAY_PS.png new file mode 100644 index 000000000..e603ffa9c Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_CLAY_PS.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_CLAY_PS_RED.png b/apps/fo/public/images/map/layericon/SWL_CLAY_PS_RED.png new file mode 100644 index 000000000..0e5450688 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_CLAY_PS_RED.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_DEPT_PS.png b/apps/fo/public/images/map/layericon/SWL_DEPT_PS.png new file mode 100644 index 000000000..fede6c304 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_DEPT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS.png new file mode 100644 index 000000000..75217f494 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS_BLACK.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS_BLACK.png new file mode 100644 index 000000000..5d2e8a70a Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS_BLACK.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS_BLUE.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS_BLUE.png new file mode 100644 index 000000000..e521cb84a Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS_BLUE.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS_GRAY.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS_GRAY.png new file mode 100644 index 000000000..e19f3d8b3 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS_GRAY.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS_GREEN.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS_GREEN.png new file mode 100644 index 000000000..4503c13c6 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS_GREEN.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS_RED.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS_RED.png new file mode 100644 index 000000000..b8fa45f9c Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS_RED.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_MANH_PS_YELLOW.png b/apps/fo/public/images/map/layericon/SWL_MANH_PS_YELLOW.png new file mode 100644 index 000000000..b7de1fbfd Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_MANH_PS_YELLOW.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPEW_PS.png b/apps/fo/public/images/map/layericon/SWL_SPEW_PS.png new file mode 100644 index 000000000..d260ea3a6 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPEW_PS.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPOT_PS.png b/apps/fo/public/images/map/layericon/SWL_SPOT_PS.png new file mode 100644 index 000000000..84c7992e4 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPOT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPOT_PS_BLACK.png b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_BLACK.png new file mode 100644 index 000000000..fe67f8f98 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_BLACK.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPOT_PS_BLUE.png b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_BLUE.png new file mode 100644 index 000000000..691f2f45e Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_BLUE.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPOT_PS_GREEN.png b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_GREEN.png new file mode 100644 index 000000000..d09c4bd97 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_GREEN.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPOT_PS_GREY.png b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_GREY.png new file mode 100644 index 000000000..32d0bf519 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_GREY.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_SPOT_PS_RED.png b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_RED.png new file mode 100644 index 000000000..a2da36f9b Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_SPOT_PS_RED.png differ diff --git a/apps/fo/public/images/map/layericon/SWL_VENT_PS.png b/apps/fo/public/images/map/layericon/SWL_VENT_PS.png new file mode 100644 index 000000000..834eeec21 Binary files /dev/null and b/apps/fo/public/images/map/layericon/SWL_VENT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPBD_ENTRC.png b/apps/fo/public/images/map/layericon/TL_SPBD_ENTRC.png new file mode 100644 index 000000000..8e7de8593 Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPBD_ENTRC.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPGF_LOC.png b/apps/fo/public/images/map/layericon/TL_SPGF_LOC.png new file mode 100644 index 000000000..74b0adac3 Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPGF_LOC.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPOT_NOIN.png b/apps/fo/public/images/map/layericon/TL_SPOT_NOIN.png new file mode 100644 index 000000000..4800beaeb Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPOT_NOIN.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPRD_BANO.png b/apps/fo/public/images/map/layericon/TL_SPRD_BANO.png new file mode 100644 index 000000000..8d08dca29 Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPRD_BANO.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPRD_CRSRD.png b/apps/fo/public/images/map/layericon/TL_SPRD_CRSRD.png new file mode 100644 index 000000000..57b5f2ca5 Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPRD_CRSRD.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPRD_NONM.png b/apps/fo/public/images/map/layericon/TL_SPRD_NONM.png new file mode 100644 index 000000000..e68c05ab6 Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPRD_NONM.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPRD_RENM.png b/apps/fo/public/images/map/layericon/TL_SPRD_RENM.png new file mode 100644 index 000000000..4800beaeb Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPRD_RENM.png differ diff --git a/apps/fo/public/images/map/layericon/TL_SPRD_RWNM.png b/apps/fo/public/images/map/layericon/TL_SPRD_RWNM.png new file mode 100644 index 000000000..e68c05ab6 Binary files /dev/null and b/apps/fo/public/images/map/layericon/TL_SPRD_RWNM.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_BMAN_PS.png b/apps/fo/public/images/map/layericon/UFL_BMAN_PS.png new file mode 100644 index 000000000..f2f64a641 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_BMAN_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_BOPC_PS.png b/apps/fo/public/images/map/layericon/UFL_BOPC_PS.png new file mode 100644 index 000000000..0f8e99f45 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_BOPC_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_BPIP_PS.png b/apps/fo/public/images/map/layericon/UFL_BPIP_PS.png new file mode 100644 index 000000000..dcc3622a7 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_BPIP_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_BPOL_PS.png b/apps/fo/public/images/map/layericon/UFL_BPOL_PS.png new file mode 100644 index 000000000..4f1102989 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_BPOL_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_BTRS_PS.png b/apps/fo/public/images/map/layericon/UFL_BTRS_PS.png new file mode 100644 index 000000000..557c17089 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_BTRS_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_GMNT_PS.png b/apps/fo/public/images/map/layericon/UFL_GMNT_PS.png new file mode 100644 index 000000000..6081d8b00 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_GMNT_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_GPIP_PS.png b/apps/fo/public/images/map/layericon/UFL_GPIP_PS.png new file mode 100644 index 000000000..5d7e86fd2 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_GPIP_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_GPRS_PS.png b/apps/fo/public/images/map/layericon/UFL_GPRS_PS.png new file mode 100644 index 000000000..79c120f20 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_GPRS_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_GVAL_PS.png b/apps/fo/public/images/map/layericon/UFL_GVAL_PS.png new file mode 100644 index 000000000..e8f7cde2e Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_GVAL_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_KMAN_PS.png b/apps/fo/public/images/map/layericon/UFL_KMAN_PS.png new file mode 100644 index 000000000..764ce8ba5 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_KMAN_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_KPOL_PS.png b/apps/fo/public/images/map/layericon/UFL_KPOL_PS.png new file mode 100644 index 000000000..d9fa712cf Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_KPOL_PS.png differ diff --git a/apps/fo/public/images/map/layericon/UFL_VALV_PS.png b/apps/fo/public/images/map/layericon/UFL_VALV_PS.png new file mode 100644 index 000000000..1b5d70178 Binary files /dev/null and b/apps/fo/public/images/map/layericon/UFL_VALV_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_FIRE_PS.png b/apps/fo/public/images/map/layericon/WTL_FIRE_PS.png new file mode 100644 index 000000000..4137a3f74 Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_FIRE_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_FLOW_PS.png b/apps/fo/public/images/map/layericon/WTL_FLOW_PS.png new file mode 100644 index 000000000..285f7edfa Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_FLOW_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_MANH_PS.png b/apps/fo/public/images/map/layericon/WTL_MANH_PS.png new file mode 100644 index 000000000..5f2a57cfe Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_MANH_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_META_PS.png b/apps/fo/public/images/map/layericon/WTL_META_PS.png new file mode 100644 index 000000000..22c7af31b Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_META_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_PIPE_PS.png b/apps/fo/public/images/map/layericon/WTL_PIPE_PS.png new file mode 100644 index 000000000..c9c6863f6 Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_PIPE_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_RSRV_PS.png b/apps/fo/public/images/map/layericon/WTL_RSRV_PS.png new file mode 100644 index 000000000..d7b307a61 Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_RSRV_PS.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_RSRV_PS_GRAY.png b/apps/fo/public/images/map/layericon/WTL_RSRV_PS_GRAY.png new file mode 100644 index 000000000..33a667a1e Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_RSRV_PS_GRAY.png differ diff --git a/apps/fo/public/images/map/layericon/WTL_VALV_PS.png b/apps/fo/public/images/map/layericon/WTL_VALV_PS.png new file mode 100644 index 000000000..ca2b4788b Binary files /dev/null and b/apps/fo/public/images/map/layericon/WTL_VALV_PS.png differ diff --git a/apps/fo/public/images/map/layericon/default.png b/apps/fo/public/images/map/layericon/default.png new file mode 100644 index 000000000..92bf1f587 Binary files /dev/null and b/apps/fo/public/images/map/layericon/default.png differ diff --git a/apps/fo/public/images/map/layericon/reset.png b/apps/fo/public/images/map/layericon/reset.png new file mode 100644 index 000000000..45f889206 Binary files /dev/null and b/apps/fo/public/images/map/layericon/reset.png differ diff --git a/apps/fo/public/images/map/toolbar/arrow_down.png b/apps/fo/public/images/map/toolbar/arrow_down.png new file mode 100644 index 000000000..88590ecaf Binary files /dev/null and b/apps/fo/public/images/map/toolbar/arrow_down.png differ diff --git a/apps/fo/public/images/map/toolbar/arrow_right.png b/apps/fo/public/images/map/toolbar/arrow_right.png new file mode 100644 index 000000000..7090c37dd Binary files /dev/null and b/apps/fo/public/images/map/toolbar/arrow_right.png differ diff --git a/apps/fo/public/images/map/toolbar/backward_icon.png b/apps/fo/public/images/map/toolbar/backward_icon.png new file mode 100644 index 000000000..3a853475e Binary files /dev/null and b/apps/fo/public/images/map/toolbar/backward_icon.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_area.png b/apps/fo/public/images/map/toolbar/btn_cont_area.png new file mode 100644 index 000000000..044562b55 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_area.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_area_on.png b/apps/fo/public/images/map/toolbar/btn_cont_area_on.png new file mode 100644 index 000000000..0129ff1bd Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_area_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_next.png b/apps/fo/public/images/map/toolbar/btn_cont_next.png new file mode 100644 index 000000000..075111dcf Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_next.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_next_on.png b/apps/fo/public/images/map/toolbar/btn_cont_next_on.png new file mode 100644 index 000000000..4fdbe3a00 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_next_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_prev.png b/apps/fo/public/images/map/toolbar/btn_cont_prev.png new file mode 100644 index 000000000..7e3fae7d2 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_prev.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_prev_on.png b/apps/fo/public/images/map/toolbar/btn_cont_prev_on.png new file mode 100644 index 000000000..fb2d55cfd Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_prev_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_print.png b/apps/fo/public/images/map/toolbar/btn_cont_print.png new file mode 100644 index 000000000..82596b83d Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_print.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_print_on.png b/apps/fo/public/images/map/toolbar/btn_cont_print_on.png new file mode 100644 index 000000000..bb7f27334 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_print_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_ruler.png b/apps/fo/public/images/map/toolbar/btn_cont_ruler.png new file mode 100644 index 000000000..501e460a4 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_ruler.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_ruler_on.png b/apps/fo/public/images/map/toolbar/btn_cont_ruler_on.png new file mode 100644 index 000000000..676a81bcc Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_ruler_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_save.png b/apps/fo/public/images/map/toolbar/btn_cont_save.png new file mode 100644 index 000000000..c057b9377 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_save.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_save_on.png b/apps/fo/public/images/map/toolbar/btn_cont_save_on.png new file mode 100644 index 000000000..bf8e6d623 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_save_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_sketch.png b/apps/fo/public/images/map/toolbar/btn_cont_sketch.png new file mode 100644 index 000000000..38a5f507f Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_sketch.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_sketch_on.png b/apps/fo/public/images/map/toolbar/btn_cont_sketch_on.png new file mode 100644 index 000000000..79fb54e03 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_sketch_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_zoomin.png b/apps/fo/public/images/map/toolbar/btn_cont_zoomin.png new file mode 100644 index 000000000..ac32fbab0 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_zoomin.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_zoomin_on.png b/apps/fo/public/images/map/toolbar/btn_cont_zoomin_on.png new file mode 100644 index 000000000..f258a965f Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_zoomin_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_zoomout.png b/apps/fo/public/images/map/toolbar/btn_cont_zoomout.png new file mode 100644 index 000000000..2ee0b5161 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_zoomout.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_cont_zoomout_on.png b/apps/fo/public/images/map/toolbar/btn_cont_zoomout_on.png new file mode 100644 index 000000000..58dcbfdae Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_cont_zoomout_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_add.png b/apps/fo/public/images/map/toolbar/btn_edit_add.png new file mode 100644 index 000000000..fe4f0993f Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_add.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_add_on.png b/apps/fo/public/images/map/toolbar/btn_edit_add_on.png new file mode 100644 index 000000000..37a836298 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_add_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_del.png b/apps/fo/public/images/map/toolbar/btn_edit_del.png new file mode 100644 index 000000000..3bb372ab9 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_del.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_del_on.png b/apps/fo/public/images/map/toolbar/btn_edit_del_on.png new file mode 100644 index 000000000..89e558739 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_del_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_next.png b/apps/fo/public/images/map/toolbar/btn_edit_next.png new file mode 100644 index 000000000..df4913c90 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_next.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_next_on.png b/apps/fo/public/images/map/toolbar/btn_edit_next_on.png new file mode 100644 index 000000000..4e5540b55 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_next_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_prev.png b/apps/fo/public/images/map/toolbar/btn_edit_prev.png new file mode 100644 index 000000000..e94bf6d42 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_prev.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_prev_on.png b/apps/fo/public/images/map/toolbar/btn_edit_prev_on.png new file mode 100644 index 000000000..8645dedfa Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_prev_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_save.png b/apps/fo/public/images/map/toolbar/btn_edit_save.png new file mode 100644 index 000000000..5f01dd55c Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_save.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_save_on.png b/apps/fo/public/images/map/toolbar/btn_edit_save_on.png new file mode 100644 index 000000000..b035687d6 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_save_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_slt.png b/apps/fo/public/images/map/toolbar/btn_edit_slt.png new file mode 100644 index 000000000..aaf2bbd08 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_slt.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_slt_on.png b/apps/fo/public/images/map/toolbar/btn_edit_slt_on.png new file mode 100644 index 000000000..02d035a74 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_slt_on.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_snap.png b/apps/fo/public/images/map/toolbar/btn_edit_snap.png new file mode 100644 index 000000000..fc91d4304 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_snap.png differ diff --git a/apps/fo/public/images/map/toolbar/btn_edit_snap_on.png b/apps/fo/public/images/map/toolbar/btn_edit_snap_on.png new file mode 100644 index 000000000..f856d8fbe Binary files /dev/null and b/apps/fo/public/images/map/toolbar/btn_edit_snap_on.png differ diff --git a/apps/fo/public/images/map/toolbar/close_icon.png b/apps/fo/public/images/map/toolbar/close_icon.png new file mode 100644 index 000000000..e8e76d5d7 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/close_icon.png differ diff --git a/apps/fo/public/images/map/toolbar/ico_gis_sprite.png b/apps/fo/public/images/map/toolbar/ico_gis_sprite.png new file mode 100644 index 000000000..0ee4b5985 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/ico_gis_sprite.png differ diff --git a/apps/fo/public/images/map/toolbar/icon-viewclose.png b/apps/fo/public/images/map/toolbar/icon-viewclose.png new file mode 100644 index 000000000..d93ce9c05 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/icon-viewclose.png differ diff --git a/apps/fo/public/images/map/toolbar/img_base_map.png b/apps/fo/public/images/map/toolbar/img_base_map.png new file mode 100644 index 000000000..7f96fcf5e Binary files /dev/null and b/apps/fo/public/images/map/toolbar/img_base_map.png differ diff --git a/apps/fo/public/images/map/toolbar/img_network_map.png b/apps/fo/public/images/map/toolbar/img_network_map.png new file mode 100644 index 000000000..df80af6dc Binary files /dev/null and b/apps/fo/public/images/map/toolbar/img_network_map.png differ diff --git a/apps/fo/public/images/map/toolbar/img_road_map.png b/apps/fo/public/images/map/toolbar/img_road_map.png new file mode 100644 index 000000000..02d015d12 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/img_road_map.png differ diff --git a/apps/fo/public/images/map/toolbar/img_settle_map.png b/apps/fo/public/images/map/toolbar/img_settle_map.png new file mode 100644 index 000000000..1a93f50e8 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/img_settle_map.png differ diff --git a/apps/fo/public/images/map/toolbar/loading.gif b/apps/fo/public/images/map/toolbar/loading.gif new file mode 100644 index 000000000..7c3683bb4 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/loading.gif differ diff --git a/apps/fo/public/images/map/toolbar/map_pin.png b/apps/fo/public/images/map/toolbar/map_pin.png new file mode 100644 index 000000000..b6cc2f6aa Binary files /dev/null and b/apps/fo/public/images/map/toolbar/map_pin.png differ diff --git a/apps/fo/public/images/map/toolbar/toggle_off.png b/apps/fo/public/images/map/toolbar/toggle_off.png new file mode 100644 index 000000000..d5dea9f67 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/toggle_off.png differ diff --git a/apps/fo/public/images/map/toolbar/toggle_on.png b/apps/fo/public/images/map/toolbar/toggle_on.png new file mode 100644 index 000000000..9cb573875 Binary files /dev/null and b/apps/fo/public/images/map/toolbar/toggle_on.png differ diff --git a/apps/fo/public/mainDashBoard.xlsx b/apps/fo/public/mainDashBoard.xlsx new file mode 100644 index 000000000..9557d13d2 Binary files /dev/null and b/apps/fo/public/mainDashBoard.xlsx differ diff --git a/apps/fo/public/next.svg b/apps/fo/public/next.svg new file mode 100644 index 000000000..5174b28c5 --- /dev/null +++ b/apps/fo/public/next.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> \ No newline at end of file diff --git a/apps/fo/public/vercel.svg b/apps/fo/public/vercel.svg new file mode 100644 index 000000000..770539603 --- /dev/null +++ b/apps/fo/public/vercel.svg @@ -0,0 +1 @@ +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg> \ No newline at end of file diff --git a/apps/fo/public/window.svg b/apps/fo/public/window.svg new file mode 100644 index 000000000..b2b2a44f6 --- /dev/null +++ b/apps/fo/public/window.svg @@ -0,0 +1 @@ +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg> \ No newline at end of file diff --git a/apps/fo/tailwind.config.ts b/apps/fo/tailwind.config.ts new file mode 100644 index 000000000..33ac9204f --- /dev/null +++ b/apps/fo/tailwind.config.ts @@ -0,0 +1,12 @@ +import type { Config } from "tailwindcss"; +import presetConfig from "../../tailwind.config.preset"; + +export default { + presets: [presetConfig], + darkMode: ["class", "[data-mode='dark']"], + content: [ + "./app/**/*.{js,ts,jsx,tsx,mdx}", + "../../packages/common/business_components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", + "../../packages/common/components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", + ], +} satisfies Config; \ No newline at end of file diff --git a/apps/fo/tsconfig.json b/apps/fo/tsconfig.json new file mode 100644 index 000000000..493584782 --- /dev/null +++ b/apps/fo/tsconfig.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noImplicitAny": false, + "baseUrl": ".", + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "verbatimModuleSyntax": true, + "strictNullChecks": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"], + "@common/components/*": ["../../packages/common/components/src/*"], + "@common/components": ["../../packages/common/components/src/index"], + "@common/business_components/*": [ + "../../packages/common/business_components/src/*" + ], + "@common/business_components": [ + "../../packages/common/business_components/src/index" + ], + "@common/network": ["../../packages/common/network"] + } + }, + "include": [ + ".eslintrc.*", + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.js", + "**/*.cjs", + "**/*.mjs", + "**/*.jsx", + "**/*.json", + ".next/types/**/*.ts" + ], + "exclude": ["**/node_modules", "**/.*/*"] +}