Merge commit 'e36bfe726786608eae8794c1ef1a7ea761d13ce5' into iipbs/wr/hasuReg

This commit is contained in:
leedj312
2025-07-02 16:22:05 +09:00
119 changed files with 4696 additions and 560 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
const BASE_URL = 'http://con.mind-one.co.kr:11083/sewerage-service/api';
export const postData = async (url: string, data: object): Promise<any> => {
const options: RequestInit = {
@@ -1,4 +1,4 @@
import { VerticalTable } from '@/components/ui/verticalTable';
import { VerticalTable } from '../../../components/ui/verticalTable';
import React, { useState } from 'react'
import InputSection from '../InputSection';
import { handleInputChange } from '../common';
@@ -1,5 +1,5 @@
import Modal from '@/components/ui/modal';
import { VerticalTable } from '@/components/ui/verticalTable';
import Modal from '../../../components/ui/modal';
import { VerticalTable } from '../../../components/ui/verticalTable';
import { UiTable } from '@common/business_components/ui';
import { Button, TableCell } from '@common/components/ui';
import React, { useState } from 'react'
@@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { type ChartConfig, MainChart, CHART_FORM } from "@common/business_components";
import Modal from '@/components/ui/modal';
import Modal from '../../../../components/ui/modal';
import { Button } from '@common/components/ui';
type ModalOpenProp = {
isModalOpen: boolean,
@@ -1,4 +1,4 @@
import { VerticalTable } from '@/components/ui/verticalTable';
import { VerticalTable } from '../../../components/ui/verticalTable';
import React from 'react'
const inputColumn = [
{ id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
@@ -1,5 +1,5 @@
// 하수맨홀
import { VerticalTable } from '@/components/ui/verticalTable';
import { VerticalTable } from '../../../components/ui/verticalTable';
import React from 'react'
const inputColumn = [
{ id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
@@ -1,4 +1,4 @@
import { VerticalTable } from '@/components/ui/verticalTable';
import { VerticalTable } from '../../../components/ui/verticalTable';
import React from 'react'
const inputColumn = [
{ id: "swtrmt_zone_cd", title: "처리구역", type: "input" },
+1
View File
@@ -106,6 +106,7 @@ const Map = () => {
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
</div>
);
+201
View File
@@ -0,0 +1,201 @@
'use client'
import { getData, putData } from "../../../sn/app/components/lib/api";
import Modal from "../../../sn/app/components/ui/modal";
import { VerticalTable } from "../../../sn/app/components/ui/verticalTable";
import { useGisContext } from "@common/business_components/ui/map/Gis";
import { Button } from "@common/components/ui";
import { useEffect, useRef, useState } from "react";
import { getLayerContentComponent } from "../../../sn/app/map/components/common";
// TODO:
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => {
const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; };
// const featureId = featureData.getId(); // 피처의 id 가져오기
const featureId = typeof featureData.getId === 'function'
? featureData.getId()
: featureData.fid;
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
const [tableData, setTableData] = useState<any>(null); // 피처 데이터
const [mntrngsData, setMntrngsData] = useState(false); // 수량/수질 측정 정보
const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
const [buttonChk, setButtonChk] = useState(true)
const [fetchData, setfetchData] = useState<any>(); // api 호출여부
const contentRef = useRef<HTMLDivElement>(null);
const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태
const [chkMainSsc, setChkMainSsc] = useState(false); // 중점우수토실 여부
// 피쳐데이터의 현재 레이어 이름 가져오는 함수
const getSelectedLayers = () => {
let result = null;
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
let featureLayer: { get: (key: string) => any } = { get: (key: string) => null }; // 피처가 속한 레이어를 찾기 위한 변수
layers.forEach((layer: any) => {
const source = layer.getSource();
if (source && source.hasFeature && featureData
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
featureLayer = layer; // 피처가 속한 레이어 찾기
}
});
// SearchModal 클릭 이벤트와 Gis 클릭 이벤트 구분하기
if (featureLayer.get('lyrNm') === null) {
if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설';
else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역';
else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
}
else result = featureLayer.get('lyrNm');
return result;
// return featureLayer.get('lyrNm'); // 레이어 이름 출력
};
useEffect(() => {
const fetchData = async () => {
if (featureId) {
const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기
setLayerNm(selectedLayerName); // 상태 업데이트
const layerId = featureId.split('.')[0]
let data = await getData(`v1/fclts/${featureId}?lyrTypeNm=${layerId}`);
setfetchData(data)
switch (selectedLayerName) {
case '하수처리시설':
setButtonChk(false)
break;
case '하수처리구역':
setButtonChk(false)
break;
case '중점우수토실':
setButtonChk(false)
setChkMainSsc(true)
// 데이터 전처리: 합쳐진 필드 추가
data = data ? {
...data,
discharge_pipe_spec: `직경: ${data.pipe_diameter || '-'} / 높이: ${data.pipe_hg || '-'} / 길이: ${data.pipe_lt || '-'}`
} : {};
// 수량/수질측정 정보 데이터 호출
const additionalResponse = await getData(`v1/onsemiro/swgRain/${data.adst_dstr_cd}/${data.manage_no}/mntrngs`);
setMntrngsData(additionalResponse.content);
break;
case '우수토실':
setButtonChk(true)
break;
case '하수관로':
setButtonChk(true)
break;
case '오수펌프':
setButtonChk(true)
break;
case '빗물펌프':
setButtonChk(true)
break;
case '하수맨홀':
setButtonChk(true)
break;
default:
break;
}
setTableData(data);
};
}
fetchData();
}, [featureId]);
// 저장 시 모달 띄우기
const handleSaveClick = () => {
setIsModalOpen(true); // 모달 열기
};
const handleModalSave = () => {
setIsModalOpen(false); // 모달 닫기
btnClickEvent(); // 저장 이벤트 실행
};
const handleModalClose = () => {
setIsModalOpen(false); // 모달 닫기
};
const btnClickEvent = async () => {
const bodyData: Record<string, any> = {}
if (buttonChk && contentRef.current) {
const inputElements = contentRef.current.querySelectorAll('input');
const selectElements = contentRef.current.querySelectorAll('select');
// 카멜케이스 변환 함수
const toCamelCase = (str: string) =>
str.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
// select 태그의 값 가져오기
selectElements.forEach((select) => {
const camelCaseId = toCamelCase(select.id);
bodyData[camelCaseId] = select.value;
});
// input 태그의 값 가져오기
inputElements.forEach((input) => {
const camelCaseId = toCamelCase(input.id);
bodyData[camelCaseId] = input.value;
});
if (layerNm === '하수관로') bodyData['lyrTypeNm'] = 'tb_ms_swage_ppl'
else if (layerNm === '우수토실') bodyData['lyrTypeNm'] = 'tb_ms_stofcbr'
else if (layerNm === '오수펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn'
else if (layerNm === '빗물펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn'
else if (layerNm === '하수맨홀') bodyData['lyrTypeNm'] = 'tb_ms_swage_mnhl'
bodyData['fid'] = featureId
const data = await putData(`v1/fclts/${featureId}`, bodyData);
if (data.status == 200) {
alert('저장되었습니다')
setTableData(data)
setfetchData(data)
}
}
}
type ContentComponentProps = {
featureData: any;
chkMainSsc: boolean;
additionalData: any;
};
const ContentComponent = layerNm ? getLayerContentComponent(layerNm) as React.ComponentType<ContentComponentProps> : null;
return (
<div id="popup" className="absolute top-0 right-0 w-[350px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-[102]">
<div className="card-header bg-primary flex items-center justify-between p-4">
<div className="flex mx-3 flex-col">
<h3 className="text-2xl font-medium text-white text-label">{layerNm} 정보</h3>
<div className="font-medium text-white text-label">(00구 00리 하수관로)</div>
</div>
<button
onClick={onClose}
className="text-white hover:text-gray-700 focus:outline-none"
aria-label="닫기"
>
✕
</button>
</div>
<div className="overflow-y-auto overflow-x-hidden flex-1" ref={contentRef}>
<VerticalTable
headName='LX 연계정보'
tableData={[tableData]}
headlist={headlist}
pageSize={20}
total={0}
children={undefined}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}
/>
<div className="w-full mx-5 flex items-center justify-end space-x-2">
{buttonChk ? <Button onClick={handleSaveClick} className="px-4 mr-5 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700">
저장
</Button> : null}
</div>
<Modal isModalOpen={isModalOpen} title="자체입력정보 저장" setIsModalOpen={setIsModalOpen} closeModal={handleModalClose} saveModal={handleModalSave}><div className="text-lg">정보를 저장하시겠습니까?</div></Modal>
{ContentComponent &&
<ContentComponent
featureData={fetchData}
chkMainSsc={chkMainSsc}
additionalData={mntrngsData}
/>
}
</div>
</div>
)
}
export default FeatureClickEventPopup;
+621
View File
@@ -0,0 +1,621 @@
"use client";
import {
Form,
FormField,
FormUi,
SelectBox,
} from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
hdtflZod,
type customHtdflBdst,
} from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
import { useEffect } from "react";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class } from "@/components/lib/class/comClass";
export default function HtdflPrfmncSttnPopup({
htdflForm,
isModal,
}: {
htdflForm: customHtdflBdst;
isModal: boolean;
}) {
const { code, htdflBdstUpdate, htdflBdstReg } = useWrStore((state) => state);
const defaultValues: z.infer<typeof hdtflZod> = hdtflZod.parse({});
const form = useForm<z.infer<typeof hdtflZod>>({
resolver: zodResolver(hdtflZod),
defaultValues: defaultValues,
});
const onSubmit = (values: z.infer<typeof hdtflZod>) => {
if (isModal) {
htdflBdstReg(values, '41');
} else {
htdflBdstUpdate(values, '41');
}
};
const cleanHtdflData = (raw: any): z.infer<typeof hdtflZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
chgBuilding: (raw?.chgBuilding || []).map((item: any) => ({
...item,
archChgPrmsnDt:
item?.archChgPrmsnDt === null
? new Date()
: new Date(item?.archChgPrmsnDt),
bdstChgNm: item?.bdstChgNm ?? "", // null → ""
})),
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
htdflObj: {
...item?.htdflObj,
},
})),
};
};
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
if (hdtflZod) form.reset(cleanHtdflData(htdflForm));
}, [htdflForm]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* <div className="flex justify-between items-center mx-3"> */}
<div>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">온배수 시설 정보</h1>
</div>
</div>
{isModal && htdflForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="text-xl col-span-10 mt-4">
온배수 시설 정보{" "}
</h1>
) : (
<></>
)}
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.etgtType"
render={({ field }) => (
<FormUi label="발전유형(코드정비후 수정)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.fclCpct" // 이름 이상함.
render={({ field }) => (
<FormUi label="설비용량">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.plwtYn"
render={({ field }) => (
<FormUi label="담수화 여부">
<SelectBox
className={""}
label={""}
selectClass={selectClass}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-4">
<h1 className={formH1Class}>
온배수 시설 운영현황
</h1>
</div>
<div className="grid grid-cols-10 gap-1 mt-4">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.orgwtWtritSpt"
render={({ field }) => (
<FormUi label="원수취수 지점(코드정비후 수정)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
value={field.value ?? 0}
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.orgwtOcrnProcs"
render={({ field }) => (
<FormUi label="원수발생 공정">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.wtritEntrcTp"
render={({ field }) => (
<FormUi label="취수구 온도">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.amtgnr"
render={({ field }) => (
<FormUi label="발생량(m³/년)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.prcsProcsYn"
render={({ field }) => (
<FormUi label="처리공정">
<SelectBox
className={""}
label={""}
selectClass={selectClass}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.prcsFcltCpct"
render={({ field }) => (
<FormUi label="처리시설 용량(m³/년)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.fmYn"
render={({ field }) => (
<FormUi label="유량계유무">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.utztnCpct"
render={({ field }) => (
<FormUi label="이용량(m³/년)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.utztnUsg"
render={({ field }) => (
<FormUi label="이용용도">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>
요구수질
</h1>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.grssClfg"
render={({ field }) => (
<FormUi label="총대장균군">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.tn"
render={({ field }) => (
<FormUi label="총질소">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.combResdch"
render={({ field }) => (
<FormUi label="결합잔류염소">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6 ">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.tph"
render={({ field }) => (
<FormUi label="총인">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.trbt"
render={({ field }) => (
<FormUi label="탁도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.ph"
render={({ field }) => (
<FormUi label="수소이온농도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.bod"
render={({ field }) => (
<FormUi label="생물학적 산소요구량">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.clIonQnt"
render={({ field }) => (
<FormUi label="염화물">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.smll"
render={({ field }) => (
<FormUi label="냄새">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.cdtnv"
render={({ field }) => (
<FormUi label="전기전도도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.crmty"
render={({ field }) => (
<FormUi label="색도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.emsnExitTp"
render={({ field }) => (
<FormUi label="배출구온도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
</div>
);
}
+160
View File
@@ -0,0 +1,160 @@
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import { useEffect, useRef, useState, useCallback } from "react";
import FeatureClickEventPopup from "../featureClickEventPopup";
import { mapService } from "../service";
import { MapSearchBar } from "../mapSearchBar";
import NewWindowPopup from "../newWindowPopup";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
const Map = () => {
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
const [selectedFeature, setSelectedFeature] = useState(null); // 선택된 피처 상태
const gisMaps = useRef<any | null>(null);
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
const [checkedLayers, setCheckedLayers] = useState<string[]>([]);
const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]);
const fetchData = async () => {
try {
const response = await mapService.getLyrStngs();
setTreeData(response); // API 응답 저장
} catch (error) {
console.error("데이터 불러오기 실패:", error);
}
};
// 팝업 닫기 핸들러를 useCallback으로 메모이제이션
const handlePopupClose = useCallback(() => {
setIsOpen(false);
setFeatureData(null);
}, []);
// 컴포넌트 마운트 시 1회 실행
useEffect(() => {
fetchData();
}, []);
// 체크된 레이어 정보를 받는 핸들러
const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: any[]) => {
setCheckedLayers(checkedIds);
setCheckedLayersData(checkedNodesData);
};
// SearchModal, GisContent 등에서 호출
const onFeatureSelect = (data: any) => {
let map = gisMaps.current;
if (!map) {
console.warn("지도가 초기화되지 않았습니다.");
return;
}
// 항상 같은 select 객체 사용
const select = GisApp.Module.select as any;
if (!select) return;
// 1. 기존 선택 초기화
select.resetSelectedFeature();
// 2. data가 OpenLayers Feature 인스턴스인지 확인
let foundFeature = 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: any) => {
const source = layer.getSource?.();
if (source?.getFeatureById) {
const feature = source.getFeatureById(String(featureId).trim());
if (feature) foundFeature = feature;
}
});
}
// 3. 피처 선택
if (foundFeature) {
let geometry = foundFeature.getGeometry();
if (geometry) {
let 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);
};
// MapSearchBar 래퍼 컴포넌트
// const CustomMapSearchBar = (props: any) => {
// return (
// <MapSearchBar
// {...props}
// searchType="공급처 후보지 조회"
// showApplyButton={false}
// />
// );
// };
return (
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={false}
gisMaps={gisMaps}
isMiniMap={true}
searchView={undefined}
// searchView={(props: any) => (
// <MapSearchBar
// searchType="공급처 후보지 조회"
// showApplyButton={false}
// checkedLayers={checkedLayers}
// checkedLayersData={checkedLayersData}
// {...props}
// />
// )
// }
detailView={(props: any) => (
<NewWindowPopup
onClose={handlePopupClose} // 메모이제이션된 함수 사용
checkedLayers={checkedLayers}
checkedLayersData={checkedLayersData}
{...props}
/>
)}
onFeatureSelect={undefined}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={handleCheckedLayersChange}
/>
</div>
);
};
export default Map;
+414
View File
@@ -0,0 +1,414 @@
// "use client"
// import { useWrStore } from "@/app/store";
// import SearchInputText from "@/components/ui/searchInput/searchInput";
// import type { EnvPartSelectDataType, ReturnType, SelectDataType } from "@common/business_components/ui";
// import { SelectBox } from "@common/business_components/ui";
// import { Button, Input, Label } from "@common/components/ui";
// import React, { useEffect, useMemo, useRef, useState } from "react";
// import { MapSearchFormLeft } from "./mapSearchForm";
// // 건물 검색 타입 정의
// interface BuildingInfo {
// bdstInfoPk: number;
// ctpvCd: number;
// sggCd: number;
// daddr: string;
// bdstNm: string;
// frstArchPrmsnDt: string;
// archArea: number;
// ctpvNm: string | null;
// sggNm: string | null;
// lat: number | null;
// lot: number | null;
// regDt: string;
// rgtr: string | null;
// mdfcnDt: string;
// mdfr: string | null;
// }
// type SearchBaseType = {
// searchType: string;
// selectSidoData?: SelectDataType[] | [];
// selectSigunData?: SelectDataType[] | [];
// selectRegionData?: SelectDataType[] | [];
// selectUnitWaterShedData?: SelectDataType[] | [];
// selectMiddleRegionData?: SelectDataType[] | [];
// selectEnvPartData?: EnvPartSelectDataType[] | [];
// onFeatureSelect: () => void;
// btnClass?: string;
// ref?: React.RefObject<ReturnType>;
// isAdmin?: boolean;
// leftClassName?: string;
// rightClassName?: string;
// showQueryButton?: boolean;
// showInitButton?: boolean;
// showApplyButton?: boolean;
// checkedLayers?: string[];
// checkedLayersData?: any[];
// }
// const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
// const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`);
// interface SearchModalProps {
// open: boolean;
// onOpenChange: (open: boolean) => void;
// onFeatureSelect: (row: any) => void;
// }
// interface SelectedDataTypeWithSggNm {
// text: string;
// val: string;
// sggNm: string;
// }
// export function MapSearchBar({
// searchType,
// selectSidoData,
// selectSigunData,
// selectRegionData,
// selectUnitWaterShedData,
// selectMiddleRegionData,
// selectEnvPartData,
// btnClass,
// onFeatureSelect,
// isAdmin = false,
// leftClassName,
// rightClassName,
// showQueryButton = true,
// showInitButton = true,
// showApplyButton = true,
// checkedLayers = [],
// checkedLayersData = [],
// }: SearchBaseType) {
// // 로딩바
// const [isLoading, setIsLoading] = useState(false);
// // 상세 정보 조회를 위한 상태 추가
// const [popupFeature, setPopupFeature] = useState<any>(null);
// const [isChHead, setIsChHead] = useState(false);
// const ref = useRef<ReturnType>({} as ReturnType);
// // zustand store에서 건물 검색 함수와 데이터 가져오기
// const {
// code,
// bdst,
// findBdst,
// wrMap,
// buildingSearchRes,
// buildingNmSearch,
// buildingNmSearchMultiple
// } = useWrStore((state) => state);
// const [selectedBuilding, setSelectedBuilding] = useState<BuildingInfo | null>(null);
// const [buildingSearchText, setBuildingSearchText] = useState("");
// const [buildingList, setBuildingList] = useState<BuildingInfo[]>([]);
// // 전체 목록
// const [partValue, setPartValue] = useState<string>("A");
// const [sidoValue, setSidoValue] = useState<string>("0");
// const [sigunValue, setSigunValue] = useState<string>("0");
// const [regionValue, setRegionValue] = useState<string>("0");
// const [untitWaterShedValue, setUntitWaterShedValue] = useState<string>("0");
// const [middleRegionValue, setMiddleRegionValue] = useState<string>("0");
// const [envPartValue, setEnvPartValue] = useState<string>("0");
// const [resetTrigger, setResetTrigger] = useState(false);
// const [sidoText, setSidoText] = useState<string>("0");
// const [sigunText, setSigunText] = useState<string>("0");
// // 공급처 후보지 조회 필터 목록
// const [selectedSido, setSelectedSido] = useState<SelectDataType>({ text: "전체", val: '0' });
// const [selectedSigun, setSelectedSigun] = useState<SelectDataType>({ text: "전체", val: '0' });
// const [selectedFclt, setSelectedFclt] = useState<SelectedDataTypeWithSggNm>({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태
// // 전체 시설
// let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }];
// const [selectedDong, setSelectedDong] = useState<SelectedDataTypeWithSggNm>({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동
// const filteredSigunData = useMemo(() => {
// if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }];
// return [
// { text: "전체", val: '0' },
// ...code.sggCd.filter((item: { val: string; }) => item.val.substring(0, 2) === selectedSido.val.substring(0, 2))
// ];
// }, [selectedSido]);
// const filteredFcltData = useMemo(() => {
// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }];
// return [
// { text: "전체", val: '0', sggNm: '전체' },
// ...allFclt.filter(item => item.sggNm === selectedSido.text)
// ];
// }, [selectedSigun]);
// const filteredDong = useMemo(() => {
// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }];
// return [
// { text: "전체", val: '0', sggNm: '전체' },
// // ...code.dong.filter(item => item.sggNm === selectedSido.text)
// ];
// }, [selectedSigun]);
// // 조회 함수
// const queryVal = () => {
// // 46 : 하수처리시설
// // 47 : 하수재이용시설
// // 48 : 빗물이용시설
// // 49 : 중수도시설
// // 50 : 온배수시설
// console.log("조회버튼 클릭: ", checkedLayers);
// if (checkedLayers && checkedLayers.includes('48')) {
// // searchBuildings
// }
// }
// // 초기화 함수
// const initVal = () => {
// setPartValue("A");
// setSidoValue("0");
// setSigunValue("0");
// setRegionValue("0");
// setUntitWaterShedValue("0");
// setMiddleRegionValue("0");
// setEnvPartValue("0");
// setSidoText("전체");
// setSigunText("전체");
// setSelectedSido({ text: "전체", val: '0' });
// setSelectedSigun({ text: "전체", val: '0' });
// setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' });
// setSelectedDong({ text: "전체", val: '0', sggNm: '전체' });
// setResetTrigger(true);
// };
// // 적용 버튼
// const applyVal = () => {
// onFeatureSelect();
// };
// useEffect(() => {
// if (resetTrigger) {
// setResetTrigger(false);
// }
// }, [resetTrigger]);
// // 시설 조회
// useEffect(() => {
// const fetchData = async () => {
// setIsLoading(true);
// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) {
// allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]);
// setIsLoading(false);
// return;
// }
// try {
// const queryParamsFacility = new URLSearchParams({
// lyrTypeNm: 'tb_il_swage_prcs_fclt',
// page: '0',
// size: '100000',
// sort: 'string',
// });
// const cqlFiltersFacility: string[] = [];
// if (selectedSigun.text && selectedSigun.val !== '0') {
// cqlFiltersFacility.push(`sgg_nm ILIKE '%${selectedSigun.text}%'`);
// }
// if (cqlFiltersFacility.length > 0) {
// queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and '));
// }
// console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
// const facilityRes = await fetch(`${API_URL}/v1/fclts?${queryParamsFacility.toString()}`, {
// method: "GET",
// headers: { accept: "application/json" }
// }).then(res => res.json());
// const facilities = (facilityRes.data || []).map((item: any) => ({
// text: item.swtrmtFcltNm,
// val: item.fid,
// sggNm: item.sggNm
// }));
// allFclt = (facilities);
// } catch (error) {
// console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
// allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]);
// } finally {
// setIsLoading(false);
// }
// };
// fetchData();
// }, [selectedSigun]);
// useEffect(() => {
// if (ref && ref.current) {
// const customRef = ref.current;
// customRef.partValue = partValue;
// customRef.sidoValue = sidoValue;
// customRef.sigunValue = sigunValue;
// customRef.regionValue = regionValue;
// customRef.unitWaterShedValue = untitWaterShedValue;
// customRef.middleRegionValue = middleRegionValue;
// customRef.envPartValue = envPartValue;
// customRef.sidoText = selectedSido.text;
// customRef.sigunText = selectedSigun.text;
// }
// }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]);
// // buildingSearchText가 변경될 때 API 호출
// useEffect(() => {
// if (buildingSearchText && buildingSearchText.length > 1) {
// if (checkedLayers && checkedLayers.length > 0) {
// // 다중 레이어 검색
// // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", buildingSearchText);
// buildingNmSearch("RNB", selectedSigun.val, buildingSearchText);
// } else {
// // 단일 레이어 검색
// // buildingNmSearch(checkedLayers[0], selectedSigun.val, buildingSearchText);
// }
// } else {
// // 검색 텍스트가 짧으면 리스트 초기화
// if (checkedLayers && checkedLayers.length > 0) {
// // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", "");
// } else {
// buildingNmSearch("RNB", selectedSigun.val || "", "");
// }
// }
// }, [buildingSearchText]);
// return (
// <div className="absolute left-1/2 -translate-x-1/2 bg-white flex items-center justify-center p-4 rounded-md shadow-lg w-auto min-w-fit">
// <div className={leftClassName ? leftClassName : `flex items-center justify-center space-x-2 `}>
// <div className="flex gap-8 items-center">
// {searchType == "공급처 후보지 조회" && (
// <>
// <SelectBox
// className="flex items-center"
// label={'시도'}
// selectArray={code.cityCd}
// onSelectValue={(val) => {
// const selected = code.cityCd.find((item: { val: string | number; }) => (item.val === val));
// setSelectedSido(selected || { text: "전체", val: '0' });
// setSelectedSigun({ text: "전체", val: '0' });
// }}
// value={selectedSido.val}
// selectClass={'w-32'}
// />
// <SelectBox
// className="flex items-center"
// label={'시군구'}
// selectArray={filteredSigunData}
// onSelectValue={(val) => {
// const selected = filteredSigunData.find(item => (item.val === val));
// setSelectedSigun(selected || { text: "전체", val: '0' });
// setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' });
// }}
// value={selectedSigun.val}
// selectClass={'w-32'}
// />
// <div className="flex items-center">
// <Label className="w-20 text-xl text-gray-600">시설명</Label>
// <div className="w-64">
// <SearchInputText
// label="시설명"
// initValue={buildingSearchText}
// setValue={(searchNm?: string) => {
// setBuildingSearchText(searchNm || "");
// }}
// setRtnData={function (rtnData: any): void {
// // 선택된 건물 데이터 처리
// // if (rtnData && rtnData.bdstInfoPk) {
// // setSelectedBuilding(rtnData);
// // setBuildingSearchText(rtnData.bdstNm || "");
// // }
// }}
// searchList={buildingSearchRes || []}
// />
// </div>
// </div>
// </>
// )}
// {searchType == "우리동네가뭄지도보기" && (
// <>
// <SelectBox
// className="flex items-center"
// label={'시도'}
// selectArray={code.cityCd}
// onSelectValue={(val) => {
// const selected = code.cityCd.find((item: { val: string | number; }) => (item.val === val));
// setSelectedSido(selected || { text: "전체", val: '0' });
// setSelectedSigun({ text: "전체", val: '0' });
// }}
// value={selectedSido.val}
// selectClass={'w-44'}
// />
// <SelectBox
// className="flex items-center"
// label={'시군구'}
// selectArray={filteredSigunData}
// onSelectValue={(val) => {
// const selected = filteredSigunData.find(item => (item.val === val));
// setSelectedSigun(selected || { text: "전체", val: '0' });
// setSelectedDong({ text: "전체", val: '0', sggNm: '전체' });
// }}
// value={selectedSigun.val}
// selectClass={'w-32'}
// />
// <SelectBox
// className="flex items-center mr-8"
// label={'행정동'}
// selectArray={filteredDong}
// onSelectValue={(val) => {
// const selected = filteredDong.find(item => (item.val === val));
// setSelectedDong(selected || { text: "전체", val: '0', sggNm: '전체' });
// }}
// value={selectedDong.val}
// selectClass={'w-32'}
// />
// </>
// )}
// {searchType == "지도보기" && (
// <div className="flex items-center gap-2 w-[800px]">
// <div className="flex min-w-0"> {/* SearchFormLeft 래퍼 */}
// <MapSearchFormLeft
// isShowPart={true}
// selectSidoData={code.cityCd}
// selectSigunData={filteredSigunData}
// selectRegionData={code.rvbsnCd}
// selectUnitWaterShedData={selectUnitWaterShedData}
// selectMiddleRegionData={selectMiddleRegionData}
// selectEnvPartData={selectEnvPartData}
// setRtnPartValue={setPartValue}
// setRtnSidoValue={setSidoValue}
// setRtnSigunValue={setSigunValue}
// setRtnRegionValue={setRegionValue}
// setRtnUnitWaterShedValue={setUntitWaterShedValue}
// setRtnMiddleRegionValue={setMiddleRegionValue}
// setRtnEnvPartValue={setEnvPartValue}
// setRtnEnvPartSidoValue={setSidoValue}
// setRtnEnvPartSigunValue={setSigunValue}
// setRtnSidoText={setSidoText}
// setRtnSigunText={setSigunText}
// resetTrigger={resetTrigger}
// isAdmin={isAdmin}
// children={undefined}
// />
// </div>
// <Label className="w-auto text-xl text-gray-600 whitespace-nowrap">시설명</Label>
// <div className="w-44">
// <Input className="mr-4" />
// </div>
// </div>
// )}
// </div>
// {/* 버튼 부분 */}
// <div className="flex items-center justify-center gap-4">
// {showQueryButton && <Button className={btnClass} onClick={queryVal}>조회</Button>}
// {showInitButton && <Button className={btnClass} onClick={initVal}>초기화</Button>}
// {showApplyButton && <Button className={btnClass} onClick={applyVal}>적용</Button>}
// </div>
// </div>
// </div>
// )
// }
+340
View File
@@ -0,0 +1,340 @@
"use client"
import React, { useEffect, useState } from 'react';
import type { SelectDataType } from '@common/business_components/ui/search_input_select';
import { SelectBox } from '@common/business_components/ui/select';
/**
* 검색 폼 왼쪽 컴포넌트 타입
*
* @param isShowPart 구분 조회 여부
* @param children 자식 컴포넌트
* @param selectSidoData 시도 데이터
* @param selectSigunData 시군구 데이터
* @param selectRegionData 유역 데이터
* @param selectUnitWaterShedData 단위유역 데이터
* @param selectMiddleRegionData 중권역 데이터
* @param selectEnvPartData 환경청 데이터
* @param selectEnvPartSidoData 환경청 시도 데이터
* @param selectEnvPartSigunData 환경청 시군구 데이터
* @param setRtnPartValue 부분 값 변경 핸들러
*/
type SearchFormLeftProps = {
isShowPart?: boolean | undefined;
children: React.ReactNode;
selectSidoData?: SelectDataType[] | [];
selectSigunData?: SelectDataType[] | [];
selectRegionData?: SelectDataType[] | [];
selectUnitWaterShedData?: SelectDataType[] | [];
selectMiddleRegionData?: SelectDataType[] | [];
selectEnvPartData?: EnvPartSelectDataType[] | [];
selectEnvPartSidoData?: SelectDataType[] | [];
selectEnvPartSigunData?: SelectDataType[] | [];
setRtnPartValue?: (value: string) => void;
setRtnSidoValue?: (value: string) => void;
setRtnSigunValue?: (value: string) => void;
setRtnRegionValue?: (value: string) => void;
setRtnUnitWaterShedValue?: (value: string) => void;
setRtnMiddleRegionValue?: (value: string) => void;
setRtnEnvPartValue?: (value: string) => void;
setRtnEnvPartSidoValue?: (value: string) => void;
setRtnEnvPartSigunValue?: (value: string) => void;
setRtnSidoText?: (value: string) => void;
setRtnSigunText?: (value: string) => void;
resetTrigger?: boolean;
isAdmin?: boolean;
}
export type EnvPartSelectDataType = SelectDataType & {
adstDliStrCd: string,
}
const selectPartData: SelectDataType[] = [
{ text: "행정별", val: 'A' },
{ text: "유역별", val: 'R' },
{ text: "환경청별", val: 'G' },
];
function MapSearchFormLeft({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSidoText, setRtnSigunValue, setRtnSigunText, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false, isAdmin = false }: SearchFormLeftProps) {
/******************선택된 데이터******************/
const [partValue, setPartValue] = useState<string>('A');
//행정별 시도 시군구
const [sidoValue, setSidoValue] = useState<string>('0');
const [sigunValue, setSigunValue] = useState<string>('0');
const [sidoText, setSidoText] = useState<string>('');
const [sigunText, setSigunText] = useState<string>('');
//유역별
const [regionValue, setRegionValue] = useState<string>('0');
const [unitWaterShedValue, setUnitWaterShedValue] = useState<string>('0');
const [middleRegionValue, setMiddleRegionValue] = useState<string>('0');
//환경청별 시도 시군구
const [envPartValue, setEnvPartValue] = useState<string>('0');
// 환경청
const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState<SelectDataType[]>([]);
/****************** 셀렉트 리스트 데이터. ******************/
// 조회된 조건에 맞는 시도 시군 유역
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [filteredUnitWaterShedData, setFilteredUnitWaterShedData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [filteredMiddleRegionData, setFilteredMiddleRegionData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
// 조회된 조건에 맞는 환경청 시도 시군
const [filteredEnvPartData, setFilteredEnvPartData] = useState<EnvPartSelectDataType[]>([{ text: "전체", val: '0', adstDliStrCd: '' }]);
const [filteredEnvPartSidoData, setFilteredEnvPartSidoData] = useState<EnvPartSelectDataType[]>([{ text: "전체", val: '0', adstDliStrCd: '' }]);
const [filteredEnvPartSigunData, setFilteredEnvPartSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
// 시군구 세팅
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 initializeUnitWaterShedData = (regionCd: string) => {
if (regionCd === '0') {
setUnitWaterShedValue('0');
setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectUnitWaterShedData.filter((item: SelectDataType) => item.val.substring(0, 1) === regionCd)
];
setUnitWaterShedValue('0');
setFilteredUnitWaterShedData(newFilteredData);
}
}
// 중권역 세팅
const initializeMiddleRegionData = (unitWaterShedCd: string) => {
if (unitWaterShedCd === '0') {
setMiddleRegionValue('0');
setFilteredMiddleRegionData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectMiddleRegionData.filter((item: SelectDataType) => item.val.substring(0, 2) === unitWaterShedCd)
];
setMiddleRegionValue('0');
setFilteredMiddleRegionData(newFilteredData);
}
}
// 환경청 세팅 - 시도
const initializeCityDataByEnvPartSido = (envPartCd: string) => {
const foundEnvPart = selectEnvPartData.find((item: EnvPartSelectDataType) => item.val === envPartCd);
const cityArray = foundEnvPart?.adstDliStrCd.toString().split('|').join('').split(',');
// 시군구 데이터
const envSigunData = selectSigunData.filter((item: SelectDataType) => cityArray?.includes(item.val));
// 환경청 시도 데이터
const sidoArray = [...new Set(envSigunData.map((item: SelectDataType) => item.val.substring(0, 2)))];
const envSidoData = selectSidoData.filter((item: SelectDataType) => sidoArray?.includes(item.val));
setFilteredEnvPartSigunDataSelected(envSigunData);
setFilteredEnvPartSidoData([
{ text: "전체", val: '0', adstDliStrCd: '' },
...envSidoData.map((item: SelectDataType) => ({ text: item.text, val: item.val, adstDliStrCd: '' }))
]);
}
// 환경청 세팅 - 시군구
const initializeCityDataByEnvPartSigun = (sidoCd: string) => {
const envSigunData = filteredEnvPartSigunDataSelected.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd);
setFilteredEnvPartSigunData([
{ text: "전체", val: '0' },
...envSigunData.map((item: SelectDataType) => ({ text: item.text, val: item.val }))
]);
}
const onSelectValue = (val: string, type: string) => {
let selectedSido;
let selectedSigun;
let selectedEnvPartSido;
let selectedEnvPartSigun;
switch (type) {
case 'part':
setPartValue(val);
if (setRtnPartValue) {
setRtnPartValue(val);
}
// 구분
if (val == 'R') {
initializeMiddleRegionData('0');
initializeUnitWaterShedData('0');
} else if (val == 'G') {
setFilteredEnvPartData([
{ text: "전체", val: '0', adstDliStrCd: '' },
...selectEnvPartData.filter((item: EnvPartSelectDataType) => item.val !== null && item.adstDliStrCd !== '')
]);
setEnvPartValue('0');
initializeCityDataByEnvPartSido('0');
} else {
initializeSigunData('0');
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
}
// 다른 selectbox 전체로 초기화
onSelectValue(selectSidoData[0]?.val || '0', 'sido');
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
break;
case 'sido':
setSidoValue(val);
selectedSido = selectSidoData.find(item => item.val === val);
setSidoText(selectedSido?.text || '');
if (setRtnSidoValue) {
setRtnSidoValue(val);
}
if (setRtnSidoText) {
setRtnSidoText(selectedSido?.text || '');
}
initializeSigunData(val);
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
break;
case 'sigun':
if (setRtnSigunValue) {
setRtnSigunValue(val);
}
setSigunValue(val);
selectedSigun = filteredSigunData.find(item => item.val === val);
if (setRtnSigunText) {
setRtnSigunText(selectedSigun?.text || '');
}
setSigunText(selectedSigun?.text || '');
break;
// 유역 세팅
case 'region':
setRegionValue(val);
if (setRtnRegionValue) {
setRtnRegionValue(val);
}
initializeUnitWaterShedData(val);
onSelectValue(selectUnitWaterShedData[0]?.val || '0', 'unitWaterShed');
break;
case 'unitWaterShed':
setUnitWaterShedValue(val);
if (setRtnUnitWaterShedValue) {
setRtnUnitWaterShedValue(val);
}
initializeMiddleRegionData(val);
onSelectValue(selectMiddleRegionData[0]?.val || '0', 'middleRegion');
break;
case 'middleRegion':
setMiddleRegionValue(val);
if (setRtnMiddleRegionValue) {
setRtnMiddleRegionValue(val);
}
break;
// 환경청 세팅
case 'envPart':
setEnvPartValue(val);
if (setRtnEnvPartValue) {
setRtnEnvPartValue(val);
}
initializeCityDataByEnvPartSido(val);
onSelectValue(filteredEnvPartSidoData[0]?.val || '0', 'envPartSido');
break;
case 'envPartSido':
setSidoValue(val);
selectedEnvPartSido = filteredEnvPartSidoData.find(item => item.val === val);
setSidoText(selectedEnvPartSido?.text || '');
if (setRtnEnvPartSidoValue) {
setRtnEnvPartSidoValue(val);
}
initializeCityDataByEnvPartSigun(val);
onSelectValue(filteredEnvPartSigunData[0]?.val || '0', 'envPartSigun');
break;
case 'envPartSigun':
setSigunValue(val);
selectedEnvPartSigun = filteredEnvPartSigunData.find(item => item.val === val);
setSigunText(selectedEnvPartSigun?.text || '');
if (setRtnEnvPartSigunValue) {
setRtnEnvPartSigunValue(val);
}
break;
}
}
// 리셋 함수
const resetAllValues = () => {
setPartValue('A');
setSidoValue('0');
setSigunValue('0');
setRegionValue('0');
setUnitWaterShedValue('0');
setMiddleRegionValue('0');
setEnvPartValue('0');
setFilteredSigunData([{ text: "전체", val: '0' }]);
setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]);
setFilteredMiddleRegionData([{ text: "전체", val: '0' }]);
setFilteredEnvPartData([{ text: "전체", val: '0', adstDliStrCd: '' }]);
setFilteredEnvPartSidoData([{ text: "전체", val: '0', adstDliStrCd: '' }]);
setFilteredEnvPartSigunData([{ text: "전체", val: '0' }]);
if (setRtnPartValue) setRtnPartValue('A');
if (setRtnSidoValue) setRtnSidoValue('0');
if (setRtnSigunValue) setRtnSigunValue('0');
if (setRtnRegionValue) setRtnRegionValue('0');
if (setRtnUnitWaterShedValue) setRtnUnitWaterShedValue('0');
if (setRtnMiddleRegionValue) setRtnMiddleRegionValue('0');
if (setRtnEnvPartValue) setRtnEnvPartValue('0');
if (setRtnEnvPartSidoValue) setRtnEnvPartSidoValue('0');
if (setRtnEnvPartSigunValue) setRtnEnvPartSigunValue('0');
};
// 리셋 트리거 변경 감지
useEffect(() => {
if (resetTrigger) {
resetAllValues();
// 리셋 후 트리거를 false로 설정
if (setRtnPartValue) {
setRtnPartValue('0');
}
}
}, [resetTrigger]);
return (
isShowPart ? (
<div className="flex gap-5">
<SelectBox className="w-auto whitespace-nowrap" label={'구분'} selectArray={isAdmin ? selectPartData.slice(0, 1) : selectPartData} onSelectValue={(val) => onSelectValue(val.toString(), 'part')} value={partValue} selectClass={'w-32'} />
{partValue === 'R' ? (
<>
<SelectBox className="w-auto whitespace-nowrap" label={'유역'} selectArray={selectRegionData} onSelectValue={(val) => onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'단위유역'} selectArray={filteredUnitWaterShedData} onSelectValue={(val) => onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'중권역'} selectArray={filteredMiddleRegionData} onSelectValue={(val) => onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={'w-44'} />
</>
) : partValue === 'G' ? (
<>
<SelectBox className="w-auto whitespace-nowrap" label={'환경청'} selectArray={filteredEnvPartData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'시도'} selectArray={filteredEnvPartSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPartSido')} value={sidoValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'시군구'} selectArray={filteredEnvPartSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPartSigun')} value={sigunValue} selectClass={'w-44'} />
</>
) : (
<>
<SelectBox className="w-auto whitespace-nowrap" label={'시도'} selectArray={selectSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'시군구'} selectArray={filteredSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={'w-44'} />
</>
)}
</div>
) : (
<div className="grid grid-cols-12 gap-5 w-11/12 m-4">
{children}
</div>
)
)
}
export { MapSearchFormLeft };
+382
View File
@@ -0,0 +1,382 @@
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import { excelDownload } from "@/components/lib/func/commom";
import { Title, UiTable, UITab, ContentsDiv } from '@common/business_components';
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import { Button } from "@common/components/ui";
import React, { useCallback, useState, useEffect } from 'react';
import NewWindowPortal from '@/app/map/newWindowPortal';
import PbnstPrfmncSttnPopup from "@/app/map/pbnstPrfmncSttnPopup";
import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal";
import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type";
import type { chgBuilding, customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import RainPrfmncSttnPopup from "@/app/map/rainPrfmncSttnPopup";
import RswtrPrfmncsSttnPopup from "@/app/map/rswtrPrfmncSttnPopup";
import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import HtdflPrfmncSttnPopup from "@/app/map/htdflPrfmncSttnPopup";
import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
interface NewWindowPopupProps {
featureData: any;
checkedLayers?: string[];
checkedLayersData?: any[];
onClose: () => void;
}
// 탭
const TABS = [
{ val: "0", text: "재이용시설1" },
{ val: "1", text: "재이용시설2" },
{ val: "2", text: "재이용시설n" },
];
// 데이터의 속성(key)을 사용자에게 보여줄 한글 이름으로 변환하기 위한 맵
// 실제 데이터에 맞게 이 부분을 확장해야 합니다.
const propertyNameMap: { [key: string]: string } = {
fid: '고유 ID',
fcltNm: '시설명',
opertDe: '운영일자',
addr: '주소',
// ... 필요한 만큼 속성 매핑 추가
};
const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
featureData,
checkedLayers = [],
checkedLayersData = [],
onClose
}) => {
const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "0");
// swcho : useState의 true, false 값 변경하여 목록, 상태 테스트 하였음
const [showDetailView, setShowDetailView] = useState(true); // 상세정보 화면 표시 상태 추가
// onClose 함수를 useCallback으로 메모이제이션
const memoizedOnClose = useCallback(() => {
onClose();
}, [onClose]);
// 하수재이용시설 변수
const {
code,
pbnstSttn,
pbnstSttnSearch,
pbnSttnInit,
getPbnStnDetail,
pbnNmSearch,
} = useWrStore((state) => state);
const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>();
const [pageNation, setPageNation] = useState(1);
const [pbnDetaileData, setPbnDetaileData] = useState<{
isModal: boolean;
data: customPbnst;
}>({ isModal: false, data: {} as customPbnst });
// 빗물재이용시설 변수
const { rainSttn, rainSttnSearch, rainSttnInit } = useWrStore(
(state) => state
);
const { rainSttnRes } = rainSttn;
const [rainTable, setRainTable] = useState<customRainBdst[]>();
const [rainBdstChgProps, setRainBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false });
// 중수도시설 변수
const { rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore(
(state) => state
);
const { rswtSttnRes } = rswtSttn;
const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>();
const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false });
const [rswtrBdstChgProps, setRswtrBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
// 온배수시설 변수
const { htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state);
const { htdflSttnRes } = htdflSttn;
const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>();
const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false });
const [bdstChgProps, setBdstChgProps] = useState<{ isOpen: boolean, chBuilding: chgBuilding[] }>({ isOpen: false, chBuilding: [] });
if (!featureData) {
return null;
}
// 2. RuseMngTable에 필요한 tableData를 생성합니다. (단일 행 데이터)
const tableData = [featureData];
console.log("tableData", tableData);
// 물재이용시설 cellClick 이벤트 (상세정보)
const pbnstCellClick = (index: number | undefined) => {
if (
index !== undefined &&
pbnstSttn.pbnstSttnRes &&
pbnstSttn.pbnstSttnRes[index]
) {
const pbnstRuseFcltPk = pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk;
getPbnStnDetail(pbnstRuseFcltPk);
}
};
useEffect(() => {
setBdstTable(pbnstSttn.pbnstSttnRes);
}, [pbnstSttn.pbnstSttnRes]);
useEffect(() => {
if (pbnstSttn.pbnstDetail.pbnstRuseFcltPk) setShowDetailView(true);
}, [pbnstSttn.pbnstDetail]);
// 빗물재이용시설 cellClick 이벤트 (상세정보)
const rainCellClick = (index: number | undefined, headId: string) => {
if (
index !== undefined &&
rainTable !== undefined &&
headId !== 'chBuildingYn') {
setRainDetailModal({ isOpen: true, data: rainTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && rainTable !== undefined) {
if (rainTable[index] && rainTable[index]?.chgBuilding[0]?.bdstChgNm !== null)
setRainBdstChgProps({ isOpen: true, chBuilding: rainTable[index].chgBuilding });
}
}
// 중수도시설 cellClick 이벤트 (상세정보)
const rswtCellClick = (index: number | undefined, headId: string) => {
if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) {
setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) {
if (rswtrTable[index] && rswtrTable[index]?.chBuildingYn === 'Y') {
setRswtrBdstChgProps({ isOpen: true, chBuilding: rswtrTable[index].chgBuilding });
}
}
}
// 온배수시설 cellClick 이벤트 (상세정보)
const htdflCellClick = (index: number | undefined, headId: string) => {
if (
headId !== "chBuildingYn" &&
index !== undefined &&
htdflTable !== undefined
) {
setHtdflDetailModal({ isOpen: true, data: htdflTable[index] });
}
};
const excelClick = useCallback(() => {
// if (codeData) pbnstSttnSearch(1, "10000", codeData);
excelDownload(
[[]],
"하수재이용시설 현황",
"sample_pbnstSttn.xlsx",
"A1:U2"
);
}, [pbnstSttnSearch]);
// 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수
const renderConditionalContent = () => {
// 특정 레이어가 체크되었는지 확인
const hasWaterTreatmentLayer = checkedLayersData.some(layer =>
layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설')
);
// swcho : true, false 값 변경하여 시설 변경하여 테스트하였음
// 재이용시설
const isPbnst = true;
// 빗물이용시설
const isRain = false;
// 중수도시설
const isRswt = false;
// 온배수시설
const isHtdfl = false;
return (
<div className="mt-4">
{/* 체크된 레이어가 없는 경우 (재이용시설 목록) */}
{isPbnst && (
<>
{showDetailView ? (
// 재이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='하수재이용시설 상세정보' />
</div>
<div className="flex justify-between items-center mb-8">
<UITab
defaultValue={TABS[0]?.val}
tabList={TABS.map((tab) => ({
value: tab.val,
label: tab.text,
onClick: () => setActiveTab(tab.val),
}))}
children={undefined}
/>
</div>
<PbnstSttnModal
data={pbnDetaileData.data || ({} as customPbnst)}
titleVisible={false}
/>
<PbnstPrfmncSttnPopup isVisible={false} />
</>
) : (
// 재이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='하수재이용시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />
}
headlist={pbnstSttn.pbnstSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
pbnstCellClick(index);
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
{/* 빗물이용시설 */}
{isRain && (
<>
{showDetailView ? (
// 빗물이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='빗물이용시설 상세정보' />
</div>
<RainPrfmncSttnPopup rainForm={rainDetailModal.data as customRainBdst} isModal={false} />
{/* TODO: 지도 점검 결과 현황 추가 할 것 */}
</>
) : (
// 빗물이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='빗물이용시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={rainTable || []}
children={
<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />
}
headlist={rainSttn.rainSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
rainCellClick(index ?? undefined, headId)
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
{/* 중수도시설 */}
{isRswt && (
<>
{showDetailView ? (
// 재이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='중수도시설 상세정보' />
</div>
<RswtrPrfmncsSttnPopup rswtForm={rswtrDetailModal.data as customRswtBdst} isModal={false} />
{/* TODO: 지도 점검 결과 현황 추가 할 것 */}
</>
) : (
// 재이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='중수도시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={rswtrTable || []}
children={
<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />
}
headlist={rswtSttn.rswtSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
rswtCellClick(index, headId);
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
{/* 온배수시설 */}
{isHtdfl && (
<>
{showDetailView ? (
// 재이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='온배수시설 상세정보' />
</div>
{/* <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> */}
<HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} />
</>
) : (
// 재이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='온배수시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />
}
headlist={htdflSttn.htdflSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
htdflCellClick(index ?? undefined, headId);
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
</div>
);
};
return (
<NewWindowPortal onClose={memoizedOnClose} title="시설 상세 정보">
{/* 체크된 레이어에 따른 조건부 렌더링 */}
{renderConditionalContent()}
</NewWindowPortal>
);
};
export default NewWindowPopup;
+90
View File
@@ -0,0 +1,90 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
// 현재 문서의 스타일(CSS)을 새 창으로 복사하는 함수
const copyStyles = (sourceDoc: Document, targetDoc: Document) => {
Array.from(sourceDoc.styleSheets).forEach(styleSheet => {
try {
if (styleSheet.cssRules) { // <style> 태그로 주입된 CSS
const newStyleEl = sourceDoc.createElement('style');
Array.from(styleSheet.cssRules).forEach(cssRule => {
newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
});
targetDoc.head.appendChild(newStyleEl);
} else if (styleSheet.href) { // <link> 태그로 연결된 CSS 파일
const newLinkEl = sourceDoc.createElement('link');
newLinkEl.rel = 'stylesheet';
newLinkEl.href = styleSheet.href;
targetDoc.head.appendChild(newLinkEl);
}
} catch (e) {
console.warn('스타일시트를 복사할 수 없습니다:', e);
}
});
};
interface NewWindowPortalProps {
children: React.ReactNode;
onClose: () => void;
title?: string;
}
const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "Detail" }) => {
const [container, setContainer] = useState<HTMLElement | null>(null);
const newWindowRef = useRef<Window | null>(null);
const onCloseRef = useRef(onClose);
// onClose 함수를 ref로 저장하여 의존성 문제 해결
useEffect(() => {
onCloseRef.current = onClose;
}, [onClose]);
const handleUnload = useCallback(() => {
onCloseRef.current();
}, []);
useEffect(() => {
// 이미 창이 열려있으면 새로 열지 않음
if (newWindowRef.current && !newWindowRef.current.closed) return;
// 1. 새 창을 열고 참조를 저장합니다.
newWindowRef.current = window.open('', title, 'width=1400,height=1024,resizable=yes,scrollbars=yes');
if (newWindowRef.current) {
newWindowRef.current.document.title = title;
// 2. React 컴포넌트를 마운트할 컨테이너 div를 생성하여 새 창의 body에 추가합니다.
const div = newWindowRef.current.document.createElement('div');
newWindowRef.current.document.body.appendChild(div);
setContainer(div);
// 3. 메인 창의 스타일을 새 창으로 복사합니다. (매우 중요)
copyStyles(document, newWindowRef.current.document);
// 4. 사용자가 창을 닫을 때 부모에게 알리기 위한 이벤트 리스너를 설정합니다.
// const handleUnload = () => {
// onClose();
// };
newWindowRef.current.addEventListener('unload', handleUnload);
// 5. 컴포넌트가 사라질 때 창을 닫는 클린업 함수를 반환합니다.
return () => {
if (newWindowRef.current) {
newWindowRef.current.removeEventListener('unload', handleUnload);
if (!newWindowRef.current.closed) {
newWindowRef.current.close();
}
}
};
} else {
alert('팝업이 차단되었습니다. 팝업 차단을 해제하고 다시 시도해주세요.');
onCloseRef.current();
// onClose();
}
}, [onClose, title]);
// `createPortal`을 사용해 children을 새 창의 container div에 렌더링합니다.
return container ? createPortal(children, container) : null;
};
export default NewWindowPortal;
+596
View File
@@ -0,0 +1,596 @@
"use client";
import { useWrStore } from "@/app/store";
import { initValInUsage, initValOutUsage, subTotalObj, totalObj, type FcPrfType, type PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type";
import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SelectBox, UiTable, type ChartConfig, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useEffect, useRef, useState } from "react";
import { Toaster } from "react-hot-toast";
interface PbnstPrfmncSttnPopupProps {
isVisible?: boolean;
}
export default function PbnstPrfmncSttnPopup({ isVisible = true }: PbnstPrfmncSttnPopupProps) {
const { code, tableHeadMaker, pbnstPrfmncSttn, pbnstPrfmncInit, pbnstPrfmncSearch, prfmnc } = useWrStore((state) => state);
const { monthHeadList } = prfmnc
const { pbnstPrfHeadList, pbnstPrfmncRes, prfmncRes, rawData, selectYearList } = pbnstPrfmncSttn;
const [isChHead, setIsChHead] = useState(false);
const [fromDate, setFromDate] = useState<string>(String(new Date().getFullYear()));
const [toDate, setToDate] = useState<string>(String(new Date().getFullYear()));
const [year, setYear] = useState<string>(String(new Date().getFullYear()));
const [bdstTable, setbdstTable] = useState<FcPrfType[]>();
const ref = useRef<ReturnType>({} as ReturnType);
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>();
const [chartConfig, setChartConfig] = useState<ChartConfig>({});
const [chartData, setChartData] = useState<dnChartForm[]>([]);
const [dnInUsage, setDnInUsage] = useState<dnChartForm[]>([]);
const [dnInChartConfig, setDnInChartConfig] = useState<ChartConfig>({});
const [dnOutUsage, setDnOutUsage] = useState<dnChartForm[]>([]);
const [dnOutChartConfig, setDnOutChartConfig] = useState<ChartConfig>({});
const [barData, setBarData] = useState<barChartForm[]>([]);
const [barChartConfig, setBarChartConfig] = useState<ChartConfig>({});
const [prfData, setPrfData] = useState<any[]>();
const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({});
const [lnOutChartConfig, setLnOutChartConfig] = useState<ChartConfig>({});
const [inLineData, setInLineData] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
chartData3: lineChartDataProps[];
chartData4: lineChartDataProps[];
chartData5: lineChartDataProps[];
chartData6: lineChartDataProps[];
chartData7: lineChartDataProps[];
chartData8: lineChartDataProps[];
chartData9: lineChartDataProps[];
}>();
// const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({});
const [outLineData, setOutLineData] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
chartData3: lineChartDataProps[];
chartData4: lineChartDataProps[];
chartData5: lineChartDataProps[];
chartData6: lineChartDataProps[];
}>();
type dnChartForm = { label: string, val: number, fill?: string };
type barChartForm = { label: string, "장내용수": number, "장외용수": number };
type lineChartDataProps = { label: string, val: number, color: string };
const prfTableData = (prfList: PrfType[], year: string) => {
const inUsageData = initValInUsage;
const outUsageData = initValOutUsage;
const inSubtotal = subTotalObj;
const outSubtotal = subTotalObj;
console.log(' prfList ', prfList)
const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year)
if (prfList && prfFilter.length > 0) {
prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
const monthKey = `${month}월`;
inUsageData.forEach((inData: any) => {
const usageKey = inData.usage;
const value = (prfItem as any)[usageKey] ?? 0;
inData[monthKey] = value;
inData.total += value;
if (inSubtotal && usageKey !== "subtotal") {
(inSubtotal as any)[monthKey] =
((inSubtotal as any)[monthKey] ?? 0) + value;
inSubtotal.total += value;
}
});
outUsageData.forEach((outData: any) => {
const usageKey = outData.usage;
const value = (prfItem as any)[usageKey] ?? 0;
outData[monthKey] = value;
outData.total += value;
if (usageKey !== "subtotal") {
(outSubtotal as any)[monthKey] =
((outSubtotal as any)[monthKey] ?? 0) + value;
outSubtotal.total += value;
}
});
});
inUsageData.push(inSubtotal);
outUsageData.push(outSubtotal);
const usageTotalObj: { [key: string]: any } = totalObj;
for (const key in usageTotalObj) {
if (["usage", "gubun", "edit"].includes(key)) continue;
const val =
(Number((inSubtotal as any)[key]) || 0) +
(Number((outSubtotal as any)[key]) || 0);
(usageTotalObj as any)[key] = val;
}
const tableData = [...inUsageData, ...outUsageData, usageTotalObj];
setPrfData(tableData);
} else {
setPrfData([]);
}
}
useEffect(() => {
let wshwtr = 0;
let coolUswtr = 0;
let cleanUswtr = 0;
let drwtUswtr = 0;
let dltnUswtr = 0;
let ithlRswtr = 0;
let ithlEtcUswtr = 0;
let cleanToilUswtr = 0;
let resn = 0;
let lndwtr = 0;
let hydUswtr = 0;
let ugwtChr = 0;
let farmUswtr = 0;
let rvrKeepUswtr = 0;
let indstUswtr = 0;
if (rawData) {
rawData.forEach((item) => {
prfTableData(item.prfList, year);
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
wshwtr += element.wshwtr; //세척수
coolUswtr += element.coolUswtr; //냉각수
cleanUswtr += element.cleanUswtr; //청소수
drwtUswtr += element.drwtUswtr; //식수대
dltnUswtr += element.dltnUswtr //희석용수
ithlRswtr += element.ithlRswtr //중수도
ithlEtcUswtr += element.ithlEtcUswtr //기타
cleanToilUswtr += element.cleanToilUswtr //청소화장실용수
resn += element.resn //세척살수용수
lndwtr += element.lndwtr; //조경용수
hydUswtr += element.hydUswtr; //친수용수
ugwtChr += element.ugwtChr; //지하수충전
farmUswtr += element.farmUswtr //농업용수
rvrKeepUswtr += element.rvrKeepUswtr //하천등유지용수
indstUswtr += element.indstUswtr //공업용수
});
})
const data: dnChartForm[] = []
data?.push({ label: "세척수", val: wshwtr, fill: "red" })
data?.push({ label: "냉각수", val: coolUswtr, fill: "orange" })
data?.push({ label: "청소수", val: cleanUswtr, fill: "yellow" })
data?.push({ label: "식수대", val: drwtUswtr, fill: "green" })
data?.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" })
data?.push({ label: "중수도", val: ithlRswtr, fill: "blue" })
data?.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" })
data?.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" })
data?.push({ label: "세척살수용수", val: resn, fill: "orange" })
data?.push({ label: "조경용수", val: lndwtr, fill: "yellow" })
data?.push({ label: "친수용수", val: hydUswtr, fill: "green" })
data?.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" })
data?.push({ label: "농업용수", val: farmUswtr, fill: "blue" })
data?.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" })
data?.push({ label: "공업용수", val: indstUswtr, fill: "black" })
setChartData(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = chartData.reduce((config, element) => {
config[element.label] = {
label: element.label,
fill: element.fill
};
return config;
}, {} as Record<string, { label: string; fill?: string }>) satisfies ChartConfig;
setChartConfig(newChartConfig);
}, [chartData]);
// 장내용수 도넛차트
useEffect(() => {
let wshwtr = 0;
let coolUswtr = 0;
let cleanUswtr = 0;
let drwtUswtr = 0;
let dltnUswtr = 0;
let ithlRswtr = 0;
let ithlEtcUswtr = 0;
let cleanToilUswtr = 0;
let resn = 0;
if (rawData) {
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
wshwtr += element.wshwtr; //세척수
coolUswtr += element.coolUswtr; //냉각수
cleanUswtr += element.cleanUswtr; //청소수
drwtUswtr += element.drwtUswtr; //식수대
dltnUswtr += element.dltnUswtr //희석용수
ithlRswtr += element.ithlRswtr //중수도
ithlEtcUswtr += element.ithlEtcUswtr //기타
cleanToilUswtr += element.cleanToilUswtr //청소화장실용수
resn += element.resn //세척살수용수
});
})
const data: dnChartForm[] = []
data?.push({ label: "세척수", val: wshwtr, fill: "yellow" })
data?.push({ label: "냉각수", val: coolUswtr, fill: "green" })
data?.push({ label: "청소수", val: cleanUswtr, fill: "yellow" })
data?.push({ label: "식수대", val: drwtUswtr, fill: "green" })
data?.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" })
data?.push({ label: "중수도", val: ithlRswtr, fill: "green" })
data?.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" })
data?.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" })
data?.push({ label: "세척살수용수", val: resn, fill: "yellow" })
setDnInUsage(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = dnInUsage.reduce((config, element) => {
config[element.label] = {
label: element.label,
fill: element.fill
};
return config;
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
setDnInChartConfig(newChartConfig);
}, [dnInUsage]);
// 장외용수 도넛차트
useEffect(() => {
let lndwtr = 0;
let hydUswtr = 0;
let ugwtChr = 0;
let farmUswtr = 0;
let rvrKeepUswtr = 0;
let indstUswtr = 0;
if (rawData) {
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
lndwtr += element.lndwtr; //조경용수
hydUswtr += element.hydUswtr; //친수용수
ugwtChr += element.ugwtChr; //지하수충전
farmUswtr += element.farmUswtr //농업용수
rvrKeepUswtr += element.rvrKeepUswtr //하천등유지용수
indstUswtr += element.indstUswtr //공업용수
});
})
const data: dnChartForm[] = []
data?.push({ label: "조경용수", val: lndwtr, fill: "yellow" })
data?.push({ label: "친수용수", val: hydUswtr, fill: "black" })
data?.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" })
data?.push({ label: "농업용수", val: farmUswtr, fill: "black" })
data?.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" })
data?.push({ label: "공업용수", val: indstUswtr, fill: "black" })
setDnOutUsage(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = dnOutUsage.reduce((config, element) => {
config[element.label] = {
label: element.label,
fill: element.fill
};
return config;
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
setDnOutChartConfig(newChartConfig);
}, [dnOutUsage]);
//월별 장내외 용수 총 실적 막대차트
useEffect(() => {
let wshwtr = 0;
let coolUswtr = 0;
let cleanUswtr = 0;
let drwtUswtr = 0;
let dltnUswtr = 0;
let ithlRswtr = 0;
let ithlEtcUswtr = 0;
let cleanToilUswtr = 0;
let resn = 0;
let lndwtr = 0;
let hydUswtr = 0;
let ugwtChr = 0;
let farmUswtr = 0;
let rvrKeepUswtr = 0;
let indstUswtr = 0;
let inUsageSum = 0;
let outUsageSum = 0;
let inUsageArr = [];
let outUsageArr = [];
if (rawData) {
const data: barChartForm[] = []
rawData.forEach((item) => {
item.prfList
.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
wshwtr = element.wshwtr; //세척수
coolUswtr = element.coolUswtr; //냉각수
cleanUswtr = element.cleanUswtr; //청소수
drwtUswtr = element.drwtUswtr; //식수대
dltnUswtr = element.dltnUswtr //희석용수
ithlRswtr = element.ithlRswtr //중수도
ithlEtcUswtr = element.ithlEtcUswtr //기타
cleanToilUswtr = element.cleanToilUswtr //청소화장실용수
resn = element.resn //세척살수용수
lndwtr = element.lndwtr; //조경용수
hydUswtr = element.hydUswtr; //친수용수
ugwtChr = element.ugwtChr; //지하수충전
farmUswtr = element.farmUswtr //농업용수
rvrKeepUswtr = element.rvrKeepUswtr //하천등유지용수
indstUswtr = element.indstUswtr //공업용수
inUsageSum = wshwtr + coolUswtr + cleanUswtr + drwtUswtr + dltnUswtr + ithlRswtr + ithlEtcUswtr + cleanToilUswtr + resn;
inUsageArr.push(inUsageSum)
outUsageSum = lndwtr + hydUswtr + ugwtChr + farmUswtr + rvrKeepUswtr + indstUswtr;
outUsageArr.push(outUsageSum)
const month = Number(element.ym.slice(4, 6))
data.push({ label: `${month}월`, "장내용수": inUsageSum, "장외용수": outUsageSum })
},
);
})
setBarData(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = barData.reduce((config, element) => {
config[element.label] = {
label: element.label,
};
return config;
}, {} as Record<string, { label: string }>) satisfies ChartConfig;
setBarChartConfig(newChartConfig);
}, [barData]);
//월별 장내 용수 실적 라인차트
useEffect(() => {
if (rawData) {
const wshwtrData: lineChartDataProps[] = []
const coolUswtrData: lineChartDataProps[] = []
const cleanUswtrData: lineChartDataProps[] = []
const drwtUswtrData: lineChartDataProps[] = []
const dltnUswtrData: lineChartDataProps[] = []
const ithlRswtrData: lineChartDataProps[] = []
const ithlEtcUswtrData: lineChartDataProps[] = []
const cleanToilUswtrData: lineChartDataProps[] = []
const resnData: lineChartDataProps[] = []
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
const month = Number(element.ym.slice(4, 6))
wshwtrData.push({ label: `${month}월`, val: element.wshwtr, color: "red" })
coolUswtrData.push({ label: `${month}월`, val: element.coolUswtr, color: "red" })
cleanUswtrData.push({ label: `${month}월`, val: element.cleanUswtr, color: "red" })
drwtUswtrData.push({ label: `${month}월`, val: element.drwtUswtr, color: "red" })
dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "red" })
ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "red" })
ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "red" })
cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "red" })
resnData.push({ label: `${month}월`, val: element.resn, color: "red" })
},
);
})
const rtndata = {
chartData1: wshwtrData,
chartData2: coolUswtrData,
chartData3: cleanUswtrData,
chartData4: drwtUswtrData,
chartData5: dltnUswtrData,
chartData6: ithlRswtrData,
chartData7: ithlEtcUswtrData,
chartData8: cleanToilUswtrData,
chartData9: resnData
}
setInLineData(rtndata)
}
}, [rawData, year])
useEffect(() => {
if (inLineData?.chartData1) {
const newChartConfig = {
chartData1: {
label: '세척수',
color: "red",
},
chartData2: {
label: '냉각수',
color: "orange",
},
chartData3: {
label: '청소수',
color: "yellow",
},
chartData4: {
label: '식수대',
color: "pink",
},
chartData5: {
label: '희석용수',
color: "purple",
},
chartData6: {
label: '중수도',
color: "green",
},
chartData7: {
label: '기타',
color: "blue",
},
chartData8: {
label: '청소화장실용수',
color: "magenta",
},
chartData9: {
label: '세척살수용수',
color: "skyblue",
},
} satisfies ChartConfig;
setLnInChartConfig(newChartConfig);
}
}, [inLineData]);
//월별 장외 용수 실적 라인차트
useEffect(() => {
if (rawData) {
const lndwtrData: lineChartDataProps[] = []
const hydUswtrData: lineChartDataProps[] = []
const ugwtChrData: lineChartDataProps[] = []
const farmUswtrData: lineChartDataProps[] = []
const rvrKeepUswtrData: lineChartDataProps[] = []
const indstUswtrData: lineChartDataProps[] = []
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
const month = Number(element.ym.slice(4, 6))
lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" })
hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "red" })
ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "red" })
farmUswtrData.push({ label: `${month}월`, val: element.farmUswtr, color: "red" })
rvrKeepUswtrData.push({ label: `${month}월`, val: element.rvrKeepUswtr, color: "red" })
indstUswtrData.push({ label: `${month}월`, val: element.indstUswtr, color: "red" })
},
);
})
setOutLineData({
chartData1: lndwtrData,
chartData2: hydUswtrData,
chartData3: ugwtChrData,
chartData4: farmUswtrData,
chartData5: rvrKeepUswtrData,
chartData6: indstUswtrData,
})
}
}, [rawData, year])
useEffect(() => {
if (outLineData?.chartData1) {
const newChartConfig = {
chartData1: {
label: '조경용수',
color: "red",
},
chartData2: {
label: '친수용수',
color: "orange",
},
chartData3: {
label: '지하수충전',
color: "yellow",
},
chartData4: {
label: '농업용수',
color: "green",
},
chartData5: {
label: '하천등유지용수',
color: "blue",
},
chartData6: {
label: '공업용수',
color: "purple",
},
} satisfies ChartConfig;
setLnOutChartConfig(newChartConfig);
}
}, [outLineData]);
useEffect(() => {
if (!pbnstPrfmncSttn.isInit) {
pbnstPrfmncInit("1", "11");
}
}, [pbnstPrfmncSttn.isInit, pbnstPrfmncInit]);
useEffect(() => {
setbdstTable(prfmncRes);
}, [prfmncRes]);
return (
<>
<ContentsDiv>
<ListDiv>
<hr />
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">하수재이용실적 상세현황</h3>
<SelectBox
className="w-1/12 text-2xl mt-4 ml-2"
label="연도"
selectClass={""}
selectArray={selectYearList}
value={year}
onSelectValue={(val: string | number) => setYear(String(val))} />
<h1 className="mt-4 ml-2">장내·외 용도별 하수재이용실적 현황(계)</h1>
<div className="flex">
<div className="w-1/3">
<MainChart chartConfig={chartConfig} chartData={chartData} chartTitle={"장내·외 용수 총 실적"} chartForm={CHART_FORM.DN} />
</div>
<div className="w-1/3">
<MainChart chartConfig={dnInChartConfig} chartData={dnInUsage} chartTitle={"장내용수 실적"} chartForm={CHART_FORM.DN} />
</div>
<div className="w-1/3">
<MainChart chartConfig={dnOutChartConfig} chartData={dnOutUsage} chartTitle={"장외용수 실적"} chartForm={CHART_FORM.DN} />
</div>
</div>
<h1 className="">장내·외 월별 하수재이용실적 현황</h1>
<div className="flex">
<div className="w-1/3">
<MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} />
</div>
<div className="w-1/3">
<MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} />
</div>
<div className="w-1/3">
<MainChart chartConfig={lnOutChartConfig} chartData={outLineData} chartTitle={"월별 장외 용수 총 실적"} chartForm={CHART_FORM.LINE} />
</div>
</div>
{/* 하수재이용실적 현황 - 조건부 렌더링 */}
{isVisible && (
<>
<h1 className="">하수재이용실적 현황</h1>
<ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
<UiTable
headName={undefined}
tableData={prfData || []}
headlist={monthHeadList}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}>
</UiTable>
</ScrollArea>
</ListDiv>
</>
)}
</ListDiv>
</ContentsDiv>
<Toaster position="top-center" />
</>
)
}
+421
View File
@@ -0,0 +1,421 @@
"use client";
import { useWrStore } from "@/app/store";
import {
rainZod,
selectClass,
TEST_UUID,
type customRainBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { DatePicker, FormUi } from "@common/business_components";
import { Form, FormField, SelectBox } from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
import { useForm, useWatch, type UseFormReturn } from "react-hook-form";
import { z } from "zod";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import { Label } from "@common/business_components/ui/form/lib/label";
export default function RainPrfmncSttnPopup({
rainForm,
isModal,
}: {
rainForm: customRainBdst;
isModal: boolean;
}) {
const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore(
(state) => state
);
const { rainFcltCd } = code;
const defaultValues: z.infer<typeof rainZod> = rainZod.parse({});
const form = useForm<z.infer<typeof rainZod>>({
resolver: zodResolver(rainZod),
defaultValues: defaultValues,
});
const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn] = form.watch([
"reuseBuilding.0.grntchBdstCertYn",
"reuseBuilding.0.instlCstSprtYn",
"reuseBuilding.0.rainyObj.filtrTrtmfctYn",
]);
const cleanRainData = (raw: any): z.infer<typeof rainZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
operMngCd: item?.operMngCd ?? "", // null → ""
grntchBdstCertYr:
typeof item?.grntchBdstCertYr === "string"
? item?.grntchBdstCertYr
: "", // null → ""
rainObj: {
...item?.rainObj,
},
})),
};
};
useEffect(() => {
if (grntchBdstCertYn === "N") {
form.setValue("reuseBuilding.0.grntchBdstCertYr", "");
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
form.setValue("reuseBuilding.0.grntchBdstCertGrd", "");
} else if (instlCstSprtYn === "N") {
form.setValue("reuseBuilding.0.instlCstSprtAmt", 0);
} else if (filtrTrtmfctYn === "N") {
form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", 0);
form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", "");
}
}, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]);
// form 값이 변경될 때마다 로그 출력
useEffect(() => {
const subscription = form.watch((value) => {
});
return () => subscription.unsubscribe();
}, [form]);
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
const onSubmit = (values: z.infer<typeof rainZod>) => {
if (isModal) {
rainBdstReg(values, "41");
} else {
rainBdstUpdate(values, "41");
}
};
useEffect(() => {
if (rainZod) form.reset(cleanRainData(rainForm));
}, [rainForm]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">
{isModal ? "건축물 운영정보" : "빗물이용시설 정보"}
</h1>
</div>
</div>
{isModal && rainForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="pl-3 text-xl col-span-10 mt-4">
빗물이용시설 정보{" "}
</h1>
) : (
<></>
)}
<h1 className={formH1Class}>
기본정보
</h1>
<div className="grid grid-cols-4 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCmptnDt"
render={({ field }) => (
<FormUi label="설치완료일">
<DatePicker
mode="single"
field={{
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(), // Provide a default Date value
onChange: (date) => field.onChange(date),
}}
selectClass={"w-full"}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCst"
render={({ field }) => (
<FormUi label="설치비">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="">
<h1 className={formH1Class}>
빗물이용시설 정보
</h1>
</div>
<div>
<h2 className={formH2Class}>집수면</h2>
</div>
<div className="grid grid-cols-10 gap-4 mt-4 ">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColKnd"
render={({ field }) => (
<FormUi label="종류">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={rainFcltCd}
onSelectValue={field.onChange}
value={field.value ?? rainFcltCd[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColArea"
render={({ field }) => (
<FormUi label="면적(m³)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
/>
</FormUi>
)}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>여과 등 처리시설</h2>
</div>
<div className="grid grid-cols-12 gap-2 mt-4">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctYn"
render={({ field }) => (
<FormUi label="유무">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctCpct"
render={({ field }) => (
<FormUi label="용량(m³/일)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctPrs"
render={({ field }) => (
<FormUi label="처리공정">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>
인센티브 정보
</h1>
</div>
<div>
<h2 className={formH2Class}>설치비 지원</h2>
</div>
<div className="grid grid-cols-10 gap-2 mt-4">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtYn"
render={({ field }) => (
<FormUi label="지원 여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtAmt"
render={({ field }) => (
<FormUi label="지원금액(원)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={instlCstSprtYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>녹색건축물 인증</h2>
</div>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYn"
render={({ field }) => (
<FormUi label="인증여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYr"
render={({ field }) => (
<FormUi label="인증년도">
<Input
className={'w-full !text-xl'}
placeholder="2024"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={grntchBdstCertYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3 ">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertNo"
render={({ field }) => (
<FormUi label="인증번호">
<Input
className={'w-full !text-xl'}
placeholder="ab1234678"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={grntchBdstCertYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertGrd"
render={({ field }) => (
<FormUi label="인증등급">
<Input
className={'w-full !text-xl'}
placeholder="A"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={grntchBdstCertYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
</div>
);
}
+404
View File
@@ -0,0 +1,404 @@
"use client";
import {
DatePicker,
Form,
FormField,
FormUi,
SelectBox,
} from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
rswtZod,
type customRswtBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import { useEffect } from "react";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import { Label } from "@common/business_components/ui/form/lib/label";
export default function RswtrPrfmncsSttnPopup({
rswtForm,
isModal,
}: {
rswtForm: customRswtBdst;
isModal: boolean;
}) {
const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore(
(state) => state
);
const defaultValues: z.infer<typeof rswtZod> = rswtZod.parse({});
const form = useForm<z.infer<typeof rswtZod>>({
resolver: zodResolver(rswtZod),
defaultValues: defaultValues,
});
const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs] = form.watch([
"reuseBuilding.0.grntchBdstCertYn",
"reuseBuilding.0.instlCstSprtYn",
"reuseBuilding.0.rswtrObj.filtrTrtmfctProcs",
]);
const onSubmit = (values: z.infer<typeof rswtZod>) => {
if (isModal) {
rswtBdstReg(values, "41");
} else {
rswtBdstUpdate(values, "41");
}
};
const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
operMngCd: item?.operMngCd ?? "", // null → ""
grntchBdstCertYr:
typeof item?.grntchBdstCertYr === "string"
? item?.grntchBdstCertYr
: "", // null → ""
rswtrObj: {
...item?.rswtrObj,
},
})),
};
};
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
console.log("filtrTrtmfctProcs ", filtrTrtmfctProcs);
if (grntchBdstCertYn === "N") {
form.setValue("reuseBuilding.0.grntchBdstCertYr", "");
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
form.setValue("reuseBuilding.0.grntchBdstCertGrd", "");
} else if (instlCstSprtYn === "N") {
form.setValue("reuseBuilding.0.instlCstSprtAmt", 0);
}
}, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs]);
useEffect(() => {
if (rswtZod) form.reset(cleanRswtData(rswtForm));
}, [rswtForm]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* <div className="flex justify-between items-center mx-3"> */}
<div>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">
건축물 운영정보
</h1>
</div>
</div>
{isModal && rswtForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="pl-3 text-xl col-span-10 mt-4 ">
중수도 시설 정보{" "}
</h1>
) : (
<></>
)}
<h1 className={formH1Class}>기본정보</h1>
<div className="grid grid-cols-4 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCmptnDt"
render={({ field }) => (
<FormUi label="설치완료일">
<DatePicker
mode="single"
field={{
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(), // Provide a default Date value
onChange: (date) => field.onChange(date),
}}
selectClass={"w-full"}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCst"
render={({ field }) => (
<FormUi label="설치비">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-4 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.wtrUsqty"
render={({ field }) => (
<FormUi label="물사용량(m³)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.oblgTrgtWtrUsqty"
render={({ field }) => (
<FormUi label="의무대상 물사용량(m³)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-4">
<h1 className={formH1Class}>중수도 시설 정보</h1>
</div>
<div className="grid grid-cols-12 gap-1 mt-4">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.filtrTrtmfctCpct"
render={({ field }) => (
<FormUi label="처리시설 용량">
<Input
className={'w-full !text-xl'}
placeholder="0"
type="number"
value={field.value ?? ""}
onChange={(e)=>{field.onChange(Number(e.target.value))} }
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.orgwtKndCd"
render={({ field }) => (
<FormUi label="원수 종류">
<SelectBox
className={""}
label={""}
selectClass={selectClass}
selectArray={code.orgwtKndCd}
onSelectValue={field.onChange}
value={field.value ?? code.orgwtKndCd[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.filtrTrtmfctProcs"
render={({ field }) => (
<FormUi label="주요처리공정">
<Input
className={'w-full !text-xl'}
placeholder="주요처리공정"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>인센티브 정보</h1>
</div>
<div>
<h2 className={formH2Class}>설치비 지원</h2>
</div>
<div className="grid grid-cols-2 gap-2 mt-4">
<div className="col-span-1">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtYn"
render={({ field }) => (
<FormUi label="설치비 지원여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-1">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtAmt"
render={({ field }) => (
<FormUi label="지원금액(원)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={instlCstSprtYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>녹색건축물 인증</h2>
</div>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYn"
render={({ field }) => (
<FormUi label="인증여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYr"
render={({ field }) => (
<FormUi label="인증년도">
<Input
className={'w-full !text-xl'}
placeholder="2024"
type="text"
{...field}
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertNo"
render={({ field }) => (
<FormUi label="인증번호">
<Input
className={'w-full !text-xl'}
placeholder="ab1234678"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertGrd"
render={({ field }) => (
<FormUi label="인증등급">
<Input
className={'w-full !text-xl'}
placeholder="A"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
</div>
);
}
+1
View File
@@ -0,0 +1 @@
export * from './map'
+47
View File
@@ -0,0 +1,47 @@
/**
* 관망도 API
*/
// const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`
const API_URL = "http://con.mind-one.co.kr:11083/sewerage-service/api/v1/map";
export type LyrStngProps = {
lyr_sn: string
lyr_nm: string
upper_lyr_sn: string
drawOrdr: string
flterSql: string
gobjTy: string
lyrTpNm: string
useYn: string
minViewSize: string
iconNm: string
lblAtrbNm: string
fontNm: string
fontIntnColor: string
fontExtnColor: string
fontExtnWdth: string
lnWdth: string
lnColor: string
polgnColor: string
initEprssYn: string
clickPsblYn: string
}
export const mapService = {
//레이어 설정 조회
getLyrStngs: async () => {
const response = await fetch(`${API_URL}/lyrStngs/WR_NETWORK`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
}
+103 -55
View File
@@ -3,18 +3,17 @@ import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type { customDrgt } from "@/app/store/matDmd/drgtSttn";
import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList";
import { excelDownload, getCity } from "@/components/lib/func/commom";
import { getCity } from "@/components/lib/func/commom";
import DrgtDetailModal from "@/components/ui/modal/coMat/drgtDetailModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { ContentsDiv, DateMonthPicker, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui";
import { MonthPicker } from "@common/business_components/ui/date_month_picker/mothPicker";
import { ContentsDiv, DateMonthPicker, downloadExcel, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, DatePicker, Label } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import { Button, Label, ScrollArea } from "@common/components/ui";
import { useEffect, useState } from "react";
import { toast, Toaster } from "react-hot-toast";
export default function DrgtSttn() {
const { code, drgt, drgtSearch, drgtModalSearch } = useWrStore((state) => state);
const { code, drgt, drgtSearch, drgtModalSearch, drgtInit } = useWrStore((state) => state);
const { drgtRes, drgtMoRes } = drgt;
const [fromDate, setFromDate] = useState<string>();
const [toDate, setToDate] = useState<string>();
@@ -22,6 +21,7 @@ export default function DrgtSttn() {
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [tableData, setTableData] = useState<customDrgt[]>();
const [selectedSgg, setSelectedSgg] = useState<string>('');
const [excelData, setExcelData] = useState<any[]>();
const [drgtModal, setDrgtModal] = useState<{
isOpen: boolean;
data: customDrgt | undefined;
@@ -35,45 +35,82 @@ export default function DrgtSttn() {
}
}
const excelClick = () => {
downloadExcel({
data: excelData ?? [],
fileName: "우리동네가뭄 현황",
sheetNameArray: ["Sheet1"],
headerRange: "A1:G1"
})
}
useEffect(() => {
if (!drgt.isInit) {
drgtInit();
}
}, [drgt.isInit, drgtInit]);
useEffect(() => {
let resData = Array.isArray(drgtRes) ? drgtRes : [drgtRes];
resData = resData.map((item) => {
if (item.sggNm === '세종시') {
return {
...item,
ctpvCd: '세종특별자치시',
sggNm: ''
}
}
return {
...item,
ctpvCd: code.cityCd.find(ct => (ct.text == item.sggNm || (code.sggCd.find(sgg => sgg.text == item.sggNm)?.val.substring(0, 2) == ct.val)))?.text,
sggNm: code.sggCd.find(sgg => sgg.text == item.sggNm)?.text
}
})
const data = Array.isArray(resData)
? resData.map((item: customDrgt) => {
return {
"시도": item.ctpvCd,
"시군구": item.sggNm,
"정상(횟수)": item.normal,
"관심(횟수)": item.attention,
"주의(횟수)": item.caution,
"관심+주의(횟수)": item.warning,
"가뭄순위": item.ranking,
}
}) : [];
setExcelData(data)
setTableData(resData)
}, [drgtRes])
useEffect(() => {
if (drgtMoRes && drgtMoRes.length > 0) {
setDrgtModal({ isOpen: true, data: drgtMoRes });
}
}, [drgtMoRes]);
const getCityVal = (val: string, sggCd: SelectDataType[]) => {
const data = sggCd
.filter((item) => String(item.val).substring(0, 2) === val)
.map((item) => ({
val: item.val,
text: item.text, // 한국어 텍스트
}));
return data;
};
const getSelCity = (val: string | number) => {
const cityValue = String(val);
const data = getCityVal(cityValue, code.sggCd);
setSelectSgg(data);
const sggVal = data.length > 0 ? data[0].val : '';
setSelectedSgg(sggVal); // 시군구 value 세팅
setObjSearch({ city: cityValue, sgg: sggVal });
if (cityValue === "0") {
setSelectSgg([]);
setSelectedSgg('');
setObjSearch({ city: cityValue, sgg: '' });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setSelectedSgg(firstSggVal);
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
setSelectedSgg(String(val));
setObjSearch({ ...objSearch, sgg: String(val) });
};
const excelClick = useCallback(() => {
excelDownload(
[[]],
"우리동네가뭄",
"sample_drgtSttn.xlsx",
"A1:H1"
);
}, []);
useEffect(() => {
if (fromDate === undefined && toDate === undefined) {
const today = new Date();
@@ -101,7 +138,7 @@ export default function DrgtSttn() {
onSelectValue={getSelCity}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
className="col-span-2 w-1/6 h-full text-2xl ml-8"
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
@@ -110,7 +147,7 @@ export default function DrgtSttn() {
/>
<div className="col-span-5 w-auto">
<div className="flex flex-row items-center">
<Label className="text-xl text-gray-600">조회기간</Label>
<Label className="text-xl text-gray-600 ml-8">조회기간</Label>
<div className="flex flex-row w-1/4">
<DateMonthPicker
field={{
@@ -125,8 +162,9 @@ export default function DrgtSttn() {
setFromDate(formatted);
}
}}
selectClass="ml-4 text-2xl"
/>
<span>~</span>
<span className="ml-2 text-center">~</span>
<DateMonthPicker
field={{
value: toDate,
@@ -140,6 +178,7 @@ export default function DrgtSttn() {
setToDate(formatted);
}
}}
selectClass="ml-4"
/>
</div>
</div>
@@ -148,35 +187,42 @@ export default function DrgtSttn() {
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={(e) => {
let sidoText = '';
let sggText = '';
let sggText = [];
if (objSearch?.city.toString().length > 0) {
sidoText = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? '';
}
if (objSearch?.sgg.toString().length > 1) {
sggText = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
} else {
sggText = code.sggCd.filter((item) => item.val.substring(0, 2) === objSearch?.city).map((item) => item.text);
}
if (sidoText === '전체') { sidoText = '' }
if (sidoText === '세종특별자치시') {
sidoText = '세종시';
sggText = sidoText;
}
drgtSearch(fromDate ?? "", toDate ?? "", sidoText, sggText)
const searchArr = [];
if (sidoText) searchArr.push(sidoText);
if (Array.isArray(sggText)) {
searchArr.push(...sggText);
} else if (sggText) {
searchArr.push(sggText);
}
drgtSearch(fromDate ?? "", toDate ?? "", searchArr)
}}
>
조회
</Button>
<Button
size={"sm"}
className={btnClass}
onClick={() => { }}
>
상세보기
</Button>
<Button
size={"sm"}
className={`${btnClass} green`}
@@ -199,17 +245,19 @@ export default function DrgtSttn() {
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
tableData={drgtRes || []}
headlist={drgtHeadListExport}
pageSize={20}
total={10000}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index ?? undefined, headId);
}}
onPageChange={() => { }}
></UiTable>
<ScrollArea className="h-[66vh]">
<UiTable
headName={undefined}
tableData={tableData || []}
headlist={drgtHeadListExport}
pageSize={14}
total={0}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index ?? undefined, headId);
}}
onPageChange={() => { }}
></UiTable>
</ScrollArea>
</ListDiv>
</div>
</div>
@@ -219,7 +267,7 @@ export default function DrgtSttn() {
setDrgtModal({ ...drgtModal, isOpen: !drgtModal.isOpen });
}}
bodyClass={"w-[50vw] max-w-none h-[80vh]"}
body={<DrgtDetailModal data={drgtModal.data} /*formObj={drgtModal.data}*/ />}
body={<DrgtDetailModal data={drgtModal.data} />}
/>
<Toaster position="top-center" />
</ContentsDiv>
+46 -27
View File
@@ -44,7 +44,7 @@ export default function LdrmntCoSttn() {
const [searchTxt, setSearchTxt] = useState<string>();
const ref = useRef<ReturnType>({} as ReturnType);
const [excelData, setExcelData] = useState<any[]>();
const [isExcelDownload, setIsExcelDownload] = useState(false);
const excelClick = useCallback(() => {
excelDownload(
@@ -57,14 +57,22 @@ export default function LdrmntCoSttn() {
}, []);
const excelDownloadClick = () => {
downloadExcel({
data: excelData ?? [],
fileName: "잠재수요처 관리",
sheetNameArray: ["Sheet1"],
headerRange: "A1:F1"
})
ldrmntSearch(1, "10000", "0", "");
setIsExcelDownload(true);
}
useEffect(() => {
if (isExcelDownload && excelData && excelData.length > 0) {
downloadExcel({
data: excelData,
fileName: "잠재수요처 관리",
sheetNameArray: ["Sheet1"],
headerRange: "A1:F1"
});
setIsExcelDownload(false);
}
}, [excelData, isExcelDownload]);
const [isExcelOpen, setIsExcelOpen] = useState<{
isModal: boolean;
}>({ isModal: false });
@@ -73,14 +81,21 @@ export default function LdrmntCoSttn() {
if (index !== undefined) {
setDetailModal({ isOpen: true, data: ldrmntRes[index] });
}
}
const getSelCity = (val: string | number) => {
const cityValue = String(val); // Ensure val is treated as a string
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: '' });
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
@@ -125,10 +140,9 @@ export default function LdrmntCoSttn() {
setExcelData(data)
}, [excelList])
useEffect(() => {
let cityTxt = '';
let sggTxt = '';
let cityTxt = ''
let sggTxt = ''
if (objSearch?.city.toString().length > 0) {
cityTxt = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? '';
@@ -138,21 +152,23 @@ export default function LdrmntCoSttn() {
sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
}
let cityNm = "";
if (cityTxt === '전체' || cityTxt === '') {
cityTxt = '0'
}
let cityNm = ""
if (sggTxt !== "0") {
cityNm = cityTxt + " " + sggTxt;
cityNm = cityTxt + " " + sggTxt
} else {
cityNm = cityTxt;
cityNm = cityTxt
}
if (cityNm) {
if (cityTxt === '') cityNm = "0";
ldrmntSearch(pageNation, cityNm, searchTxt || "");
ldrmntSearch(pageNation, '20', cityNm, searchTxt || "");
} else {
ldrmntSearch(pageNation, "", searchTxt || "");
ldrmntSearch(pageNation, '20', '0', searchTxt || "");
}
}, [pageNation, objSearch, code, searchTxt, ldrmntSearch])
}, [pageNation]);
return (
<>
@@ -175,6 +191,7 @@ export default function LdrmntCoSttn() {
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<SearchInput
@@ -189,7 +206,7 @@ export default function LdrmntCoSttn() {
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={(e) => {
@@ -205,6 +222,10 @@ export default function LdrmntCoSttn() {
sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
}
if (cityTxt === '전체' || cityTxt === '') {
cityTxt = '0'
}
let cityNm = ""
if (sggTxt !== "0") {
@@ -212,10 +233,8 @@ export default function LdrmntCoSttn() {
} else {
cityNm = cityTxt
}
if (cityNm) {
if (cityTxt === '') cityNm = "0";
ldrmntSearch(pageNation, cityNm, searchTxt || "");
ldrmntSearch(pageNation, '20', cityNm, searchTxt || "");
}
}}
>
+12
View File
@@ -153,6 +153,18 @@ export default function Home() {
</div>
</div>
</div>
{/* 시설 현황(지도) */}
<div>
<h2 className="text-xl font-semibold mb-4">시설 현황(지도)</h2>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-2">
<Button className="w-full" onClick={() => {router.push('/map/htdflSttn')}}>
시설 현황(지도)
</Button>
</div>
</div>
</div>
</div>
</>
);
@@ -37,10 +37,7 @@ export default function RainSttn() {
const [pageNation, setPageNation] = useState(1);
const [isChHead, setIsChHead] = useState(false);
const [bdstTable, setBdstTable] = useState<customRainBdst[]>();
const [detailModal, setDetailModal] = useState<{
isOpen: boolean;
data: customRainBdst | undefined;
}>({ data: undefined, isOpen: false });
const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false })
const [bdstChgProps, setBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
@@ -49,25 +46,12 @@ export default function RainSttn() {
const codeData = getCityOrSgg(ref);
const cellClick = (index: number | undefined, headId: string) => {
if (
headId !== "chBuildingYn" &&
index !== undefined &&
bdstTable !== undefined
) {
setDetailModal({ isOpen: true, data: bdstTable[index] });
} else if (
headId === "chBuildingYn" &&
index !== undefined &&
bdstTable !== undefined
) {
if (
bdstTable[index] &&
bdstTable[index]?.chgBuilding[0]?.bdstChgNm !== null
) {
setBdstChgProps({
isOpen: true,
chBuilding: bdstTable[index].chgBuilding,
});
if (headId !== 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
setDetailModal({ isOpen: true, data: bdstTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
if (bdstTable[index] && bdstTable[index]?.chgBuilding[0]?.bdstChgNm !== null) {
setBdstChgProps({ isOpen: true, chBuilding: bdstTable[index].chgBuilding });
}
}
};
@@ -168,9 +152,7 @@ export default function RainSttn() {
headlist={rainSttn.rainSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index ?? undefined, headId);
}}
cellClick={(index: number | undefined, headId: string) => { cellClick(index ?? undefined, headId) }}
onPageChange={setPageNation}
></UiTable>
</ListDiv>
@@ -31,7 +31,6 @@ import { useCallback, useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
import { shallow } from "zustand/shallow";
//-----------------------
export default function Page() {
const {
code,
@@ -43,7 +42,6 @@ export default function Page() {
rainChkExcelDownload,
rainChkHeadChange,
isRainSttn,
rswtChkgdnInit,
rswtChkPolInit,
rswtChkDetilsUpdate,
@@ -61,7 +59,7 @@ export default function Page() {
rswtrChkDetailsZodType[]
>([]);
const [deleteIdx, setDeleteIdx] = useState<number[]>([]);
const [pageNation, setPageNation] = useState(1);
const [selFcltDiv, setSelFcltDiv] = useState("RNB");
const [isModal, setIsModal] = useState({
open: false,
@@ -70,9 +68,9 @@ export default function Page() {
const cellClick = () => {
if (searchYear)
if (selFcltDiv === "RNB") {
rainChkInit(searchYear, "0");
rainChkInit("1", "20", searchYear, "0");
} else {
rswtChkgdnInit(searchYear, "0");
rswtChkgdnInit("1", "20", searchYear, "0");
}
};
@@ -82,14 +80,14 @@ export default function Page() {
useEffect(() => {
if (!rainChkgdn.isInit) {
rainChkInit(new Date().getFullYear().toString(), "0");
rainChkInit("1", "20", new Date().getFullYear().toString(), "0");
isRainSttn(false);
setIsModal({ data: {}, open: false });
setIsRegExcel(false);
}
if (!rswtChkgdn.isInit) {
rswtChkgdnInit(new Date().getFullYear().toString(), "0");
rswtChkgdnInit("1", "20", new Date().getFullYear().toString(), "0");
setIsModal({ data: {}, open: false });
setIsRegExcel(false);
}
@@ -179,9 +177,11 @@ export default function Page() {
selectClass={""}
selectArray={yearOptions}
value={searchYear}
onSelectValue={(val: string | number) =>
onSelectValue={(val: string | number) => {
setSearchYear(String(val))
}
}
/>
</div>
<div className="col-span-2 flex flex-row space-x-2">
@@ -255,6 +255,8 @@ export default function Page() {
detailUp={setUpdateRainDetail}
handleClick={handleClick}
rainChkData={rainChkgdn.rainChk}
pageNation={pageNation}
onPageChange={setPageNation}
/>
) : (
<RswtChkgdTable
@@ -262,6 +264,8 @@ export default function Page() {
deleteRswtData={setDeleteIdx}
handleRswtClick={handleClick}
rswtChkData={rswtChkgdn.rswtChkRes}
pageNation={pageNation}
onPageChange={setPageNation}
/>
)}
</ListDiv>
@@ -25,7 +25,7 @@ import {
} from "@common/components/ui";
import { useState, useEffect, useCallback } from "react";
import { set } from "react-hook-form";
import { Toaster } from "react-hot-toast";
import toast, { Toaster } from "react-hot-toast";
import { headMakeprfmnc } from "../chkgdnReg/TableHead";
import { InputTableBody } from "@/components/lib/func/TableData";
import {
@@ -51,8 +51,8 @@ export default function prfmncPrfmncReg() {
}>({ isModal: false, data: {} as FcPrfType });
const [inputData, setInputData] = useState<any[]>();
const [pbnstRuseFcltPk, setPbnstRuseFcltPk] = useState<string>();
const [pageNation , setPageNation] = useState<number>(1);
const [pageNation, setPageNation] = useState<number>(1);
const [cellNum, setCellNum] = useState<number>();
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
@@ -76,7 +76,7 @@ export default function prfmncPrfmncReg() {
);
prfmncHasuInit(String(pageNation), "41");
}
}, [inputData, searchYear, pbnstRuseFcltPk,prfmncHasuInit,pageNation]);
}, [inputData, searchYear, pbnstRuseFcltPk, prfmncHasuInit, pageNation]);
@@ -88,10 +88,16 @@ export default function prfmncPrfmncReg() {
isModal: false,
data: prfmncRes[index] || ({} as FcPrfType),
});
const inUsageData = structuredClone(initValInUsage);
const outUsageData = structuredClone(initValOutUsage);
const inSubtotal = structuredClone(subTotalObj);
const outSubtotal = structuredClone(subTotalObj);
const inUsageData = structuredClone(initValInUsage).map(row => ({
...row,
구분: "장내용수",
}));
const outUsageData = structuredClone(initValOutUsage).map(row => ({
...row,
구분: "장외용수",
}));
const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" };
const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" };
if (prfmncRes[index]?.prfList) {
prfmncRes[index].prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
@@ -127,7 +133,7 @@ export default function prfmncPrfmncReg() {
});
inUsageData.push(inSubtotal);
outUsageData.push(outSubtotal);
const usageTotalObj: { [key: string]: any } = structuredClone(totalObj);
const usageTotalObj: { [key: string]: any } = { ...structuredClone(totalObj), 구분: "총합계", 용수명: "" };
for (const key in usageTotalObj) {
if (["usage", "gubun", "edit"].includes(key)) continue;
const val =
@@ -162,7 +168,7 @@ export default function prfmncPrfmncReg() {
<ContentsDiv>
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<Title title={"하수재이용 시설 등록"} />
<Title title={"하수재이용 실적 등록"} />
<SearchDiv>
<SearchFormLeft>
<div className="col-span-2 flex flex-row space-x-2">
@@ -197,7 +203,7 @@ export default function prfmncPrfmncReg() {
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="space-y">
<div className="flex flex-row space-x-2">
<Button size={"sm"} onClick={(e) => {}}>
<Button size={"sm"} onClick={(e) => { }}>
조회
</Button>
<Button
@@ -211,7 +217,12 @@ export default function prfmncPrfmncReg() {
<Button
size={"sm"}
onClick={() => {
setIsExcelOPen({ ...isExcelOpen, isModal: true });
if (cellNum === undefined) {
toast.error("하수처리재이용시설을 선택해주세요.")
return;
} else {
setIsExcelOPen({ ...isExcelOpen, isModal: true });
}
}}
>
파일등록
@@ -238,6 +249,7 @@ export default function prfmncPrfmncReg() {
headName={"하수재이용시설 목록"}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index, headId);
setCellNum(index ?? 0);
}}
onPageChange={setPageNation}
children={undefined}
@@ -266,7 +278,7 @@ export default function prfmncPrfmncReg() {
</div>
</ContentsDiv>
<UiModal
bodyClass={""}
bodyClass={"w-[30vw] max-w-none"}
body={<PbnPrfExcelModal data={isExcelOpen.data}></PbnPrfExcelModal>}
open={isExcelOpen.isModal}
onOpenChange={() => {
@@ -8,9 +8,11 @@ import { excelDownload, yearOptions } from "@/components/lib/func/commom";
import SearchInputText from "@/components/ui/searchInput/searchInput";
import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ChartConfig, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import { Button, Label, Table, TableBody, TableHeader } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { headMakeprfmnc } from "../../prfmncReg/chkgdnReg/TableHead";
import { InputTableBody } from "@/components/lib/func/TableData";
export default function PbnstPrfmncSttn() {
const {
@@ -34,9 +36,9 @@ export default function PbnstPrfmncSttn() {
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>();
const [pbnRuseNm, setPbnRuseNm] = useState<string>("");
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [excelData, setExcelData] = useState<any[]>([]);
const excelClick = useCallback(() => {
excelDownload(
@@ -83,15 +85,20 @@ export default function PbnstPrfmncSttn() {
type lineChartDataProps = { label: string, val: number, color: string };
const prfTableData = (prfList: PrfType[], year: string) => {
const inUsageData = structuredClone(initValInUsage);
const outUsageData = structuredClone(initValOutUsage);
const inSubtotal = structuredClone(subTotalObj);
const outSubtotal = structuredClone(subTotalObj);
const inUsageData = structuredClone(initValInUsage).map(row => ({
...row,
구분: "장내용수",
}));
const outUsageData = structuredClone(initValOutUsage).map(row => ({
...row,
구분: "장외용수",
}));
const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" };
const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" };
const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year)
if (prfList && prfFilter.length > 0) {
prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
const monthKey = `${month}월`;
@@ -142,7 +149,7 @@ export default function PbnstPrfmncSttn() {
setPrfData([]);
}
}
const [chartId, setChartId] = useState<string>();
useEffect(() => {
// 누적 변수 초기화
@@ -198,8 +205,8 @@ export default function PbnstPrfmncSttn() {
prfTableData([], year)
rawData.forEach((item) => {
// 실적 테이블 데이터
if (chartId){
if(item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId){
if (chartId) {
if (item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId) {
prfTableData(item.prfList, year);
}
item.prfList.filter((e) => e.ym.slice(0, 4) === year && e.pbnstRuseFcltFk === chartId).forEach((element) => {
@@ -265,40 +272,40 @@ export default function PbnstPrfmncSttn() {
});
// 도넛차트 데이터
dnData.push({ label: "세척수", val: wshwtr, fill: "red" });
dnData.push({ label: "냉각수", val: coolUswtr, fill: "orange" });
dnData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" });
dnData.push({ label: "식수대", val: drwtUswtr, fill: "green" });
dnData.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" });
dnData.push({ label: "중수도", val: ithlRswtr, fill: "blue" });
dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" });
dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" });
dnData.push({ label: "세척살수용수", val: resn, fill: "orange" });
dnData.push({ label: "조경용수", val: lndwtr, fill: "yellow" });
dnData.push({ label: "친수용수", val: hydUswtr, fill: "green" });
dnData.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" });
dnData.push({ label: "농업용수", val: farmUswtr, fill: "blue" });
dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" });
dnData.push({ label: "공업용수", val: indstUswtr, fill: "black" });
dnData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" });
dnData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" });
dnData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" });
dnData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" });
dnData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" });
dnData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" });
dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" });
dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" });
dnData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" });
dnData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" });
dnData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" });
dnData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" });
dnData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" });
dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" });
dnData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" });
// 장내용수 도넛
dnInData.push({ label: "세척수", val: wshwtr, fill: "yellow" });
dnInData.push({ label: "냉각수", val: coolUswtr, fill: "green" });
dnInData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" });
dnInData.push({ label: "식수대", val: drwtUswtr, fill: "green" });
dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" });
dnInData.push({ label: "중수도", val: ithlRswtr, fill: "green" });
dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" });
dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" });
dnInData.push({ label: "세척살수용수", val: resn, fill: "yellow" });
dnInData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" });
dnInData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" });
dnInData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" });
dnInData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" });
dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" });
dnInData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" });
dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" });
dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" });
dnInData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" });
// 장외용수 도넛
dnOutData.push({ label: "조경용수", val: lndwtr, fill: "yellow" });
dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "black" });
dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" });
dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "black" });
dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" });
dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "black" });
dnOutData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" });
dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" });
dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" });
dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" });
dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" });
dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" });
// 라인차트 데이터
setInLineData({
@@ -466,7 +473,7 @@ export default function PbnstPrfmncSttn() {
useEffect(() => {
setbdstTable(prfmncRes);
if(prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk)
if (prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk)
}, [prfmncRes]);
@@ -619,12 +626,12 @@ export default function PbnstPrfmncSttn() {
</div>
<h1 className="">하수재이용실적 현황</h1>
<ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
{/* <ScrollArea className="h-[60vh]"> */}
{/* <div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
</div> */}
<UiTable
headName={undefined}
@@ -637,7 +644,26 @@ export default function PbnstPrfmncSttn() {
throw new Error("Function not implemented.");
}}>
</UiTable>
</ScrollArea>
{/* <ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
<Table variant="secondary">
<TableHeader className="font-bold">
{headMakeprfmnc(monthHeadList, [])}
</TableHeader>
<TableBody>
{InputTableBody(prfData ?? [], monthHeadList, setPrfData)}
</TableBody>
</Table>
</ScrollArea>
</ListDiv> */}
{/* </ScrollArea> */}
</ListDiv>
</ListDiv>
+185
View File
@@ -0,0 +1,185 @@
import { getFetch } from "@/components/lib/api";
export type wrMapType = wrMapState & wrMapActions;
export type wrMapState = {
wrMap: {
buildingSearchRes: any[]
} // 건물 검색 결과 데이터
};
type wrMapActions = {
buildingNmSearch: (
flg: string,
sigunCode: string,
searchNm: string | undefined
) => void;
buildingNmSearchMultiple: (
checkedLayers: string[],
sigunCode: string,
searchNm: string | undefined
) => void; // 여러 레이어 동시 검색 함수 추가
};
export const wrMapStateExport: wrMapState = {
wrMap: {
buildingSearchRes: []
}
};
// 레이어 타입과 플래그 매핑
const LAYER_FLAG_MAP: { [key: string]: string } = {
// '46': '???', // 하수처리시설
// '47': '???', // 하수재이용시설
'48': 'RNB', // 빗물이용시설
'49': 'RST', // 중수도시설
'50': 'HTB', // 온배수시설
};
const findBdst = async (flg: string, sggCd: string, searchTxt: string | undefined) => {
return await getFetch('wr', `/reuseFc/building/${flg}/${sggCd}` + (searchTxt ? `/${searchTxt}` : ''))
}
export const wrMapActionsExport: (set: any) => wrMapActions = (
set: any
) => {
return {
buildingNmSearch: async (
flg: string,
sigunCode: string,
searchNm: string | undefined
) => {
try {
// if (!searchNm || searchNm.length < 2) {
// set((state: wrMapState) => ({
// ...state,
// buildingSearchData: []
// }));
// return;
// }
console.log("건축물 코드, 시군구 코드, 검색어: ", flg, sigunCode, searchNm);
const response = await findBdst(flg, sigunCode, searchNm);
console.log("response: ", response);
// API 응답을 SearchInputText 형식으로 변환
const searchListFormatted = (response || []).map((building: any) => ({
searchNm: building.bdstNm,
searchPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
layerType: flg,
...building
}));
// findBdst와 유사한 API 호출
// findBdst(flg, sigunCode || "", searchNm).then(() => {
// console.log("건물 검색 API 호출: ", sigunCode, searchNm, flg);
// // bdst.bdstBase를 SearchInputText 형식으로 변환
// const searchListFormatted = (state.bdst.bdstBase || []).map((building: any) => ({
// searchNm: building.bdstNm,
// searchPk: String(building.bdstInfoPk),
// dmdCoNm: building.bdstNm,
// layerType: flg,
// ...building
// }));
// set((state: any) => ({
// ...state,
// wrMap: {
// ...state.wrMap,
// buildingSearchData: searchListFormatted
// }
// }));
// });
set((state: wrMapState) => ({
...state,
wrMap: {
...state.wrMap,
buildingSearchRes: searchListFormatted
}
}));
} catch (error) {
console.error("건물 검색 중 오류:", error);
}
},
buildingNmSearchMultiple: async (
checkedLayers: string[],
sigunCode: string,
searchNm: string | undefined
) => {
try {
if (!searchNm || searchNm.length < 2) {
set((state: any) => ({
...state.wrMap,
buildingSearchData: []
}));
return;
}
// 체크된 레이어들에 대해 병렬로 API 호출
const searchPromises = checkedLayers
.filter(layer => LAYER_FLAG_MAP[layer]) // 매핑된 레이어만 필터링
.map(layer => {
const flg = LAYER_FLAG_MAP[layer];
return findBdst(flg, sigunCode, searchNm)
.then(response => {
console.log(`레이어 ${layer}(${flg}) API 응답:`, response);
const searchListFormatted = (state.bdst.bdstBase || []).map((building: any) => ({
searchNm: building.bdstNm,
searchPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
layerType: flg,
...building
}));
return {
layerType: layer,
flg: flg,
data: bdstNames
};
})
.catch(error => {
console.error(`레이어 ${layer}(${flg}) 검색 중 오류:`, error);
return { layerType: layer, flg: flg, data: [] };
});
});
const results = await Promise.all(searchPromises);
console.log("다중 레이어 검색 결과:", results);
// 모든 결과를 하나의 배열로 합치기
const allSearchResults = results.reduce((acc, result) => {
const formatted = result.data.map((building: any) => ({
searchNm: building.bdstNm,
searchPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
layerType: result.layerType,
flg: result.flg,
...building.originalData
}));
return [...acc, ...formatted];
}, [] as any[]);
console.log("최종 합쳐진 검색 결과:", {
totalCount: allSearchResults.length,
sampleData: allSearchResults.slice(0, 3)
});
set((state: any) => ({
...state.wrMap,
buildingSearchData: allSearchResults
}));
} catch (error) {
console.error("다중 레이어 검색 중 오류:", error);
set((state: any) => ({
...state,
buildingSearchData: []
}));
}
}
};
};
@@ -2,8 +2,8 @@ import { z } from "zod";
import type { CommonTableProps } from "../../common/type/header";
export const drgtZod = z.object({
ctpvCd: z.string().default(''),
sggNm: z.string().default(''),
ctpvCd: z.string().default('').nullish(),
sggNm: z.string().default('').nullish(),
normal: z.string().default(''),
attention: z.string().default(''),
caution: z.string().default(''),
+83 -59
View File
@@ -1,4 +1,4 @@
import { fetchType, getFetch } from "@/components/lib/api";
import { fetchType, getFetch, postFectch } from "@/components/lib/api";
import type { CommonTableProps } from "../../common/type/header";
import { drgtHeadListExport, drgtZod } from "./headList";
import type { z } from "zod";
@@ -14,11 +14,13 @@ export type drgtState = {
drgtHeadList: CommonTableProps[];
drgtRes: customDrgt[];
drgtMoRes: customDrgt[];
isInit: boolean;
}
}
type drgtActions = {
drgtSearch: (fromDate: string, toDate: string, sido: string, sgg: string) => void
drgtInit: () => void
drgtSearch: (fromDate: string, toDate: string, sido: string[]) => void
drgtModalSearch: (fromDate: string, toDate: string, sgg: string) => void
};
@@ -29,72 +31,97 @@ export const drgtStateExport: drgtState = {
drgtHeadList: drgtHeadList,
drgtRes: drgtRes,
drgtMoRes: drgtRes,
isInit: false,
}
}
const initFetch = async (fromDate: string, toDate: string, sido:string[]) => {
console.log("sido",sido)
const drgtData = await postFectch(
`/il/getDataCount/${fromDate}/${toDate}`,
sido,
fetchType.WR
);
//주의, 관심, 정상 추출
const statusData = Array.from(
new Set(drgtData.data.map((e) => e.lvngMfgUswtrDrgtInfo))
);
const statusMap = drgtData.data.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => {
if (!acc[sggNm]) {
acc[sggNm] = { sggNm, ...Object.fromEntries(statusData.map((e) => [e, 0])) };
}
acc[sggNm][status] = Number(countDrgt);
return acc;
}, {});
const data = Object.values(statusMap);
const formatStatus = data.map((item) => {
const attention = item['관심'] || 0;
const caution = item['주의'] || 0;
return {
sggNm: item.sggNm,
normal: String(item['정상'] || 0),
caution: String(caution),
attention: String(attention),
warning: String(attention + caution),
};
});
const ranking = Array.from(
new Set(formatStatus.map(e => Number(e.warning)))
).sort((a, b) => b - a);
const rankData = ranking.reduce((acc, val, idx) => {
acc[val] = idx + 1;
return acc;
}, {});
const drgtRes = formatStatus.map(item => ({
...item,
warning: String(item.warning),
ranking: rankData[Number(item.warning)],
}));
return drgtRes;
}
export const drgtActionsExport: (set: any) => drgtActions = (
set: any
) => {
return {
drgtSearch: async (fromDate: string, toDate: string, sido: string, sgg: string) => {
drgtInit: async () => {
try {
let searchUrl = "";
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const formatted = `${year}${month}`;
const fromDate = formatted;
const toDate = formatted;
if (sido === "" || sido === "전체") {
searchUrl = `/il/getDataCount/${fromDate}/${toDate}`;
} else {
searchUrl = `/il/getDataCount/${fromDate}/${toDate}/${sido}/${sgg}`;
}
console.log("searchUrl", searchUrl);
const drgtData = await getFetch(
fetchType.WR,
searchUrl
);
console.log("drgtRes", drgtData)
//주의, 관심, 정상 추출
const statusData = Array.from(
new Set(drgtData.map((e) => e.lvngMfgUswtrDrgtInfo))
);
const statusMap = drgtData.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => {
if (!acc[sggNm]) {
acc[sggNm] = { sggNm, ...Object.fromEntries(statusData.map((e) => [e, 0])) };
}
acc[sggNm][status] = Number(countDrgt);
return acc;
}, {});
const data = Object.values(statusMap);
const formatStatus = data.map((item) => {
const attention = item['관심'] || 0;
const caution = item['주의'] || 0;
//FIXME: 페이지 처음 로딩될때 데이터 보려고 6월로 박아놓음 이거 fromDate로 고치기
const drgtRes = await initFetch("202506", toDate, []);
set((state: drgtState) => {
return {
sggNm: item.sggNm,
normal: String(item['정상'] || 0),
caution: String(caution),
attention: String(attention),
warning: String(attention + caution),
};
...state,
drgt: {
...state.drgt,
drgtRes: drgtRes,
isInit: false,
},
}
});
} catch (e) {
console.log(e)
}
},
drgtSearch: async (fromDate: string, toDate: string, sido: string[]) => {
try {
const drgtRes = await initFetch(fromDate, toDate, sido);
const ranking = Array.from(
new Set(formatStatus.map(e => Number(e.warning)))
).sort((a, b) => b - a);
const rankData = ranking.reduce((acc, val, idx) => {
acc[val] = idx + 1;
return acc;
}, {});
const drgtRes = formatStatus.map(item => ({
...item,
warning: String(item.warning),
ranking: rankData[Number(item.warning)],
}));
set((state: drgtState) => {
return {
...state,
@@ -115,10 +142,7 @@ export const drgtActionsExport: (set: any) => drgtActions = (
`/il/getData/${fromDate}/${toDate}/${sgg}/${sgg}`
);
console.log("drgtMoData", drgtMoData)
const drgtMoRes = drgtMoData.map((item) => {
console.log("item", item)
const year = item.anlsYmd.slice(0, 4);
const month = item.anlsYmd.slice(4, 6);
const drgtState = item.lvngMfgUswtrDrgtInfo;
@@ -46,29 +46,17 @@ export const names: nameMap = {
}
export const excelPbnstSttnParsing = (data: any[]): string[][] => {
export const excelLdrmntParsing = (data: any[]): string[][] => {
const excelExportData: string[][] = [];
data.forEach((item) => {
excelExportData.push([
item.rvbsn,
String(item.lgbsn),
item.mdmbsn ?? "",
item.rgnNm ?? "",
String(item.sido ?? ""),
String(item.gugun ?? ""),
String(item.bscFctNm ?? ""),
String(item.efflWqltStndrdNm ?? ""),
String(item.fctCpc ?? ""),
String(item.tel ?? ""),
String(item.ruseUtztnFcltNm ?? ""),
String(item.fcltCpct ?? ""),
String(item.splyExtraCpct ?? ""),
String(item.ruseCpct ?? ""),
String(item.usageTotal ?? ""),
String(item.inUsage ?? ""),
String(item.outUsage ?? ""),
item.fctryAddr?.split(" ")[0] || "",
item.fctryAddr?.split(" ")[1] || "",
item.dmdCoNm,
item.fctryAddr,
item.fctryMngNo,
item.telno,
])
})
return excelExportData;
+40 -27
View File
@@ -2,10 +2,8 @@ import type { CommonTableProps } from "@/app/store/common/type/header";
import { deleteFectch, fetchType, getFetch, postFectch, putFectch } from "@/components/lib/api";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import { z } from "zod";
import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList";
import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg";
import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList";
// 테이블 헤드 리스트
const ldrmntHeadList = ldrmntHeadListExport;
@@ -34,7 +32,7 @@ const excelDownList: ExcelType[] = [];
type ldrmntActions = {
ldrmntRegData: (data: any[]) => void,
ldrmntInit: (con: string) => void
ldrmntSearch: (page: number, sggCd: string, coNm: string) => void;
ldrmntSearch: (page: number, sggCd: string, total: string, coNm: string) => void;
ldrmntUpdate: (ldrmntRes: customLdrmnt) => void;
ldrmntDelete: () => void;
};
@@ -61,6 +59,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
fetchType.WR,
`/reuseFc/request/temp/1/20/${con}`
);
set((state: ldrmntState) => {
return {
...state,
@@ -68,6 +67,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
...state.ldrmnt,
ldrmntRes: ldrmntRes,
isInit: false,
excelList: excelDownList,
},
}
});
@@ -101,44 +101,57 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
}
},
ldrmntSearch: async (page: number, sggCd: string, coNm: string) => {
ldrmntSearch: async (page: number, total: string, sggCd: string, coNm: string) => {
let searchUrl = "";
if (sggCd !== "") {
if (coNm !== "") {
searchUrl = `/reuseFc/request/temp/${page}/20/${sggCd}/${coNm}`;
searchUrl = `/reuseFc/request/temp/${page}/${total}/${sggCd}/${coNm}`;
} else {
searchUrl = `/reuseFc/request/temp/${page}/20/${sggCd}`;
searchUrl = `/reuseFc/request/temp/${page}/${total}/${sggCd}`;
}
}
try {
const ldrmntRes = await getFetch(
fetchType.WR,
searchUrl
);
ldrmntRes.forEach((item: customLdrmnt) => {
excelDownList.push({
ctpvCd: item.ctpvCd,
sggCd: item.sggCd,
dmdCoNm: item.dmdCoNm,
fctryAddr: item.fctryAddr,
fctryMngNo: item.fctryMngNo,
telno: item.telno,
if (Number(total) > 5000) {
let excelDownList: ExcelType[] = [];
ldrmntRes.forEach((item: customLdrmnt) => {
excelDownList.push({
ctpvCd: item.ctpvCd,
sggCd: item.sggCd,
dmdCoNm: item.dmdCoNm,
fctryAddr: item.fctryAddr,
fctryMngNo: item.fctryMngNo,
telno: item.telno,
})
})
})
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
excelList: excelDownList,
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes,
excelList: excelDownList,
},
}
})
},
};
});
return excelDownList;
} else
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes,
isInit: false,
},
};
});
return ldrmntRes;
} catch (e) {
console.log(e)
}
@@ -134,7 +134,7 @@ export const rainChkgdnHeadListExport: TableUpperProps[] = [
{ id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true },
{ id: "chckYmd", title: "점검일", mergeCell: true },
{ id: "utztnUsgCd", title: "이용용도" },
{ id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" },
{ id: "totalSplyUswtrUtztnQnt", title: "연간대체수이용량(m³)" },
{ id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" },
{
id: "ph",
@@ -52,7 +52,7 @@ export type rainChkgdnState = {
};
type rainChkgdnActions = {
rainChkInit: (sggCd: string, searchTxt: string) => void;
rainChkInit: (pageNo: string, cnt: string, sggCd: string, searchTxt: string) => void;
rainChkDetilUpdate(rainChk: rainChkZodType[]): Promise<void>;
rainChkGuidUpdate(rainChk: rainChkZodType): Promise<void>;
rainChkExcelDownload(
@@ -98,11 +98,11 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => {
return chData;
};
const initFetch = async (txt: string | undefined, sgg: string | undefined) => {
const initFetch = async (pageNo: string, cnt: string, txt: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rainRes = await getFetch(
"wr",
`/reuseChk/rainy` + (txt ? `/${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0")
`/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + (cnt ? `/${cnt}` : "/20") + (txt ? `/${txt}` : `/${currentYear}`) + (sgg ? `/${sgg}` : "/0")
);
return rainRes;
};
@@ -140,7 +140,7 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
});
}
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(currentYear, "0");
const rainChkRes = await initFetch("1", "20", currentYear, "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
@@ -156,9 +156,9 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
});
}
},
rainChkInit: async (searchTxt: string, sggCd: string) => {
rainChkInit: async (pageNo: string, cnt: string, searchTxt: string, sggCd: string) => {
try {
const rainChkRes = await initFetch(searchTxt, sggCd);
const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd);
return set((state: rainChkgdnState) => ({
...state,
@@ -3,7 +3,7 @@ import {
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import type {
SelectDataType,
SheetParameter,
SheetParameter,
} from "@common/business_components";
import {
deleteFectch,
@@ -41,12 +41,12 @@ export type rswtChkgdnState = {
rswtChkRes: rswtChkZodType[];
rswtChkPolGuide: rswtChkZodType[];
isInit: boolean;
isSttn:boolean
isSttn: boolean
};
};
type rswtChkgdnActions = {
rswtChkgdnInit: (year: string, sggCd: string) => void;
rswtChkgdnInit: (pageNo: string, cnt: string, year: string, sggCd: string) => void;
rswtrChkExcelDownload(
sidoCode: SelectDataType[],
sggCode: SelectDataType[]
@@ -57,7 +57,7 @@ type rswtChkgdnActions = {
rswtChkPolAndGuide: (chkGdnPk: number) => void;
rswtChkPolInit: () => void;
rwstPolAndGuideUpdate: (rswtChk: rswtChkZodType[]) => void;
rswtChkHeadChange: (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => void;
rswtChkHeadChange: (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => void;
};
export type rswtChkgdnType = rswtChkgdnState & rswtChkgdnActions;
@@ -75,17 +75,19 @@ export const rswtChkgdnStateExport: rswtChkgdnState = {
rswtChkRes: [],
rswtChkPolGuide: [],
isInit: false,
isSttn:false,
isSttn: false,
},
};
const initFetch = async (year: string | undefined, sgg: string | undefined) => {
const initFetch = async (pageNo: string, cnt: string, year: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetch(
"wr",
`/reuseChk/reuseWater/` +
(year ? `${year}` : currentYear) +
(sgg ? `/${sgg}` : "/0")
`/reuseChk/reuseWater` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/20") +
(year ? `/${year}` : `/${currentYear}`) +
(sgg ? `/${sgg}` : "/0")
);
return rswtChkRes;
};
@@ -137,9 +139,9 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
},
}));
},
rswtChkgdnInit: async (year: string, sggCd: string) => {
rswtChkgdnInit: async (pageNo: string, cnt: string, year: string, sggCd: string) => {
try {
const rswtChkRes = await initFetch(year, sggCd);
const rswtChkRes = await initFetch(pageNo, cnt, year, sggCd);
return set((state: rswtChkgdnState) => ({
...state,
@@ -163,7 +165,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
});
}
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
const rswtChkRes = await initFetch("1", "20", currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -188,7 +190,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
});
}
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
const rswtChkRes = await initFetch("1", "20", currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -212,7 +214,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
});
}
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
const rswtChkRes = await initFetch("1", "20", currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -238,7 +240,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
rswtChkPolGuide: rswtChkPolGuide,
},
}));
} catch (error) {}
} catch (error) { }
},
rswtChkPolInit: () => {
try {
@@ -249,7 +251,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
rswtChkPolGuide: undefined,
},
}));
} catch (error) {}
} catch (error) { }
},
rwstPolAndGuideUpdate: async (rswtChk: rswtChkZodType[]) => {
try {
@@ -265,7 +267,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
});
}
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
const rswtChkRes = await initFetch("1", "20", currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -299,7 +301,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
rswtDetailHeadList: rswtDetailHeadList,
},
}));
} catch (e) {}
} catch (e) { }
},
};
};
@@ -4,7 +4,7 @@ import type { TableUpperProps } from "../../common/type/header";
export const pbnPrfRegExcelZod = z.object({
ym: z.string().default(""),
bscFctNm: z.string().default(""),
pbnstRuseFcltFk:z.string(),
pbnstRuseFcltFk: z.string(),
pbnstRuseFcltNm: z.string().default(""),
file: z.instanceof(File).optional(),
});
@@ -18,20 +18,21 @@ export const pbnstPrfmncHeadListExport: TableUpperProps[] = [
];
export const monthHeadListExport: TableUpperProps[] = [
{ id: "구분", title: "구분", mergeCell: true },
{ id: "용수명", title: "용수명" },
{ id: "total", title: "계" },
{ id: "1월", title: "1월" ,edit: true },
{ id: "2월", title: "2월" ,edit: true },
{ id: "3월", title: "3월" ,edit: true },
{ id: "4월", title: "4월" ,edit: true },
{ id: "5월", title: "5월" ,edit: true },
{ id: "6월", title: "6월" ,edit: true },
{ id: "7월", title: "7월" ,edit: true },
{ id: "8월", title: "8월" ,edit: true },
{ id: "9월", title: "9월" ,edit: true },
{ id: "10월", title: "10월" ,edit: true },
{ id: "11월", title: "11월" ,edit: true },
{ id: "12월", title: "12월" ,edit: true },
{ id: "1월", title: "1월", edit: true },
{ id: "2월", title: "2월", edit: true },
{ id: "3월", title: "3월", edit: true },
{ id: "4월", title: "4월", edit: true },
{ id: "5월", title: "5월", edit: true },
{ id: "6월", title: "6월", edit: true },
{ id: "7월", title: "7월", edit: true },
{ id: "8월", title: "8월", edit: true },
{ id: "9월", title: "9월", edit: true },
{ id: "10월", title: "10월", edit: true },
{ id: "11월", title: "11월", edit: true },
{ id: "12월", title: "12월", edit: true },
];
+106 -47
View File
@@ -54,7 +54,7 @@ export const rainChkTableData = (chkData: rainChkZodType[]) => {
export const rswtChkTableData = (chkData: rswtChkZodType[]) => {
// 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기
const makeRswtData: any[] = [];
chkData.forEach((root) => {
chkData?.forEach((root) => {
root.rswtrDetails.forEach((detail) => {
let flatRswtChk = {};
const findGuide = root.guideDetails.find(
@@ -174,8 +174,6 @@ export const chRowspanTable = (
);
};
export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean) => {
return headList.forEach((head) => {
if (head.edit !== undefined)
@@ -187,63 +185,124 @@ export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean)
});
}
const handleChange = (
rowIndex: number,
field: any,
value: string | number,
setInputData: (data:any) => void
setInputData: (data: any) => void
) => {
if(Number(value) < 0 ||! Number(value)){
value = 0;
}else{
if (Number(value) < 0 || !Number(value)) {
value = 0;
} else {
value = Number(value);
}
setInputData((prevData:any) =>
prevData?.map((row:any, index: number) => {
if (index === rowIndex) {
return {
...row,
[field ]: value,
};
}
return row;
})
);
setInputData((prevData: any) =>
prevData?.map((row: any, index: number) => {
if (index === rowIndex) {
return {
...row,
[field]: value,
};
}
return row;
})
);
};
;
export const InputTableBody = (
chgRes: any[] | [],
headList: TableUpperProps[],
setInputData: (data: any[]) => void
) => {
// mergeCell 처리용
const getRowSpan = (data: any[], key: string, rowIndex: number) => {
if (rowIndex > 0 && data[rowIndex][key] === data[rowIndex - 1][key]) {
return 0; // 이전과 같으면 셀을 그리지 않음
}
let span = 1;
for (let i = rowIndex + 1; i < data.length; i++) {
if (data[i][key] === data[rowIndex][key]) span++;
else break;
}
return span;
};
export const InputTableBody = (chgRes: any[] | [] ,headList:TableUpperProps[], setInputData: (data: any[]) => void) => {
return (
return (
<>
{chgRes.map((data, dataIndex) => (
<TableRow key={dataIndex + "row"}>
{headList.map((head, headIndex) => (
<TableCell key={head.id + headIndex} className="w-36" >
{head.edit ===true && data.edit !==false?
<Input
className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`}
onChange={(e) =>
handleChange(dataIndex, head.id, e.target.value,setInputData)
}
value={data[head.id as keyof any] ?? ""}
/> : (
<div
className={`
${head.id !== "utztnUsgCd" ? "text-right" : "text-center"}
!text-xl w-full h-10
`}
{chgRes.map((data, dataIndex) => {
// 소계/총합계 행 판별
const isSubTotal = data["용수명"] === "소계";
const isTotal = data["구분"] === 0;
const bgClass = isTotal
? "bg-[#d5e1eb] font-bold"
: isSubTotal
? "bg-[#e6eef5] font-semibold"
: "";
return (
<TableRow key={dataIndex + "row"}>
{headList.map((head, headIndex) => {
// 총합계 행이면 "구분"에서 colSpan=2로 병합
if (
data["구분"] === 0 &&
head.id === "구분"
) {
return (
<TableCell
key={head.id + headIndex}
colSpan={2}
className="font-bold text-center bg-[#d5e1eb]"
>
{data[head.id as keyof any] ?? ""}
</div>
)
총합계
</TableCell>
);
}
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
if (
data["구분"] === 0 &&
head.id === "용수명"
) {
return null;
}
// mergeCell 처리
if (head.mergeCell) {
const rowSpan = getRowSpan(chgRes, head.id, dataIndex);
if (rowSpan === 0) return null;
return (
<TableCell key={head.id + headIndex} rowSpan={rowSpan} className={`w-36 align-middle text-center whitespace-nowrap ${bgClass}`}>
{data[head.id as keyof any] ?? ""}
</TableCell>
);
}
</TableCell>
))}
</TableRow>
))}
// 기본 셀
return head.edit === true && data.edit !== false ? (
<TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}>
<Input
className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`}
onChange={(e) =>
handleChange(dataIndex, head.id, e.target.value, setInputData)
}
value={data[head.id as keyof any] ?? ""}
/>
</TableCell>
) : (
<TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}>
<div
className={`
${head.id !== "utztnUsgCd" ? "text-right" : "text-center"}
!text-xl w-full h-10
`}
>
{data[head.id as keyof any] ?? ""}
</div>
</TableCell>
);
})}
</TableRow>
);
})}
</>
);
}
};
@@ -52,6 +52,7 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) {
useEffect(() => {
if (files) {
console.log("이런")
const file = files[0];
if (form.getValues("ym").length < 1) {
ToastFormUi({
@@ -62,11 +63,12 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) {
return;
}
if (file)
if (file) {
filePicked(file, 0).then((res) => {
if (res !== undefined)
setExcelData(excelToPbnPrfData(res as unknown as JSON[], form.getValues("ym"),form.getValues('pbnstRuseFcltFk') ) as PrfType[])
setExcelData(excelToPbnPrfData(res as unknown as JSON[], form.getValues("ym"), form.getValues('pbnstRuseFcltFk')) as PrfType[])
});
}
}
}, [files]);
@@ -75,82 +77,85 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) {
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<div className="flex justify-between items-center">
<Title title={"하수재이용 실적등록"}></Title>
</div>
<div className="flex justify-between">
<h3 className="text-2xl font-bold text-gray-900 mt-1 ml-7">
{"점검정보 입력"}
</h3>
<Button className="" type="submit">
<Button className="mr-6 mt-10" type="submit">
저장
</Button>
</div>
<h3 className="text-2xl font-bold text-gray-900 my-3 ml-7">
{"점검정보 입력"}
</h3>
<div>
<div className="mx-7">
<FormField
control={form.control}
name="ym"
render={({ field }) => (
<FormUi label="등록년도">
<SelectBox
className="w-1/4 text-3xl"
className="w-1/2 pr-4 text-3xl"
selectClass={""}
label=""
selectArray={yearOptions}
value={field.value||new Date().getFullYear()}
value={field.value || new Date().getFullYear()}
onSelectValue={(val) => field.onChange(val)}
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="bscFctNm"
render={({ field }) => (
<FormUi label="시설명">
<Input
className="w-1/4 text-3xl"
value={field.value||''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="pbnstRuseFcltNm"
render={({ field }) => (
<FormUi label="재이용시설명">
<Input
className="w-1/4 text-3xl"
value={field.value||''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormUi label="점검결과 파일">
<FileUpload
files={files || []}
setFiles={(files: File[] | null) => {
if (files) setFiles(files);
}}
fileTypes="xlsx"
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
<div className="flex gap-4 mt-4">
<div className="flex-1">
<FormField
control={form.control}
name="bscFctNm"
render={({ field }) => (
<FormUi label="시설명">
<Input
className="text-3xl"
value={field.value || ''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
</div>
<div className="flex-1">
<FormField
control={form.control}
name="pbnstRuseFcltNm"
render={({ field }) => (
<FormUi label="재이용시설명">
<Input
className="flex-1 text-3xl"
value={field.value || ''}
onChange={field.onChange}
readOnly
/>
</FormUi>
)}
/>
</div>
</div>
<div className="mt-4">
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormUi label="점검결과 파일">
<FileUpload
files={files || []}
setFiles={(files: File[] | null) => {
if (files) setFiles(files);
}}
fileTypes="xlsx"
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
@@ -3,25 +3,38 @@
import { btnClass } from "@/app/store/common/code"
import type { customDrgt } from "@/app/store/matDmd/drgtSttn"
import { drgtDetailHeadListExport } from "@/app/store/matDmd/drgtSttn/headList"
import { excelDownload } from "@/components/lib/func/commom"
import { Title, UiTable } from "@common/business_components/ui"
import { downloadExcel, Title, UiTable } from "@common/business_components/ui"
import ListDiv from "@common/business_components/ui/list_div"
import { Button } from "@common/components/ui"
import { useCallback, useEffect } from "react"
import { Button, ScrollArea } from "@common/components/ui"
import { useEffect, useState } from "react"
export default function DrgtDetailModal({
data,
}: {
data: customDrgt;
}) {
const excelClick = useCallback(() => {
excelDownload(
[[]],
"우리동네가뭄",
"sample_drgtSttn.xlsx",
"A1:H1"
);
}, []);
const [excelData, setExcelData] = useState<any[]>();
const excelClick = () => {
downloadExcel({
data: excelData ?? [],
fileName: "우리동네가뭄 상세현황",
sheetNameArray: ["Sheet1"],
headerRange: "A1:C1"
})
}
useEffect(() => {
const resData = Array.isArray(data) ? data : [data];
const exData = Array.isArray(resData)
? resData.map((item: customDrgt) => {
return {
"년": item.year,
"월": item.month,
"가뭄상태": item.drgtState,
}
}) : [];
setExcelData(exData)
}, [data])
return (
<>
@@ -43,15 +56,17 @@ export default function DrgtDetailModal({
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
tableData={data}
headlist={drgtDetailHeadListExport}
pageSize={10}
total={10000}
cellClick={(index: number | undefined, headId: string) => { }}
onPageChange={() => { }}
></UiTable>
<ScrollArea className="h-[63vh]">
<UiTable
headName={undefined}
tableData={data}
headlist={drgtDetailHeadListExport}
pageSize={10}
total={10000}
cellClick={(index: number | undefined, headId: string) => { }}
onPageChange={() => { }}
></UiTable>
</ScrollArea>
</ListDiv>
</div>
</div>
@@ -4,9 +4,9 @@ import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn";
import { names } from "@/app/store/matDmd/ldrmntCoSttn/excel";
import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui";
import { Form, FormField } from "@common/business_components/ui/form";
import { Button } from "@common/components/ui";
import { Button, Progress } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import type { z } from "zod";
@@ -30,20 +30,20 @@ export default function CoRegExcel() {
try {
let array: { [key: string]: string | number }[] = []
let isError = false;
if (Array.isArray(res)){
if (Array.isArray(res)) {
res.some((item, index) => {
let object: { [key: string]: string | number } = {}
Object.keys(names).some((key) => {
if (item && names[key] !== undefined) {
if (key === "공장관리번호") {
item[key] = String(item[key])
} else {
object[names[key] as string] = item[key];
}
// if (key === "공장관리번호") {
// item[key] = String(item[key])
// } else {
object[names[key] as string] = item[key];
// }
}
return false;
})
if(isError === true)
if (isError === true)
return true;
array.push(object)
@@ -55,8 +55,8 @@ export default function CoRegExcel() {
return false;
})
}
if(isError === false)
ldrmntRegData(array);
if (isError === false)
ldrmntRegData(array);
} catch (e) {
console.log(e)
alert(e)
@@ -67,7 +67,6 @@ export default function CoRegExcel() {
};
}
return (
<>
<div>
@@ -87,7 +86,6 @@ export default function CoRegExcel() {
</div>
<div>
<FormField
control={form.control}
name="file"
@@ -28,7 +28,7 @@ import { useEffect, useState } from "react";
import { set, useForm } from "react-hook-form";
import { z } from "zod";
export function PbnstSttnModal({ data }: { data: customPbnst }) {
export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titleVisible?: boolean }) {
const { code, pbnstSttn, prfmnc } = useWrStore((state) => state);
const [fcltTab, setFcltTab] = useState<string>("fclt");
const [ruseTab, setRuseTab] = useState<string>("inoutTotal");
@@ -237,10 +237,10 @@ export function PbnstSttnModal({ data }: { data: customPbnst }) {
}, [pbnstSttn.pbnstSttnRes, data]);
useEffect(()=>{
useEffect(() => {
if(data){
form.reset({items:[data]})
if (data) {
form.reset({ items: [data] })
}
},[data, form])
@@ -252,7 +252,11 @@ export function PbnstSttnModal({ data }: { data: customPbnst }) {
return (
<>
<div>
<Title title={"하수재이용 시설 상세정보"} />
{titleVisible && (
<>
<Title title={"하수재이용 시설 상세정보"} />
</>
)}
<ScrollArea className={"h-[70vh]"}>
<PbnstMngForm
form={form}
@@ -16,22 +16,27 @@ import {
TableHeader,
} from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import UiPageNations from "@common/business_components/ui/table/uiPageNations";
export default function RainChkgdTable({
detailUp,
handleClick=() => {},
detailUp,
handleClick = () => { },
deleteData,
rainChkData,
pageNation,
onPageChange,
}: {
detailUp: (data: rainChkZodType[]) => void;
handleClick?: (data:any) => void;
deleteData:(selectedIds:number[])=>void
handleClick?: (data: any) => void;
deleteData: (selectedIds: number[]) => void
rainChkData?: rainChkZodType[];
pageNation: number;
onPageChange: (page: number) => void;
}) {
const { code, rainChkgdn ,rainChkSttn,getRainSttnExcel } = useWrStore((state) => state);
const { code, rainChkgdn, rainChkSttn, getRainSttnExcel } = useWrStore((state) => state);
const [makeTableData, setMakeTableData] = useState<any[]>([]);
const [selectedIds, setSelectedIds] = useState<number[]>([]);
const [upRaintDetail, setUpRaintDetail] = useState<rainChkZodType[]>([]);
const [headList, setHeadList] = useState< {headList:TableUpperProps[],upHeadList:chHeadType[]}>({headList:[], upHeadList:[]});
const [headList, setHeadList] = useState<{ headList: TableUpperProps[], upHeadList: chHeadType[] }>({ headList: [], upHeadList: [] });
const toggleSelection = (pk: number, checked: boolean) => {
@@ -46,29 +51,29 @@ export default function RainChkgdTable({
) => {
setMakeTableData((prev) =>
prev.map((row, idx) => {
return idx === rowIndex ? { ...row, [field]: value } : row
prev.map((row, idx) => {
return idx === rowIndex ? { ...row, [field]: value } : row
}
)
);
};
const handleCellClick= useCallback((index: number, headId: string) => {
const handleCellClick = useCallback((index: number, headId: string) => {
if (
headId === "totalSplyUswtrUtztnQnt" ||
headId === "totalRuseFcltUtztnQnt"
) {
handleClick(makeTableData[index])
}
},[makeTableData, handleClick]);
}, [makeTableData, handleClick]);
useEffect(() => {
setSelectedIds([])// 셀렉트 초기화를 위한 작업
if(rainChkData){
if (rainChkData) {
const rainChg = rainChkData.map((item: any) => {
const bud = tableData([item.building], { code });
return {
@@ -83,10 +88,10 @@ export default function RainChkgdTable({
useEffect(() => {
//데이터를 평탄화 한것중에 체크박스 선택한 테이블만 선택
//현황 페이지에서 필요한 엑셀 데이터를 바뀔때마다 넣기 위해서
getRainSttnExcel(makeTableData);
const selectedData = makeTableData.filter((item) =>selectedIds.includes(item.chkgdnPk));
const selectedData = makeTableData.filter((item) => selectedIds.includes(item.chkgdnPk));
if (selectedData.length > 0) {
const updateTableData = rainChkgdn.rainChk
@@ -101,7 +106,7 @@ export default function RainChkgdTable({
);
if (findRain != undefined) {
console.log(findRain)
console.log(findRain)
return {
...rd,
ph: Number(findRain.ph),
@@ -127,34 +132,34 @@ export default function RainChkgdTable({
}
})
.filter(Boolean);
setUpRaintDetail(updateTableData as rainChkZodType[]);
}
}, [makeTableData,selectedIds]);
}, [makeTableData, selectedIds]);
useEffect(() => {
detailUp(upRaintDetail);
}, [upRaintDetail]);
useEffect(() =>{
if(selectedIds.length >0){
useEffect(() => {
if (selectedIds.length > 0) {
// 삭제를 위한 데이터 삽입
deleteData(selectedIds )
deleteData(selectedIds)
}
},[selectedIds])
}, [selectedIds])
///현황일 경우 input 비활성화 한다.
useEffect(() => {
if(rainChkSttn.isSttn){
useEffect(() => {
if (rainChkSttn.isSttn) {
setHeadList({
headList: rainChkSttn.rainChkgdnHeadList,
upHeadList: rainChkSttn.rainHeadNm
})
}else{
} else {
setHeadList({
headList: rainChkgdn.rainChkgdnHeadList,
upHeadList: rainChkgdn.rainHeadNm
@@ -162,28 +167,36 @@ export default function RainChkgdTable({
}
}, [rainChkSttn.isSttn, rainChkgdn.rainChkgdnHeadList , rainChkSttn.rainChkgdnHeadList]);
}, [rainChkSttn.isSttn, rainChkgdn.rainChkgdnHeadList, rainChkSttn.rainChkgdnHeadList]);
return (
<Table variant="secondary">
<TableHeader className="font-bold">
{headMakeprfmnc(
headList?.headList,
headList?.upHeadList
)}
</TableHeader>
<>
<Table variant="secondary">
<TableHeader className="font-bold">
{headMakeprfmnc(
headList?.headList,
headList?.upHeadList
)}
</TableHeader>
<TableBody>
{chRowspanTable(
makeTableData,
headList.headList,
handleChange,
toggleSelection,
selectedIds,
handleCellClick
)}
</TableBody>
</Table>
<TableBody>
{chRowspanTable(
makeTableData,
headList.headList,
handleChange,
toggleSelection,
selectedIds,
handleCellClick
)}
</TableBody>
</Table>
<UiPageNations
total={rainChkData?.length ?? 0}
cur={pageNation}
pageSize={20}
onPageChange={onPageChange}
/>
</>
);
}
@@ -14,22 +14,27 @@ import {
} from "@/components/lib/func/TableData";
import { Table, TableBody, TableHeader } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import UiPageNations from "@common/business_components/ui/table/uiPageNations";
export default function RswtChkgdTable({
detaiRswtlUpdate,
handleRswtClick = () => {},
handleRswtClick = () => { },
deleteRswtData,
rswtChkData,
pageNation,
onPageChange,
}: {
detaiRswtlUpdate: (data: rswtrChkDetailsZodType[]) => void;
handleRswtClick?: (data: any) => void;
deleteRswtData: (selectedIds: number[]) => void;
rswtChkData: rswtChkZodType[];
pageNation: number;
onPageChange: (page: number) => void;
}) {
const { code, rswtChkgdn,rswtChkSttn , rswtChkgdnInit, rswtChkPolAndGuide,getRswtSttnExcel } = useWrStore(
const { code, rswtChkgdn, rswtChkSttn, rswtChkgdnInit, rswtChkPolAndGuide, getRswtSttnExcel } = useWrStore(
(state) => state
);
const [headList, setHeadList] = useState< {headList:TableUpperProps[],upHeadList:chHeadType[]}>({headList:[], upHeadList:[]});
const [headList, setHeadList] = useState<{ headList: TableUpperProps[], upHeadList: chHeadType[] }>({ headList: [], upHeadList: [] });
const [makeTableData, setMakeTableData] = useState<any[]>([]);
const [selectedIds, setSelectedIds] = useState<number[]>([]);
@@ -111,22 +116,20 @@ export default function RswtChkgdTable({
}, [selectedIds]);
///현황일 경우 input 비활성화 한다.
useEffect(() => {
if(rswtChkSttn.isSttn){
useEffect(() => {
if (rswtChkSttn.isSttn) {
setHeadList({
headList: rswtChkSttn.rswtChkSttnHeadList,
upHeadList: rswtChkSttn.rswtSttnHeadNm
})
}else{
} else {
setHeadList({
headList: rswtChkgdn.rswtChkHeadList,
upHeadList: rswtChkgdn.rswtHeadNm
})
}
}, [rswtChkSttn.isSttn, rswtChkgdn.rswtChkHeadList , rswtChkSttn.rswtChkSttnHeadList]);
}, [rswtChkSttn.isSttn, rswtChkgdn.rswtChkHeadList, rswtChkSttn.rswtChkSttnHeadList]);
return (
<>
@@ -146,6 +149,12 @@ export default function RswtChkgdTable({
)}
</TableBody>
</Table>
<UiPageNations
total={rswtChkData?.length ?? 0}
cur={pageNation}
pageSize={20}
onPageChange={onPageChange}
/>
</>
);
}
+21 -1
View File
@@ -5,6 +5,9 @@ import { CS_URL } from "./components/lib/config";
const CLAIM_NAME = "Authorization";
const AUTH_USER_ID = "token-id";
const serverApiUrl = CS_URL;
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
export function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname;
console.log(`[Middleware] Request URL: ${req.url}`);
@@ -44,9 +47,26 @@ export function middleware(req: NextRequest) {
});
}
if (pathname.startsWith("/geoserver")) {
const queryParams = searchParams.toString();
const newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
if (pathname.startsWith("/sewerage-service")) {
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));
}
return NextResponse.next();
}
export const config = {
matcher: ["/server/:path*", "/attach-service/:path*"],
matcher: ["/server/:path*", "/attach-service/:path*", "/geoserver/:path*", "/sewerage-service/:path*"],
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More