Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend
@@ -52,9 +52,9 @@ const LinkForm = ({ form, linkSn, updateLinkInfo, handleInsertLink, deleteLink,
|
||||
if (await confirm(confirmMsg)) {
|
||||
if (linkSn) {
|
||||
updateLinkInfo?.(data);
|
||||
} else {
|
||||
} else {
|
||||
handleInsertLink?.(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ function LinkParamsPage() {
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const dbIdList = ['link0001', 'link0002'];
|
||||
const dbIdList = ['link0001', 'link0002', 'link0007'];
|
||||
|
||||
// Link 목록 쿼리
|
||||
const query = useQuery({
|
||||
|
||||
@@ -7,7 +7,7 @@ import ListDiv from '@common/business_components/ui/list_div';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import SdmLogin from '../sdmLogin/page';
|
||||
import type { DemandCompType } from '@/app/store/sdmMng';
|
||||
import type { DemandCompBizType } from '@/app/store/sdmMng';
|
||||
import TransValueFormat from '@/app/common/transValueFormat';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
@@ -31,8 +31,8 @@ export default function SdmApplyList({}: SdmApplyProps) {
|
||||
const {selectSidoData, selectSigunData} = Common;
|
||||
const [isLogin, setIsLogin] = useState(false);
|
||||
const [isLoginStorageInfo, setIsLoginStorageInfo] = useState(false);
|
||||
const [sdmApplyDetail, setSdmApplyDetail] = useState<DemandCompType>({} as DemandCompType);
|
||||
const [transformedList, setTransformedList] = useState<DemandCompType[]>([]);
|
||||
const [sdmApplyDetail, setSdmApplyDetail] = useState<DemandCompBizType>({} as DemandCompBizType);
|
||||
const [transformedList, setTransformedList] = useState<DemandCompBizType[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('sessionStorage : ',sessionStorage.getItem('user'));
|
||||
@@ -60,10 +60,10 @@ export default function SdmApplyList({}: SdmApplyProps) {
|
||||
// 데이터 변환을 위한 별도의 useEffect
|
||||
useEffect(() => {
|
||||
if (sdmInfoStatusList.length > 0) {
|
||||
const newTransformedList = sdmInfoStatusList.map((item: DemandCompType) => ({
|
||||
const newTransformedList = sdmInfoStatusList.map((item: DemandCompBizType) => ({
|
||||
...item,
|
||||
ctpvCd: selectSidoData.find(sido => sido.val === item.ctpvCd?.trim())?.text || '',
|
||||
sggCd: selectSigunData.find(sigun => sigun.val === item.sggCd?.trim())?.text || ''
|
||||
sggCd: selectSigunData.find(sigun => sigun.val === item.sggCd?.trim())?.text || '',
|
||||
}));
|
||||
setTransformedList(newTransformedList);
|
||||
}
|
||||
@@ -73,6 +73,8 @@ export default function SdmApplyList({}: SdmApplyProps) {
|
||||
if (index !== undefined) {
|
||||
const selectedStatus = transformedList[index];
|
||||
if (selectedStatus) {
|
||||
|
||||
|
||||
setSdmApplyDetail(selectedStatus);
|
||||
}
|
||||
}
|
||||
@@ -121,33 +123,33 @@ export default function SdmApplyList({}: SdmApplyProps) {
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>총대장균군수(개/100ml)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.grssClfg} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{ sdmApplyDetail?.grssClfg!=-1?<TransValueFormat value={sdmApplyDetail?.grssClfg ?? ''} format="number"/>:''} {sdmApplyDetail?.grssClfg?sdmApplyDetail.grssClfg!=-1? '이하':'불검출':'-'} </TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>결합잔류염소(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.combResdch} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.combResdch ?? ''} format="number"/>} {sdmApplyDetail?.combResdch?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>탁도(NTU)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.trbt} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.trbt ?? ''} format="number"/>} {sdmApplyDetail?.trbt?'이하':'-'}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>생물학적 산소요구량(BOD)(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.blgcOxygDmndQnt} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.blgcOxygDmndQnt ?? ''} format="number"/>} {sdmApplyDetail?.blgcOxygDmndQnt?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>냄새</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.smll} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.smll} format="number"/>} {sdmApplyDetail?.smll?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>색도(도)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.crmty} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.crmty ?? ''} format="number"/>} {sdmApplyDetail?.crmty?'이하':'-'}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>총질소(T-N)(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tn} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tn ?? ''} format="number"/>} {sdmApplyDetail?.tn?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>총인(T-P)(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tph} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tph ?? ''} format="number"/>} {sdmApplyDetail?.tph?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>수소이온농도(pH)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.ph} format="number"/>}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>염화물(mgCL/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.clIonQnt} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.clIonQnt ?? ''} format="number"/>} {sdmApplyDetail?.clIonQnt?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>전기전도도(㎲/cm)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.cdtnv} format="number"/>}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.cdtnv ?? ''} format="number"/>} {sdmApplyDetail?.cdtnv?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>비고</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.rmrk}</TableCell>
|
||||
</TableRow>
|
||||
@@ -156,11 +158,11 @@ export default function SdmApplyList({}: SdmApplyProps) {
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지1</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}></TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{ sdmApplyDetail?.splyCndtList?.[0]?.ruseUtztnFcltNm ?? ""}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지2</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}></TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.splyCndtList?.[1]?.ruseUtztnFcltNm ?? ""}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지3</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}></TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.splyCndtList?.[2]?.ruseUtztnFcltNm ?? ""}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
@@ -177,7 +179,7 @@ export default function SdmApplyList({}: SdmApplyProps) {
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>진행상태</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{sdmApplyDetail?.status?.toString()}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>공급처매칭결과</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}></TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{sdmApplyDetail?.ruseUtztnFcltNm??''}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useDmStore } from "@/app/store";
|
||||
import type { DemandCompType } from "@/app/store/sdmMng";
|
||||
import { sdmInfoFormSchema, type DemandCompBizType, type SdmInfoFormData } from "@/app/store/sdmMng";
|
||||
import { SdmInfoRegForm } from "@/components/ui/sdmInfoInsertForm/component";
|
||||
import { UiModal } from "@common/business_components/ui/dialog_ui";
|
||||
import { Button } from "@common/components/ui";
|
||||
@@ -9,229 +9,207 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { FormProvider, useForm, type SubmitHandler } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import * as z from "zod";
|
||||
import toast, { Toaster } from "react-hot-toast";
|
||||
import SdmLogin from "../sdmLogin/page";
|
||||
|
||||
const sdmInfoFormSchema = z.object({
|
||||
sdmNm: z.string()
|
||||
.min(1, "수요처를 선택해주세요"),
|
||||
demandYmd: z.date().optional(),
|
||||
demandQnty: z.string()
|
||||
.min(1, "요구수량을 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "요구수량은 숫자만 입력 가능합니다"),
|
||||
usePurpose: z.string().optional(),
|
||||
money: z.string()
|
||||
.regex(/^[0-9]+$/, "희망단가는 숫자만 입력 가능합니다"),
|
||||
/**요구수질 */
|
||||
reqWaterQlty: z.string().optional(),
|
||||
grssClfg: z.string()
|
||||
.min(1, "총대장균군수를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "총대장균군수는 숫자만 입력 가능합니다"),
|
||||
tn: z.string()
|
||||
.min(1, "총질소를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "총질소는 숫자만 입력 가능합니다"),
|
||||
combResdch: z.string()
|
||||
.min(1, "결합잔류염소를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "결합잔류염소는 숫자만 입력 가능합니다"),
|
||||
tph: z.string()
|
||||
.min(1, "총인산물을 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "총인산물은 숫자만 입력 가능합니다"),
|
||||
trbt: z.string()
|
||||
.min(1, "탁도를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "탁도는 숫자만 입력 가능합니다"),
|
||||
ph: z.string()
|
||||
.min(1, "수소이온농도를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "수소이온농도는 숫자만 입력 가능합니다"),
|
||||
bod: z.string()
|
||||
.min(1, "생물학적 산소요구량을 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "생물학적 산소요구량은 숫자만 입력 가능합니다"),
|
||||
/**컬럼확인필요 */
|
||||
clIonQnt: z.string()
|
||||
.min(1, "염화물을 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "염화물은 숫자만 입력 가능합니다"),
|
||||
smll: z.string()
|
||||
.min(1, "냄새를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "냄새는 숫자만 입력 가능합니다"),
|
||||
cdtnv: z.string()
|
||||
.min(1, "전기전도도를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "전기전도도는 숫자만 입력 가능합니다"),
|
||||
crmty: z.string()
|
||||
.min(1, "색도를 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "색도는 숫자만 입력 가능합니다"),
|
||||
rmrk: z.string().optional(),
|
||||
/* telno: z.string()
|
||||
.min(8, "전화번호는 8자 이상 입력해주세요")
|
||||
.max(12, "전화번호는 12자 이내로 입력해주세요")
|
||||
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"), */
|
||||
candidateTitle: z.array(z.object({
|
||||
ruseUtztnFcltNm: z.string(),
|
||||
ruseUtztnFcltCd: z.string(),
|
||||
})).optional(),
|
||||
});
|
||||
|
||||
export type FormData = z.infer<typeof sdmInfoFormSchema>;
|
||||
|
||||
function SdmInfoInsertContent() {
|
||||
const searchParams = useSearchParams();
|
||||
const {insertSdmInfoData, Sdm ,setSubsInfo} = useDmStore((state) => state);
|
||||
const { insertSdmInfoData, Sdm, setSubsInfo } = useDmStore((state) => state);
|
||||
const { successLogin } = Sdm;
|
||||
const [ismoneyDisabled, setIsmoneyDisabled] = useState(false);
|
||||
const formSchema = sdmInfoFormSchema;
|
||||
|
||||
const [isLogin, setIsLogin] = useState(false);
|
||||
const [facilityName, setFacilityName] = useState<string>('');
|
||||
const [defaultValues, setDefaultValues] = useState<FormData>({
|
||||
sdmNm: '',
|
||||
const [facilityName, setFacilityName] = useState<string>("");
|
||||
const [defaultValues, setDefaultValues] = useState<SdmInfoFormData>({
|
||||
sdmNm: "",
|
||||
demandYmd: new Date(),
|
||||
demandQnty: '',
|
||||
usePurpose: '',
|
||||
money: '',
|
||||
demandQnty: "",
|
||||
usePurpose: "",
|
||||
money: "",
|
||||
/**요구수질 */
|
||||
reqWaterQlty: '',
|
||||
grssClfg: '',
|
||||
tn: '',
|
||||
combResdch: '',
|
||||
tph: '',
|
||||
trbt: '',
|
||||
ph: '',
|
||||
bod: '',
|
||||
reqWaterQlty: "",
|
||||
grssClfg: null,
|
||||
tn: null,
|
||||
combResdch: null,
|
||||
tph: null,
|
||||
trbt: null,
|
||||
ph: null,
|
||||
bod: null,
|
||||
/**컬럼확인필요 */
|
||||
clIonQnt: '',
|
||||
smll: '',
|
||||
cdtnv: '',
|
||||
crmty: '',
|
||||
rmrk: '',
|
||||
clIonQnt: null,
|
||||
smll: "",
|
||||
cdtnv: null,
|
||||
crmty: null,
|
||||
rmrk: "",
|
||||
/** 후보지 제목 */
|
||||
candidateTitle: [
|
||||
{ ruseUtztnFcltNm: '', ruseUtztnFcltCd: '' },
|
||||
{ ruseUtztnFcltNm: '', ruseUtztnFcltCd: '' },
|
||||
{ ruseUtztnFcltNm: '', ruseUtztnFcltCd: '' },
|
||||
{ ruseUtztnFcltNm: "", ruseUtztnFcltCd: "" },
|
||||
{ ruseUtztnFcltNm: "", ruseUtztnFcltCd: "" },
|
||||
{ ruseUtztnFcltNm: "", ruseUtztnFcltCd: "" },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const user = sessionStorage.getItem('user');
|
||||
setDefaultValues(prev => ({
|
||||
const user = sessionStorage.getItem("user");
|
||||
setDefaultValues((prev) => ({
|
||||
...prev,
|
||||
sdmNm: searchParams.get('dmdCoNm') || '',
|
||||
sdmNm: searchParams.get("dmdCoNm") || "",
|
||||
}));
|
||||
sessionStorage.setItem('user', searchParams.get('dmdInfoId') || '');
|
||||
setIsLogin(user || searchParams.get('dmdInfoId') ? true : false);
|
||||
sessionStorage.setItem("user", searchParams.get("dmdInfoId") || "");
|
||||
setIsLogin(user || searchParams.get("dmdInfoId") ? true : false);
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const local = localStorage.getItem('user');
|
||||
const local = localStorage.getItem("user");
|
||||
if (local) {
|
||||
setIsLogin(true);
|
||||
}
|
||||
setSubsInfo([]); // subsInfo 초기화 후보지 처음 화면 들어올때 초기화(스토어내 객체만)
|
||||
|
||||
}, [setSubsInfo]);
|
||||
|
||||
const form = useForm<FormData>({
|
||||
const form = useForm<SdmInfoFormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues,
|
||||
mode: "onChange",
|
||||
mode: "onSubmit",
|
||||
});
|
||||
|
||||
const subsInfo = useDmStore((state) => state.map.subsInfo);
|
||||
|
||||
useEffect(()=>{
|
||||
useEffect(() => {
|
||||
//map.subsInfo 스토어 객체 값이 변경되면 해당 값이 폼에 자동으로 들어가도록 수정 대신 값이 있어야함
|
||||
if(subsInfo.length>0){
|
||||
form.setValue('candidateTitle', subsInfo)
|
||||
if (subsInfo.length > 0) {
|
||||
form.setValue("candidateTitle", subsInfo);
|
||||
}
|
||||
}, [subsInfo, form]);
|
||||
|
||||
},[subsInfo, form])
|
||||
//후보지 팝업 값 셋팅
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data?.type === "SET_SUBS_INFO") {
|
||||
setSubsInfo(event.data.payload); // zustand로 값 설정
|
||||
}
|
||||
};
|
||||
|
||||
//후보지 팝업 값 셋팅
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data?.type === 'SET_SUBS_INFO') {
|
||||
setSubsInfo(event.data.payload); // zustand로 값 설정
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleMessage);
|
||||
return () => window.removeEventListener('message', handleMessage);
|
||||
}, [setSubsInfo]);
|
||||
window.addEventListener("message", handleMessage);
|
||||
return () => window.removeEventListener("message", handleMessage);
|
||||
}, [setSubsInfo]);
|
||||
|
||||
// 로그인 성공 시 모달 닫기 및 폼 업데이트
|
||||
useEffect(() => {
|
||||
if (successLogin) {
|
||||
setIsLogin(true);
|
||||
const user = sessionStorage.getItem('user');
|
||||
const user = sessionStorage.getItem("user");
|
||||
if (user) {
|
||||
try {
|
||||
const userData = JSON.parse(user);
|
||||
if (userData.dmdCoNm) {
|
||||
setFacilityName(userData.dmdCoNm);
|
||||
form.setValue('sdmNm', userData.dmdCoNm);
|
||||
form.setValue("sdmNm", userData.dmdCoNm);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error parsing user data:', error);
|
||||
console.error("Error parsing user data:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [successLogin, form]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Form state changed:", form.formState.errors);
|
||||
}, [form.formState.errors]);
|
||||
|
||||
// 체크박스 상태가 변경될 때마다 money 필드의 에러를 초기화
|
||||
useEffect(() => {
|
||||
if (ismoneyDisabled) {
|
||||
form.clearErrors('money');
|
||||
form.setValue('money', '');
|
||||
form.clearErrors("money");
|
||||
form.setValue("money", "");
|
||||
}
|
||||
}, [ismoneyDisabled, form]);
|
||||
const router = useRouter();
|
||||
const onSubmit: SubmitHandler<FormData> = async (data) => {
|
||||
const router = useRouter();
|
||||
const onSubmit: SubmitHandler<SdmInfoFormData> = async (data) => {
|
||||
try {
|
||||
// Transform form data to match API schema
|
||||
const apiData: DemandCompType = {
|
||||
dmdInfoId: JSON.parse(sessionStorage.getItem('user') || '{}')?.dmdInfoId?.toString() || searchParams.get('dmdInfoId') + (searchParams.get('bznmTotNo') || ''),
|
||||
dmdInfoFk: JSON.parse(sessionStorage.getItem('user') || '{}')?.dmdInfoId?.toString() || searchParams.get('dmdInfoId') + (searchParams.get('bznmTotNo') || ''),
|
||||
dmdCoNm: JSON.parse(sessionStorage.getItem('user') || '{}')?.dmdCoNm?.toString() || data.sdmNm,
|
||||
ctpvCd: JSON.parse(sessionStorage.getItem('user') || '{}')?.ctpvCd?.toString() || searchParams.get('ctpvCd') || '',
|
||||
sggCd: JSON.parse(sessionStorage.getItem('user') || '{}')?.sggCd?.toString() || searchParams.get('sggCd') || '',
|
||||
daddr: JSON.parse(sessionStorage.getItem('user') || '{}')?.daddr?.toString() || searchParams.get('daddr') || '',
|
||||
telno: JSON.parse(sessionStorage.getItem('user') || '{}')?.telno?.toString() || searchParams.get('telno') || '',
|
||||
rgtr: JSON.parse(sessionStorage.getItem('user') || '{}')?.rgtr?.toString() || searchParams.get('rgtr') || '',
|
||||
useUsgCd: data.usePurpose || '',
|
||||
wntUntprc: Number(data.money) || '',
|
||||
const dmSubsInfo = data.candidateTitle?.filter(
|
||||
(item) => item.ruseUtztnFcltNm && item.ruseUtztnFcltCd
|
||||
);
|
||||
// Transform form data to madata.candidateTitletch API schema
|
||||
const apiData: DemandCompBizType = {
|
||||
dmdInfoId:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.dmdInfoId?.toString() ||
|
||||
searchParams.get("dmdInfoId") + (searchParams.get("bznmTotNo") || ""),
|
||||
dmdInfoFk:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.dmdInfoId?.toString() ||
|
||||
searchParams.get("dmdInfoId") + (searchParams.get("bznmTotNo") || ""),
|
||||
dmdCoNm:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.dmdCoNm?.toString() || data.sdmNm,
|
||||
ctpvCd:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.ctpvCd?.toString() ||
|
||||
searchParams.get("ctpvCd") ||
|
||||
"",
|
||||
sggCd:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.sggCd?.toString() ||
|
||||
searchParams.get("sggCd") ||
|
||||
"",
|
||||
daddr:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.daddr?.toString() ||
|
||||
searchParams.get("daddr") ||
|
||||
"",
|
||||
telno:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.telno?.toString() ||
|
||||
searchParams.get("telno") ||
|
||||
"",
|
||||
rgtr:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.rgtr?.toString() ||
|
||||
searchParams.get("rgtr") ||
|
||||
"",
|
||||
useUsgCd: data.usePurpose || "",
|
||||
wntUntprc: Number(data.money) || "",
|
||||
dmndTmng: data.demandYmd || new Date(),
|
||||
dmndQty: Number(data.demandQnty) || '',
|
||||
grssClfg: Number(data.grssClfg) || '',
|
||||
tn: Number(data.tn) || '',
|
||||
combResdch: Number(data.combResdch) || '',
|
||||
tph: Number(data.tph) || '',
|
||||
trbt: Number(data.trbt) || '',
|
||||
ph: Number(data.ph) || '',
|
||||
blgcOxygDmndQnt: Number(data.bod) || '',
|
||||
clIonQnt: Number(data.clIonQnt) || '',
|
||||
smll: Number(data.smll) || '',
|
||||
cdtnv: Number(data.cdtnv) || '',
|
||||
crmty: Number(data.crmty) || '',
|
||||
rmrk: data.rmrk || '',
|
||||
dmndQty: Number(data.demandQnty) || "",
|
||||
grssClfg: data.grssClfg === null ? -1 : Number(data.grssClfg),
|
||||
tn: Number(data.tn) || null,
|
||||
combResdch: Number(data.combResdch) || null,
|
||||
tph: Number(data.tph) || null,
|
||||
trbt: Number(data.trbt) || null,
|
||||
ph: data.ph || "",
|
||||
blgcOxygDmndQnt: Number(data.bod) ||null,
|
||||
clIonQnt: Number(data.clIonQnt) || null,
|
||||
smll: data.smll || "",
|
||||
cdtnv: Number(data.cdtnv) ||null,
|
||||
crmty: Number(data.crmty) || null,
|
||||
rmrk: data.rmrk || "",
|
||||
bzmnNo: "",
|
||||
dmdAplyPswd: "",
|
||||
index: 0,
|
||||
reqDt: "",
|
||||
dmdInfoSn: 0,
|
||||
status: '',
|
||||
splyCndtList: data.candidateTitle || [],
|
||||
status: "",
|
||||
splyCndtList: dmSubsInfo || [],
|
||||
};
|
||||
|
||||
|
||||
|
||||
await insertSdmInfoData(apiData);
|
||||
setTimeout(() => {
|
||||
router.push("/");
|
||||
}, 1000);
|
||||
}, 1000); // 0.8초 후 이동
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
console.error("Error submitting form:", error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -254,8 +232,10 @@ const router = useRouter();
|
||||
/>
|
||||
<div className="flex justify-center pt-4">
|
||||
<Button
|
||||
color="sky"
|
||||
size={"sm"}
|
||||
type="submit"
|
||||
className="w-40 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-md transition-colors duration-200"
|
||||
className="w-40 font-semibold py-2 px-4 rounded-md "
|
||||
>
|
||||
수요정보 등록
|
||||
</Button>
|
||||
@@ -265,20 +245,21 @@ const router = useRouter();
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isLogin ?
|
||||
{!isLogin ? (
|
||||
<UiModal
|
||||
body={<SdmLogin />}
|
||||
bodyClass="sm:max-w-[90vw] w-[530px] h-[430px]"
|
||||
open={!isLogin}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
toast.dismiss();
|
||||
router.push("/");
|
||||
}
|
||||
}}
|
||||
noHeaderYn="Y"
|
||||
noFooterYn="Y"
|
||||
/>
|
||||
: null}
|
||||
) : null}
|
||||
<Toaster position="top-center" />
|
||||
</div>
|
||||
);
|
||||
@@ -291,4 +272,3 @@ export default function MyForm() {
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useDmStore } from "@/app/store";
|
||||
import type { DemandCompType } from "@/app/store/sdmMng";
|
||||
import SdmInsertForm from '@common/business_components/ui/sdmInsertForm';
|
||||
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
@@ -44,15 +44,9 @@ export default function MyForm() {
|
||||
const handleInsertSdmData = useCallback((data: import('@common/business_components/ui/sdmInsertForm/form').DemandCompType) => {
|
||||
const convertedData: DemandCompType = {
|
||||
...data,
|
||||
dmdInfoSn: '',
|
||||
dmdInfoFk: '',
|
||||
status: '',
|
||||
splyCndtList: []
|
||||
};
|
||||
formDataRef.current = convertedData;
|
||||
insertSdmData(convertedData).catch(error => {
|
||||
console.error('Error inserting SDM data:', error);
|
||||
});
|
||||
insertSdmData(convertedData)
|
||||
}, [insertSdmData]);
|
||||
|
||||
return (
|
||||
@@ -70,4 +64,3 @@ export default function MyForm() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ export default function WrAboutPage() {
|
||||
물재이용 필요성
|
||||
</h2>
|
||||
<section aria-labelledby="card2-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card2-desc" className="font-NotoSansKR text-[#ED5534] text-5xl font-semibold mb-8">물 재이용, 왜 필요한가요?</h1>
|
||||
<h1 id="card2-desc" className="font-NotoSansKR text-[#D63F1F] text-5xl font-semibold mb-8">물 재이용, 왜 필요한가요?</h1>
|
||||
<p className="font-NotoSansKR text-2xl font-normal mb-10 leading-relaxed">
|
||||
빗물, 중수, 하수처리수는 지속적으로 이용 가능한 순환자원입니다.<br />
|
||||
물 재이용을 통한 효율적인 수자원 활용은 물 스트레스국가인 우리나라에서<br />가뭄대응능력 향상 및 하천수질보전을 위해 반드시 필요한 일입니다.
|
||||
@@ -237,7 +237,7 @@ export default function WrAboutPage() {
|
||||
하수 재이용
|
||||
</h2>
|
||||
<section aria-labelledby="card5-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card5-desc" className="font-NotoSansKR leading-snug text-[#0099A4] text-5xl font-bold mb-6">아직 할 일이 많은 하수처리수,<br />풍족하게 재이용할 수 있어요!</h1>
|
||||
<h1 id="card5-desc" className="font-NotoSansKR leading-snug text-[#00818A] text-5xl font-bold mb-6">아직 할 일이 많은 하수처리수,<br />풍족하게 재이용할 수 있어요!</h1>
|
||||
<p className="font-NotoSansKR text-2xl mb-10 leading-relaxed">사용하고 버려져 공공하수처리장으로 모여든 물은<br />용도에 맞는 처리 후 재이용 할 수 있습니다.</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_05_1.png"
|
||||
|
||||
@@ -62,10 +62,9 @@ const Map = () => {
|
||||
Object.keys(item).length > 0 &&
|
||||
!prev.some((f) => f.pbnstRuseFcltId === item.pbnstRuseFcltId)
|
||||
);
|
||||
console.log(newItems)
|
||||
console.log(newItems);
|
||||
return [...prev, ...newItems];
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("데이터 불러오기 실패:", error);
|
||||
}
|
||||
@@ -232,16 +231,9 @@ const Map = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchBar
|
||||
setHasuPk={(hasuPk: any) => {
|
||||
if (hasuPk) {
|
||||
setSelectDataPk(hasuPk);
|
||||
}
|
||||
}}
|
||||
onApply={onApply}
|
||||
/>
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
height='h-screen'
|
||||
treeData={treeData}
|
||||
isSearch={false}
|
||||
gisMaps={gisMaps}
|
||||
@@ -257,6 +249,18 @@ const Map = () => {
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={onCheckedLayersChange}
|
||||
imgVisble={false}
|
||||
searchBar={(props: any) => (
|
||||
<SearchBar
|
||||
setHasuPk={(hasuPk: any) => {
|
||||
if (hasuPk) {
|
||||
setSelectDataPk(hasuPk);
|
||||
}
|
||||
}}
|
||||
onApply={onApply}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
waterDepth={undefined}
|
||||
/>
|
||||
</div>
|
||||
<SelectedFcltList
|
||||
|
||||
@@ -8,6 +8,9 @@ export default function RootLayout({
|
||||
}>) {
|
||||
return (
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<title>물재이용 대민</title>
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
</body>
|
||||
|
||||
@@ -2,12 +2,108 @@
|
||||
import type { SelectDataType } from "@common/business_components/ui";
|
||||
import type { TableUpperProps } from "../common";
|
||||
import toast from "react-hot-toast";
|
||||
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
||||
import { z } from "zod";
|
||||
|
||||
/**====================================
|
||||
* Store - 타입
|
||||
=====================================*/
|
||||
|
||||
export const sdmInfoFormSchema = z.object({
|
||||
sdmNm: z.string().min(1, "수요처를 선택해주세요"),
|
||||
demandYmd: z.date().optional(),
|
||||
demandQnty: z
|
||||
.string()
|
||||
.min(1, "요구수량을 입력해주세요")
|
||||
.regex(/^[0-9]+$/, "요구수량은 숫자만 입력 가능합니다"),
|
||||
usePurpose: z.string().optional(),
|
||||
money: z.string().regex(/^[0-9]+$/, "희망단가는 숫자만 입력 가능합니다"),
|
||||
/**요구수질 */
|
||||
reqWaterQlty: z.string().optional(),
|
||||
grssClfg: z.number().nullish(),
|
||||
tn: z.number().nullish(),
|
||||
combResdch: z.number().nullish(),
|
||||
tph: z.number().nullish(),
|
||||
trbt: z.number().nullish(),
|
||||
ph: z.string().nullish(),
|
||||
bod: z.number().nullish(),
|
||||
/**컬럼확인필요 */
|
||||
clIonQnt: z.number().nullish(),
|
||||
smll: z.string(),
|
||||
cdtnv: z.number().nullish(),
|
||||
crmty: z.number().nullish(),
|
||||
rmrk: z.string().optional(),
|
||||
/* telno: z.string()
|
||||
.min(8, "전화번호는 8자 이상 입력해주세요")
|
||||
.max(12, "전화번호는 12자 이내로 입력해주세요")
|
||||
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"), */
|
||||
candidateTitle: z
|
||||
.array(
|
||||
z.object({
|
||||
ruseUtztnFcltNm: z.string(),
|
||||
ruseUtztnFcltCd: z.string(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
}).superRefine((data, ctx) => {
|
||||
const disabledFields = disableRules[data.usePurpose ?? ""] || [];
|
||||
// 모든 수질 관련 필드
|
||||
// 총대장균군수는 null 값을 불검출로 표시하기때문에 해당 검사에서 제외함.
|
||||
const allFields: (keyof typeof data)[] = [
|
||||
"tn", "combResdch", "tph",
|
||||
"trbt", "ph", "bod", "clIonQnt",
|
||||
"smll", "cdtnv", "crmty",
|
||||
];
|
||||
|
||||
const allFieldsName: Record<string, string> = {
|
||||
tn: "총질소(T-N)(mg/L)",
|
||||
combResdch: "결합잔류염소(mg/L)",
|
||||
tph: "총인(T-P)(mg/L)",
|
||||
trbt: "탁도(NTU)",
|
||||
ph: "수소이온농도(pH)",
|
||||
bod: "생물학적 산소요구량(BOD)(mg/L)",
|
||||
clIonQnt: "염화물(mgCL/L)",
|
||||
smll: "냄새",
|
||||
cdtnv: "전기전도도(㎲/cm)",
|
||||
crmty: "색도(도)",
|
||||
};
|
||||
allFields.forEach((field) => {
|
||||
const isDisabled = disabledFields.includes(field);
|
||||
if (!isDisabled) {
|
||||
// disabled가 아닌 필드는 반드시 값이 있어야 함
|
||||
const value = data[field];
|
||||
if (typeof value === "string") {
|
||||
if (value.trim().length === 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: [field],
|
||||
message: `${allFieldsName[field]} 을 입력해주세요.`,
|
||||
});
|
||||
}
|
||||
} else if (typeof value === "number") {
|
||||
if (value <= 0 || Number.isNaN(value)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: [field],
|
||||
message: `${allFieldsName[field]} 을 입력해주세요.`,
|
||||
});
|
||||
}
|
||||
} else if (value == null) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: [field],
|
||||
message: `${allFieldsName[field]} 을 입력해주세요.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export type SdmInfoFormData = z.infer<typeof sdmInfoFormSchema>;
|
||||
|
||||
|
||||
// 수요처 정보
|
||||
export type DemandCompType = {
|
||||
export type DemandCompBizType = {
|
||||
index : number | '';
|
||||
dmdInfoId : string | '';
|
||||
dmdInfoFk : string | '';
|
||||
@@ -23,22 +119,23 @@ export type DemandCompType = {
|
||||
wntUntprc : number | '';
|
||||
dmndTmng : Date | '';
|
||||
dmndQty : number | '';
|
||||
grssClfg : number | '';
|
||||
tn : number | '';
|
||||
combResdch : number | '';
|
||||
tph : number | '';
|
||||
trbt : number | '';
|
||||
ph : number | '';
|
||||
blgcOxygDmndQnt : number | '';
|
||||
clIonQnt : number | '';
|
||||
smll : number | '';
|
||||
cdtnv : number | '';
|
||||
crmty : number | '';
|
||||
grssClfg : number | null //총대장균군수
|
||||
tn : number | null //총질소
|
||||
combResdch : number | null //결합잔류염소
|
||||
tph : number | null //총인
|
||||
trbt : number | null //탁도
|
||||
ph : string | ''; //수소이온농
|
||||
blgcOxygDmndQnt : number | null //생물학적산소요구량
|
||||
clIonQnt : number | null //염화물
|
||||
smll : string | ''; //냄새
|
||||
cdtnv : number | null; //전기전도도
|
||||
crmty : number | null //색도
|
||||
rmrk : string | '';
|
||||
reqDt : string | '';
|
||||
dmdInfoSn : number | '';
|
||||
status : string | '';
|
||||
splyCndtList:SupplyCandidate[]|[],
|
||||
ruseUtztnFcltNm?:string|''
|
||||
|
||||
}
|
||||
|
||||
@@ -152,7 +249,8 @@ export type MoneyDetailType = {
|
||||
clclnBrdnCst : string | '';
|
||||
clclnEcqrRt : string | '';
|
||||
}
|
||||
|
||||
// 규칙 타입 선언
|
||||
export type DisableRulesType = Record<string, string[]>;
|
||||
/**====================================
|
||||
* Store - 데이터
|
||||
====================================*/
|
||||
@@ -160,6 +258,139 @@ export type MoneyDetailType = {
|
||||
const demandCompList : DemandCompType[] = [
|
||||
]
|
||||
|
||||
|
||||
const disableRules: DisableRulesType = {
|
||||
"청소": ["tn", 'tph',"clIonQnt" ,'cdtnv'],
|
||||
"화장실용수": ["tn", 'tph',"clIonQnt" ,'cdtnv'],
|
||||
"세척": ["combResdch", "crmty",'tph','tn',"clIonQnt",'cdtnv'],
|
||||
"살수": ["combResdch", "crmty",'tph','tn',"clIonQnt",'cdtnv'],
|
||||
"조경": ["combResdch", "crmty",'tph','cdtnv'],
|
||||
"농업_간접용수": ["combResdch", "crmty",'tn','tph','clIonQnt'],
|
||||
"농업_직접용수": ["combResdch", "crmty",'tn','tph','clIonQnt'],
|
||||
"하천": ["combResdch", "trbt", "crmty" ,'clIonQnt','cdtnv'],
|
||||
'공업':[]
|
||||
};
|
||||
|
||||
const defaultRules: Record<string, object> = {
|
||||
"청소": {
|
||||
grssClfg: null,
|
||||
combResdch:0.2,
|
||||
trbt: 2,
|
||||
bod: 5,
|
||||
crmty: 20,
|
||||
ph: "5.8~8.5",
|
||||
smll:'불쾌하지않을것',
|
||||
tn:null,
|
||||
tph: null,
|
||||
clIonQnt: null,
|
||||
cdtnv: null,
|
||||
|
||||
},
|
||||
"화장실용수": {
|
||||
grssClfg: null,
|
||||
combResdch:0.2,
|
||||
trbt: 2,
|
||||
bod: 5,
|
||||
crmty: 20,
|
||||
smll:'불쾌하지않을것',
|
||||
ph: "5.8~8.5",
|
||||
tn:null,
|
||||
tph: null,
|
||||
clIonQnt: null,
|
||||
cdtnv: null,
|
||||
},
|
||||
"세척": {
|
||||
grssClfg: null,
|
||||
trbt: 2,
|
||||
bod: 5,
|
||||
ph: "5.8~8.5",
|
||||
combResdch:null,
|
||||
smll:'불쾌하지않을것',
|
||||
crmty:null,
|
||||
tn:null,
|
||||
tph: null,
|
||||
clIonQnt: null,
|
||||
cdtnv: null,
|
||||
},
|
||||
"살수": {
|
||||
grssClfg: null,
|
||||
trbt: 2,
|
||||
bod: 5,
|
||||
ph: "5.8~8.5",
|
||||
smll:'불쾌하지않을것',
|
||||
combResdch:null,
|
||||
crmty:null,
|
||||
tn:null,
|
||||
tph: null,
|
||||
clIonQnt: null,
|
||||
cdtnv: null,
|
||||
},
|
||||
"조경": {
|
||||
grssClfg: null,
|
||||
trbt: 2,
|
||||
bod: 5,
|
||||
ph: "5.8~8.5",
|
||||
smll:'불쾌하지않을것',
|
||||
clIonQnt:250,
|
||||
combResdch:null,
|
||||
crmty:null,
|
||||
tn:null,
|
||||
tph: null,
|
||||
cdtnv: null,
|
||||
},
|
||||
"농업_간접용수": {
|
||||
grssClfg: null,
|
||||
trbt: 5,
|
||||
bod: 8,
|
||||
ph: "5.8~8.5",
|
||||
cdtnv:2000,
|
||||
combResdch:null,
|
||||
crmty:null,
|
||||
tn:null,
|
||||
tph: null,
|
||||
clIonQnt: null,
|
||||
},
|
||||
"농업_직접용수": {
|
||||
grssClfg: null,
|
||||
trbt: 2,
|
||||
bod: 8,
|
||||
smll:'불쾌하지않을것',
|
||||
ph: "5.8~8.5",
|
||||
cdtnv:700,
|
||||
combResdch:null,
|
||||
crmty:null,
|
||||
tn:null,
|
||||
tph: null,
|
||||
clIonQnt: null,
|
||||
},
|
||||
"하천": {
|
||||
grssClfg: 1000,
|
||||
bod: 5,
|
||||
tn: 10,
|
||||
tph: 0.5,
|
||||
smll:'불쾌하지않을것',
|
||||
ph: "5.8~8.5",
|
||||
combResdch:null,
|
||||
trbt:null,
|
||||
crmty:null,
|
||||
clIonQnt: null,
|
||||
cdtnv:null
|
||||
},
|
||||
'공업':{
|
||||
grssClfg: null,
|
||||
bod: null,
|
||||
tn: null,
|
||||
tph: null,
|
||||
ph: "~",
|
||||
combResdch:null,
|
||||
smll:'불쾌하지않을것',
|
||||
trbt:null,
|
||||
crmty:null,
|
||||
clIonQnt: null,
|
||||
cdtnv:null
|
||||
}
|
||||
};
|
||||
|
||||
// 사용용도
|
||||
const useTypesData : SelectDataType[] = [
|
||||
{ text: '청소', val: "청소" },
|
||||
@@ -167,9 +398,9 @@ const useTypesData : SelectDataType[] = [
|
||||
{ text: "세척", val: "세척" },
|
||||
{ text: "살수", val: "살수" },
|
||||
{ text: "조경", val: "조경" },
|
||||
{ text: "농업", val: "농업" },
|
||||
{ text: "농업_간접용수", val: "농업_간접용수" },
|
||||
{ text: "농업_직접용수", val: "농업_직접용수" },
|
||||
{ text: "하천", val: "하천" },
|
||||
{ text: "지하수", val: "지하수" },
|
||||
{ text: "공업", val: "공업" },
|
||||
];
|
||||
|
||||
@@ -280,7 +511,7 @@ const postSdmData = async (data: DemandCompType) => {
|
||||
}
|
||||
|
||||
// 수요정보 등록
|
||||
const postSdmInfoData = async (data: DemandCompType) => {
|
||||
const postSdmInfoData = async (data: DemandCompBizType) => {
|
||||
try {
|
||||
await postFetch("/reuseFc/request/demandInfo", data);
|
||||
toast.success('수요 정보 등록에 성공하였습니다.');
|
||||
@@ -360,7 +591,7 @@ const fetchGetSimilarCaseList = async (part: string, value: number|string) => {
|
||||
export type SdmActions = {
|
||||
initializeSdmData: () => void;
|
||||
insertSdmData: (data: DemandCompType) => Promise<void>;
|
||||
insertSdmInfoData: (data: DemandCompType) => Promise<void>;
|
||||
insertSdmInfoData: (data: DemandCompBizType) => Promise<void>;
|
||||
getDemandCompList: (searchText: string, pageCnt: number) => Promise<void>;
|
||||
actionLogin: (data: DemandCompType) => Promise<void>;
|
||||
getSdmInfoStatusList: (dmdInfoId: string) => Promise<void>;
|
||||
@@ -378,9 +609,10 @@ export type SdmType ={
|
||||
moneyDetailList: MoneyDetailType[];
|
||||
|
||||
// 수요정보 신청현황
|
||||
sdmInfoStatusList: DemandCompType[];
|
||||
sdmInfoStatusList: DemandCompBizType[];
|
||||
upHeadSdmInfoStatus: TableUpperProps[];
|
||||
|
||||
defaultRules: Record<string, object>
|
||||
disableRules: DisableRulesType;
|
||||
// 로그인관련
|
||||
successNextPage : boolean;
|
||||
successLogin: boolean;
|
||||
@@ -398,7 +630,10 @@ export const SdmState: SdmType = {
|
||||
moneyDetailList: moneyDetailList,
|
||||
// 수요정보 신청현황
|
||||
sdmInfoStatusList: [],
|
||||
defaultRules:defaultRules,
|
||||
disableRules:disableRules,
|
||||
upHeadSdmInfoStatus: upHeadSdmInfoStatus,
|
||||
|
||||
// 로그인관련
|
||||
successNextPage : false,
|
||||
successLogin: false,
|
||||
@@ -428,7 +663,7 @@ export const SdmReducer = (set: any): SdmActions => {
|
||||
}
|
||||
},
|
||||
|
||||
insertSdmInfoData: async (data: DemandCompType) => {
|
||||
insertSdmInfoData: async (data: DemandCompBizType) => {
|
||||
postSdmInfoData(data);
|
||||
},
|
||||
|
||||
@@ -465,11 +700,13 @@ export const SdmReducer = (set: any): SdmActions => {
|
||||
}))
|
||||
return;
|
||||
}
|
||||
const sdmInfoStatusList : DemandCompType[] = [];
|
||||
const sdmInfoStatusList : DemandCompBizType[] = [];
|
||||
|
||||
res.demandInfo.map((el: DemandCompType, index: number) => {
|
||||
res.demandInfo.map((el: DemandCompBizType, index: number) => {
|
||||
const matchingInfo = res.demandMatchings.find((matching: DemandMatchings) => matching.dmdInfoSn === el.dmdInfoSn);
|
||||
const status = matchingInfo?.splyFcltFlg?.trim() === 'Sub' ? '검토' : matchingInfo?.splyFcltFlg?.trim() === 'matched' ? '매칭완료' : '신청';
|
||||
const ruseUtztnFcltNm = matchingInfo?.ruseUtztnFcltNm ? matchingInfo.ruseUtztnFcltNm : '';
|
||||
|
||||
|
||||
const tmpData = {
|
||||
...el,
|
||||
@@ -485,7 +722,9 @@ export const SdmReducer = (set: any): SdmActions => {
|
||||
useUsgCd: el.useUsgCd,
|
||||
dmndTmng: el.dmndTmng,
|
||||
dmdInfoSn : el.dmdInfoSn,
|
||||
status : status
|
||||
status : status,
|
||||
splyCndtList: el.splyCndtList,
|
||||
ruseUtztnFcltNm: ruseUtztnFcltNm || ''
|
||||
}
|
||||
sdmInfoStatusList.push(tmpData);
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useDmStore } from '@/app/store';
|
||||
import type { DemandCompType } from '@/app/store/sdmMng';
|
||||
import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
type thisProps = {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
export const sanitizeInput = (val: string, allowDecimal = false) => {
|
||||
if (val === "") return ""; // 빈 값 허용
|
||||
|
||||
// 소숫점 허용
|
||||
if (allowDecimal) {
|
||||
// 숫자와 점만 허용
|
||||
let cleaned = val.replace(/[^0-9.]/g, "");
|
||||
// 소숫점 여러 개일 경우 첫 번째만 유지
|
||||
const parts = cleaned.split(".");
|
||||
if (parts.length > 2) {
|
||||
cleaned = parts[0] + "." + parts.slice(1).join("");
|
||||
}
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
// 정수만 허용
|
||||
return val.replace(/[^0-9]/g, "");
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
"use client";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import {
|
||||
SearchDiv,
|
||||
SearchFormLeft,
|
||||
SelectBox,
|
||||
type SelectDataType,
|
||||
} from "@common/business_components/ui";
|
||||
@@ -48,6 +46,7 @@ export function SearchBar({
|
||||
},
|
||||
[Common.selectSigunData]
|
||||
);
|
||||
|
||||
const onSelectValue = useCallback(
|
||||
(val: string, type: string) => {
|
||||
switch (type) {
|
||||
@@ -62,78 +61,58 @@ export function SearchBar({
|
||||
[initializeSigunData]
|
||||
);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log("map.ruseFcltList ", map.ruseFcltList);
|
||||
// }, [map.ruseFcltList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-4 gap-4 ">
|
||||
<div className="col-span-4">
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className="col-span-10 flex flex-row">
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/6 h-full text-2xl"
|
||||
label="시도"
|
||||
selectClass={""}
|
||||
selectArray={Common.selectSidoData || []}
|
||||
onSelectValue={(val) =>
|
||||
onSelectValue(val.toString(), "ctpvCd")
|
||||
}
|
||||
value={sidoValue || Common.selectSidoData[0]?.val || ""}
|
||||
/>
|
||||
<SelectBox
|
||||
className="col-span-2 w-1/6 h-full text-2xl ml-10"
|
||||
label="시군구"
|
||||
selectClass={""}
|
||||
selectArray={filteredSigunData || []}
|
||||
onSelectValue={(val) =>
|
||||
onSelectValue(val.toString(), "sggCd")
|
||||
}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<div className="col-span-6 flex flex-row items-center min-w-[27rem] ml-10">
|
||||
<Label className="text-xl justify-self-center mt-2 mx-3">
|
||||
하수재이용시설
|
||||
</Label>
|
||||
<SearchInputText
|
||||
label={"시설명"}
|
||||
initValue={undefined}
|
||||
setValue={(searchNm?: string) => {
|
||||
let sggOrCity = "";
|
||||
if (searchNm) {
|
||||
if (sigunValue === "0" || sigunValue === "") {
|
||||
sggOrCity = sidoValue;
|
||||
} else {
|
||||
sggOrCity = sigunValue;
|
||||
}
|
||||
findRuseFclt(sggOrCity, searchNm);
|
||||
}
|
||||
}}
|
||||
setRtnData={(rtnData: any) => {
|
||||
setHasuPk(rtnData);
|
||||
}}
|
||||
searchList={map.ruseFcltList || []}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button
|
||||
color="sky"
|
||||
className="mt-2"
|
||||
size={"sm"}
|
||||
onClick={() => {
|
||||
onApply();
|
||||
}}
|
||||
>
|
||||
적용
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
</SearchDiv>
|
||||
</div>
|
||||
<div className="flex justify-center w-full px-4 py-3">
|
||||
<div className="flex items-center w-full max-w-screen-lg rounded-xl bg-white shadow-md px-6 py-4 space-x-3">
|
||||
<SelectBox
|
||||
labelClass="w-[40px]"
|
||||
className="w-[200px]"
|
||||
label="시도"
|
||||
selectClass="rounded-lg bg-white"
|
||||
selectArray={Common.selectSidoData || []}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
||||
value={sidoValue || Common.selectSidoData[0]?.val || ""}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[65px]"
|
||||
className="w-[200px]"
|
||||
label="시군구"
|
||||
selectClass="rounded-lg bg-white"
|
||||
selectArray={filteredSigunData || []}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<Label className="text-gray-600">하수재이용시설</Label>
|
||||
<SearchInputText
|
||||
label="시설명"
|
||||
initValue={undefined}
|
||||
setValue={(searchNm?: string) => {
|
||||
let sggOrCity = "";
|
||||
if (searchNm) {
|
||||
if (sigunValue === "0" || sigunValue === "") {
|
||||
sggOrCity = sidoValue;
|
||||
} else {
|
||||
sggOrCity = sigunValue;
|
||||
}
|
||||
findRuseFclt(sggOrCity, searchNm);
|
||||
}
|
||||
}}
|
||||
setRtnData={(rtnData: any) => {
|
||||
setHasuPk(rtnData);
|
||||
}}
|
||||
searchList={map.ruseFcltList || []}
|
||||
/>
|
||||
<Button
|
||||
color="sky"
|
||||
className="ml-2 text-white"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
onApply();
|
||||
}}
|
||||
>
|
||||
적용
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { FormUi } from "@common/business_components/ui";
|
||||
import type { ReactNode } from "react";
|
||||
import type { ControllerRenderProps, FieldError, FieldValues, Path } from "react-hook-form";
|
||||
|
||||
|
||||
import { cloneElement, isValidElement } from "react";
|
||||
interface AccessibleFormFieldProps<T extends FieldValues> {
|
||||
field: ControllerRenderProps<T, Path<T>>;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
error?: FieldError;
|
||||
inputComponent: ReactNode;
|
||||
isDisabled?: boolean;
|
||||
inputComponent: React.ReactElement<{ disabled?: boolean }>;
|
||||
}
|
||||
|
||||
export function AccessibleFormField<T extends FieldValues>({
|
||||
label,
|
||||
required = false,
|
||||
inputComponent,
|
||||
isDisabled = false,
|
||||
}: AccessibleFormFieldProps<T>) {
|
||||
const labelText = required ? `${label} *` : label;
|
||||
|
||||
return (
|
||||
<FormUi label={labelText}>
|
||||
<FormUi label={labelText} isDisabled={isDisabled}>
|
||||
<div>
|
||||
{inputComponent}
|
||||
{isValidElement(inputComponent)
|
||||
? cloneElement(inputComponent, { disabled: isDisabled } as Partial<typeof inputComponent.props>)
|
||||
: inputComponent}
|
||||
{/* {error?.message && Array.from(new Set(error.message.split('\n'))).map((msg, idx) => (
|
||||
<p key={idx} className="text-red-500 text-sm mt-1" role="alert">
|
||||
{msg}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import ModalSimilarPage from "@/app/(base-layout)/pages/sdmMng/sdmInfoInsert/modalSimilar/page";
|
||||
import type { FormData } from "@/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import {
|
||||
CANDIDATE,
|
||||
@@ -11,22 +10,30 @@ import {
|
||||
UiModal,
|
||||
} from "@common/business_components";
|
||||
import { Input } from "@common/components";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { Button, Label } from "@common/components/ui";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { AccessibleFormField } from "./AccessibleFormField";
|
||||
import type { ControllerRenderProps, UseFormReturn, FieldError } from "react-hook-form";
|
||||
import {
|
||||
type ControllerRenderProps,
|
||||
type UseFormReturn,
|
||||
type FieldError,
|
||||
useWatch,
|
||||
} from "react-hook-form";
|
||||
import SearchInputText from "../common/searhDemandInput/component";
|
||||
import type { DemandCompType } from "@/app/store/sdmMng";
|
||||
import React from "react";
|
||||
import { sanitizeInput } from "../common/utils/utils";
|
||||
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
||||
import type { SdmInfoFormData } from "@/app/store/sdmMng";
|
||||
|
||||
export interface SdmInfoRegFormProps {
|
||||
form: UseFormReturn<FormData>;
|
||||
form: UseFormReturn<SdmInfoFormData>;
|
||||
onCheckboxChange?: (checked: boolean) => void;
|
||||
facilityName?: string;
|
||||
}
|
||||
|
||||
type FieldComponent = (
|
||||
field: ControllerRenderProps<FormData, keyof FormData>
|
||||
field: ControllerRenderProps<SdmInfoFormData, keyof SdmInfoFormData>
|
||||
) => JSX.Element;
|
||||
|
||||
export function SdmInfoRegForm({
|
||||
@@ -35,12 +42,15 @@ export function SdmInfoRegForm({
|
||||
facilityName,
|
||||
}: SdmInfoRegFormProps) {
|
||||
const { initializeCommonData, Sdm } = useDmStore((state) => state);
|
||||
const { useTypesData } = Sdm;
|
||||
const { useTypesData, defaultRules, disableRules } = Sdm;
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const [sdmNmValue, setSdmNmValue] = useState<string>(
|
||||
searchParams.get("dmdCoNm") || ""
|
||||
);
|
||||
|
||||
const [frontPHdata, setFrontPHdata] = useState("0");
|
||||
const [backPHdata, setBackPHdata] = useState("0");
|
||||
const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(false);
|
||||
const [isSimilarCase, setIsSimilarCase] = useState(false);
|
||||
const [key, setKey] = useState(0);
|
||||
@@ -58,6 +68,7 @@ export function SdmInfoRegForm({
|
||||
useEffect(() => {
|
||||
console.log("candidateTitle ", form.getValues("candidateTitle"));
|
||||
}, [form.getValues]);
|
||||
|
||||
useEffect(() => {
|
||||
if (useTypesData && useTypesData.length > 0) {
|
||||
setTimeout(() => {
|
||||
@@ -109,6 +120,52 @@ export function SdmInfoRegForm({
|
||||
onCheckboxChange?.(checked);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const watchPH = form.getValues(`ph`);
|
||||
if (watchPH && watchPH !== undefined) {
|
||||
const [frontPH, backPH] = watchPH.split("~");
|
||||
if (frontPH) {
|
||||
setFrontPHdata(frontPH);
|
||||
}
|
||||
if (backPH) {
|
||||
setBackPHdata(backPH);
|
||||
}
|
||||
} else {
|
||||
setFrontPHdata("0");
|
||||
setBackPHdata("0");
|
||||
}
|
||||
}, [form.getValues(`ph`)]);
|
||||
|
||||
const isSetPh = useCallback(
|
||||
(typePHdata: string, index: number) => {
|
||||
const PHdata = form.getValues("ph");
|
||||
const isPH = PHdata?.split("~")[index] || "";
|
||||
if (index == 0) {
|
||||
form.setValue("ph", `${isPH}~${typePHdata}`);
|
||||
setBackPHdata(typePHdata);
|
||||
} else if (index == 1) {
|
||||
form.setValue("ph", `${typePHdata}~${isPH}`);
|
||||
setFrontPHdata(typePHdata);
|
||||
}
|
||||
},
|
||||
[form]
|
||||
);
|
||||
|
||||
const usePurpose = useWatch({ control: form.control, name: "usePurpose" });
|
||||
|
||||
useEffect(() => {
|
||||
if (!usePurpose) return;
|
||||
|
||||
// Apply default values
|
||||
const defaults = defaultRules[usePurpose];
|
||||
|
||||
if (defaults) {
|
||||
Object.entries(defaults).forEach(([field, value]) => {
|
||||
form.setValue(field as keyof SdmInfoFormData, value, { shouldDirty: true });
|
||||
});
|
||||
}
|
||||
}, [form.getValues("usePurpose")]);
|
||||
|
||||
// 시설 선택시 이벤트
|
||||
const selectComp = useCallback(
|
||||
(rtnData: DemandCompType[]) => {
|
||||
@@ -119,7 +176,7 @@ export function SdmInfoRegForm({
|
||||
);
|
||||
|
||||
const formFields: Array<{
|
||||
name: keyof FormData;
|
||||
name: keyof SdmInfoFormData;
|
||||
label: string;
|
||||
component: FieldComponent | undefined;
|
||||
formIndex: number;
|
||||
@@ -178,7 +235,6 @@ export function SdmInfoRegForm({
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="요구수량(㎥/d)을 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
@@ -222,7 +278,6 @@ export function SdmInfoRegForm({
|
||||
isDisabled={isBznmTotNoDisabled}
|
||||
>
|
||||
<Input
|
||||
placeholder="희망단가(원/㎥)을 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
@@ -244,221 +299,7 @@ export function SdmInfoRegForm({
|
||||
required: true,
|
||||
component: undefined,
|
||||
},
|
||||
{
|
||||
name: "grssClfg",
|
||||
label: "총대장균군수(개/100ml)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="총대장균군수를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "tn",
|
||||
label: "총질소(T-N)(mg/L)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="총질소를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "combResdch",
|
||||
label: "결합잔류염소(mg/L)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="결합잔류염소를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "tph",
|
||||
label: "총인(T-P)(mg/L)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="총인을 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "trbt",
|
||||
label: "탁도(NTU)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="탁도를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "ph",
|
||||
label: "수소이온농도(pH)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="수소이온농도를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "bod",
|
||||
label: "생물학적 산소요구량(BOD)(mg/L)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="생물학적 산소요구량을 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "clIonQnt",
|
||||
label: "염화물(mgCL/L)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="염화물을 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "smll",
|
||||
label: "냄새",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="냄새를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "cdtnv",
|
||||
label: "전기전도도(㎲/cm)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="전기전도도를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "crmty",
|
||||
label: "색도(도)",
|
||||
formIndex: 1,
|
||||
required: true,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="색도를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "rmrk",
|
||||
label: "비고",
|
||||
formIndex: 1,
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="비고를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
value={field.value as string}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
name: "candidateTitle",
|
||||
label: CANDIDATE || "공급처 후보지 선택(필수아님)",
|
||||
@@ -466,10 +307,20 @@ export function SdmInfoRegForm({
|
||||
component: () => (
|
||||
<div>
|
||||
<div className="-mt-14 float-right flex gap-10">
|
||||
<Button className={"bg-gray-200"} onClick={searchCandidate}>
|
||||
<Button
|
||||
className="rounded-md"
|
||||
color="sky"
|
||||
size={"sm"}
|
||||
onClick={searchCandidate}
|
||||
>
|
||||
후보지 조회
|
||||
</Button>
|
||||
<Button className={"bg-gray-200"} onClick={searchSimilarCase}>
|
||||
<Button
|
||||
className="rounded-md"
|
||||
color="sky"
|
||||
size={"sm"}
|
||||
onClick={searchSimilarCase}
|
||||
>
|
||||
유사사례
|
||||
</Button>
|
||||
</div>
|
||||
@@ -495,9 +346,7 @@ export function SdmInfoRegForm({
|
||||
const normalFields = formFields.filter(
|
||||
(f) => f.formIndex === 0 && f.component
|
||||
);
|
||||
const groupFields = formFields.filter(
|
||||
(f) => f.formIndex === 1 && f.component
|
||||
);
|
||||
|
||||
const candidateFields = formFields.filter(
|
||||
(f) => f.formIndex === 2 && f.component
|
||||
);
|
||||
@@ -512,17 +361,20 @@ export function SdmInfoRegForm({
|
||||
name={name}
|
||||
render={({ field }) =>
|
||||
name === "money" ? (
|
||||
component(field)
|
||||
<div>
|
||||
<Label className="text-black" >
|
||||
희망단가*
|
||||
</Label>
|
||||
{ component(field)}
|
||||
</div>
|
||||
// <FormUi label={label + "*"}>{component(field)}</FormUi>
|
||||
) : (
|
||||
// <FormUi label={label}>
|
||||
// {component(field)}
|
||||
// </FormUi>
|
||||
<AccessibleFormField
|
||||
field={field}
|
||||
label={label}
|
||||
required={required}
|
||||
error={
|
||||
typeof form.formState.errors[name]?.message === 'string'
|
||||
typeof form.formState.errors[name]?.message === "string"
|
||||
? (form.formState.errors[name] as FieldError)
|
||||
: undefined
|
||||
}
|
||||
@@ -534,37 +386,527 @@ export function SdmInfoRegForm({
|
||||
) : null
|
||||
)}
|
||||
|
||||
{groupFields.length > 0 && (
|
||||
<div className="mt-10">
|
||||
<FormLabel className="text-xl">요구수질</FormLabel>
|
||||
<div className="flex flex-wrap justify-start gap-10 mt-3 mb-3">
|
||||
{groupFields.map(({ name, label, component, required }) => (
|
||||
<div key={name} className="min-w-[180px]">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
// <FormUi label={label}>
|
||||
// {component?.(field)}
|
||||
// </FormUi>
|
||||
<AccessibleFormField
|
||||
field={field}
|
||||
label={label}
|
||||
required={required}
|
||||
error={
|
||||
typeof form.formState.errors[name]?.message === 'string'
|
||||
? (form.formState.errors[name] as FieldError)
|
||||
: undefined
|
||||
}
|
||||
inputComponent={component?.(field)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className="mt-10">
|
||||
<FormLabel className="text-xl">요구수질</FormLabel>
|
||||
<div className="grid grid-cols-2 gap-x-10 gap-y-6 mt-3 mb-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`grssClfg`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={
|
||||
!isDisabled ? `총대장균군수(개/mg)*` : "총대장균군수(개/mg)"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: "불검출"
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
{/* TODO: 총대장균군수 확인 필요*/}
|
||||
{field.value !== null ? (
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`tn`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={
|
||||
!isDisabled ? `총질소(T-N)(mg/L)*` : "총질소(T-N)(mg/L)"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`combResdch`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={
|
||||
!isDisabled ? `결합잔류염소(mg/L)*` : "결합잔류염소(mg/L)"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const cleanValue = sanitizeInput(
|
||||
e.target.value,
|
||||
true
|
||||
);
|
||||
field.onChange(
|
||||
cleanValue === "" ? null : Number(cleanValue)
|
||||
);
|
||||
}}
|
||||
value={field.value ?? ""}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이상
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`tph`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={!isDisabled ? `총인(T-P)(mg/L)*` : "총인(T-P)(mg/L)"}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const cleanValue = sanitizeInput(
|
||||
e.target.value,
|
||||
true
|
||||
);
|
||||
field.onChange(
|
||||
cleanValue === "" ? null : Number(cleanValue)
|
||||
);
|
||||
}}
|
||||
value={field.value ?? ""}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`trbt`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi label={!isDisabled ? `탁도(NTU)*` : "탁도(NTU)"}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="col-span-1">
|
||||
<Label className="text-xl">수소이온농도(PH)</Label>
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<Input
|
||||
className="!text-xl w-36"
|
||||
placeholder=""
|
||||
type="text"
|
||||
onChange={(e) => {
|
||||
const cleanValue = sanitizeInput(e.target.value, true);
|
||||
isSetPh(cleanValue, 1);
|
||||
}}
|
||||
value={frontPHdata ?? ""}
|
||||
maxLength={5}
|
||||
/>
|
||||
<span className="text-xl mx-2">~</span>
|
||||
<Input
|
||||
className="!text-xl w-36 "
|
||||
placeholder=""
|
||||
type="text"
|
||||
onChange={(e) => {
|
||||
const cleanValue = sanitizeInput(e.target.value, true);
|
||||
isSetPh(cleanValue, 0);
|
||||
}}
|
||||
value={backPHdata ?? ""}
|
||||
maxLength={5}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`bod`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={
|
||||
!isDisabled
|
||||
? `생물학적 산소요구량(BOD)(mg/L)*`
|
||||
: "생물학적 산소요구량(BOD)(mg/L)"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`clIonQnt`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={!isDisabled ? `염화물(mgCL/L)*` : "염화물(mgCL/L)"}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`smll`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi label={!isDisabled ? `냄새*` : "냄새"}>
|
||||
<SelectBox
|
||||
className="!text-2xl rounded-md"
|
||||
label={""}
|
||||
selectClass={undefined}
|
||||
selectArray={[
|
||||
{ text: "불쾌하지않을것", val: "불쾌하지않을것" },
|
||||
{ text: "해당사항없음", val: "해당사항없음" },
|
||||
]}
|
||||
onSelectValue={(val: string | number) => {
|
||||
field.onChange(val.toString());
|
||||
}}
|
||||
value={field.value }
|
||||
readOnly={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`cdtnv`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi
|
||||
label={
|
||||
!isDisabled ? `전기전도도(㎲/cm)*` : "전기전도도(㎲/cm)"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`crmty`}
|
||||
render={({ field }) => {
|
||||
const disabledFields = usePurpose ? disableRules[usePurpose] : [];
|
||||
const isDisabled = disabledFields
|
||||
? disabledFields.includes(field.name)
|
||||
: true;
|
||||
return (
|
||||
<FormUi label={!isDisabled ? `색도(도)*` : "색도(도)"}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value.replace(
|
||||
/[^0-9]/g,
|
||||
""
|
||||
); // 정수만
|
||||
field.onChange(
|
||||
inputValue === "" ? null : Number(inputValue)
|
||||
);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== null
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
maxLength={6}
|
||||
disabled={isDisabled}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
|
||||
이하
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`rmrk`}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormUi label={"비고"}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Input
|
||||
type="text"
|
||||
className="w-full pr-16 border py-2 text-right rounded-md"
|
||||
placeholder=""
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
}}
|
||||
value={
|
||||
field.value !== undefined && field.value !== ""
|
||||
? `${field.value}`
|
||||
: ""
|
||||
}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</FormUi>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{candidateFields.map(({ name, label, component, required }, index) =>
|
||||
component ? (
|
||||
@@ -581,7 +923,7 @@ export function SdmInfoRegForm({
|
||||
label={label}
|
||||
required={required}
|
||||
error={
|
||||
typeof form.formState.errors[name]?.message === 'string'
|
||||
typeof form.formState.errors[name]?.message === "string"
|
||||
? (form.formState.errors[name] as FieldError)
|
||||
: undefined
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useDmStore } from "@/app/store";
|
||||
import type { DemandCompType } from "@/app/store/sdmMng";
|
||||
import { BZNM_TOT_NO, FormField, FormUi } from "@common/business_components";
|
||||
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
||||
import { Button, Input } from "@common/components";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { sha512 } from 'js-sha512';
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { FormProvider, useForm, type ControllerRenderProps } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import * as z from "zod";
|
||||
|
||||
export function SdmLoginForm() {
|
||||
@@ -15,10 +16,10 @@ export function SdmLoginForm() {
|
||||
const formatBusinessNumber = (value: string) => {
|
||||
// 숫자만 추출
|
||||
const digitsOnly = value.replace(/\D/g, "");
|
||||
|
||||
|
||||
// 10자리까지만 처리
|
||||
const limitedDigits = digitsOnly.slice(0, 10);
|
||||
|
||||
|
||||
// 3-2-5 형식으로 포맷팅
|
||||
if (limitedDigits.length <= 3) {
|
||||
return limitedDigits;
|
||||
@@ -28,33 +29,33 @@ export function SdmLoginForm() {
|
||||
return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3, 5)}-${limitedDigits.slice(5)}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 스키마를 useMemo로 최적화
|
||||
const sdmLoginFormSchema = useMemo(() => z.object({
|
||||
dmdInfoId: z.string()
|
||||
.min(1, "사업자등록번호를 입력해주세요")
|
||||
.regex(/^\d{10}$/, "사업자등록번호는 10자리 숫자만 입력 가능합니다"),
|
||||
bznmTotNo: isBznmTotNoDisabled
|
||||
bznmTotNo: isBznmTotNoDisabled
|
||||
? z.string().optional()
|
||||
: z.string().length(4, "종사업자등록번호는 4자리 숫자만 입력 가능합니다"),
|
||||
password: z.string()
|
||||
.min(1, "비밀번호를 입력해주세요")
|
||||
.min(8, "비밀번호는 8자 이상 입력해주세요")
|
||||
.min(8, "비밀번호는 8자 이상 입력해주세요")
|
||||
.max(16, "비밀번호는 16자 이내로 입력해주세요"),
|
||||
rgtr: z.string().optional(),
|
||||
dmdAplyPswd: z.string().optional(),
|
||||
}), [isBznmTotNoDisabled]);
|
||||
|
||||
type FormData = z.infer<typeof sdmLoginFormSchema>;
|
||||
type FieldComponent = (field: ControllerRenderProps<FormData, keyof FormData>) => JSX.Element;
|
||||
|
||||
type SdmLoginForm = z.infer<typeof sdmLoginFormSchema>;
|
||||
type FieldComponent = (field: ControllerRenderProps<SdmLoginForm, keyof SdmLoginForm>) => JSX.Element;
|
||||
|
||||
const formSchema = sdmLoginFormSchema;
|
||||
|
||||
useEffect(() => {
|
||||
initializeCommonData();
|
||||
}, [initializeCommonData]);
|
||||
|
||||
const defaultValues: FormData = {
|
||||
const defaultValues: SdmLoginForm = {
|
||||
dmdInfoId: '',
|
||||
bznmTotNo: '',
|
||||
password: '',
|
||||
@@ -62,7 +63,7 @@ export function SdmLoginForm() {
|
||||
dmdAplyPswd: '',
|
||||
};
|
||||
|
||||
const form = useForm<FormData>({
|
||||
const form = useForm<SdmLoginForm>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues,
|
||||
mode: "onChange",
|
||||
@@ -72,6 +73,26 @@ export function SdmLoginForm() {
|
||||
setIsBznmTotNoDisabled(checked);
|
||||
};
|
||||
|
||||
const initPassword = () => {
|
||||
toast(
|
||||
(t) => (
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="text-2xl mb-2">
|
||||
관리자(031-0000-0000)에게 비밀번호 초기화 요청을 유선으로 하시기 바랍니다.
|
||||
</div>
|
||||
<Button size={"sm"} onClick={() => toast.dismiss(t.id)}>
|
||||
확인
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
id: "pwd-reset",
|
||||
duration: Infinity,
|
||||
position: "top-center",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const handleLogin = () => {
|
||||
const data = form.getValues();
|
||||
const passwordStr = sha512.hex(data.password)
|
||||
@@ -82,61 +103,61 @@ export function SdmLoginForm() {
|
||||
}
|
||||
actionLogin(param as unknown as DemandCompType);
|
||||
};
|
||||
|
||||
|
||||
const formFields: Array<{
|
||||
name: keyof FormData;
|
||||
name: keyof SdmLoginForm;
|
||||
label: string;
|
||||
component: FieldComponent | undefined;
|
||||
}> = [
|
||||
{
|
||||
name: "dmdInfoId",
|
||||
label: "사업자등록번호",
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="사업자등록번호를 입력하세요"
|
||||
type="text"
|
||||
className="!mt-5 !mb-5 !text-xl text-gray-600"
|
||||
{...field}
|
||||
value={formatBusinessNumber(field.value as string)}
|
||||
onChange={(e) => {
|
||||
const dmdId = e.target.value.replace(/\D/g, "").slice(0, 10);
|
||||
field.onChange(dmdId);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: "bznmTotNo",
|
||||
label: BZNM_TOT_NO || "종사업자등록번호",
|
||||
component: (field) => (
|
||||
<FormUi
|
||||
label="종사업자등록번호"
|
||||
onCheckboxChange={handleCheckboxChange}
|
||||
isDisabled={isBznmTotNoDisabled}
|
||||
>
|
||||
<Input
|
||||
placeholder="여러사업장이 있을 경우 입력해주세요"
|
||||
{
|
||||
name: "dmdInfoId",
|
||||
label: "사업자등록번호",
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="사업자등록번호를 입력하세요"
|
||||
type="text"
|
||||
className="!mt-5 !text-xl text-gray-600"
|
||||
disabled={isBznmTotNoDisabled}
|
||||
className="!mt-5 !mb-5 !text-xl text-gray-600"
|
||||
{...field}
|
||||
value={formatBusinessNumber(field.value as string)}
|
||||
onChange={(e) => {
|
||||
const dmdId = e.target.value.replace(/\D/g, "").slice(0, 10);
|
||||
field.onChange(dmdId);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: "bznmTotNo",
|
||||
label: BZNM_TOT_NO || "종사업자등록번호",
|
||||
component: (field) => (
|
||||
<FormUi
|
||||
label="종사업자등록번호"
|
||||
onCheckboxChange={handleCheckboxChange}
|
||||
isDisabled={isBznmTotNoDisabled}
|
||||
>
|
||||
<Input
|
||||
placeholder="여러사업장이 있을 경우 입력해주세요"
|
||||
type="text"
|
||||
className="!mt-5 !text-xl text-gray-600"
|
||||
disabled={isBznmTotNoDisabled}
|
||||
{...field}
|
||||
/>
|
||||
</FormUi>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "비밀번호",
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="비밀번호를 입력하세요"
|
||||
type="password"
|
||||
className="!mt-5 !mb-5 !text-xl text-gray-600"
|
||||
{...field}
|
||||
/>
|
||||
</FormUi>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "비밀번호",
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="비밀번호를 입력하세요"
|
||||
type="password"
|
||||
className="!mt-5 !mb-5 !text-xl text-gray-600"
|
||||
{...field}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -148,11 +169,11 @@ export function SdmLoginForm() {
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
label==='종사업자등록번호' ?
|
||||
<>
|
||||
label === '종사업자등록번호' ?
|
||||
<>
|
||||
{component && component(field)}
|
||||
</>
|
||||
: <FormUi label={label}>
|
||||
: <FormUi label={label}>
|
||||
{component && component(field)}
|
||||
</FormUi>
|
||||
)}
|
||||
@@ -160,12 +181,15 @@ export function SdmLoginForm() {
|
||||
))}
|
||||
</FormProvider>
|
||||
<div className="flex justify-center pt-4">
|
||||
<Button
|
||||
onClick={handleLogin}
|
||||
className="w-32 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-md transition-colors duration-200"
|
||||
>
|
||||
로그인
|
||||
</Button>
|
||||
<div className="flex flex-col items-center">
|
||||
<Button
|
||||
onClick={handleLogin}
|
||||
className="w-32 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-md transition-colors duration-200"
|
||||
>
|
||||
로그인
|
||||
</Button>
|
||||
<div className="text-xl mt-2 text-gray-400 cursor-pointer" onClick={initPassword}>비밀번호 초기화 요청</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 896 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 923 B |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 35 KiB |
@@ -16,7 +16,6 @@ export class LoginApiService implements LoginService {
|
||||
|
||||
public async login(payload: ILogin): Promise<void> {
|
||||
try {
|
||||
console.log(' ENDPOINT.USER_SERVICE.LOGIN ', ENDPOINT.USER_SERVICE.LOGIN)
|
||||
const response = await fetch( PROXY_HOST+ ENDPOINT.USER_SERVICE.LOGIN, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -2,6 +2,7 @@ NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
|
||||
@@ -15,6 +15,7 @@ NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
|
||||
@@ -169,10 +169,9 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}:
|
||||
} else if (index === 8) {
|
||||
el['item'] = (Number(searchYear)-0).toString();
|
||||
el['provisionalApproval'] = waterUsage1;
|
||||
} else if(index === 4){
|
||||
el['provisionalApproval'] = Math.round((Number(waterUsage1)+Number(waterUsage2)+Number(waterUsage3))/3 * 100)/100;
|
||||
}
|
||||
});
|
||||
console.log('planFcltyList :', planFcltyList);
|
||||
}
|
||||
}, [waterUsage1, waterUsage2, waterUsage3, searchYear, planFcltyList]);
|
||||
// 연도가 바뀌면 hasSearched를 false로 리셋
|
||||
@@ -226,8 +225,8 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}:
|
||||
:(index < 5 || index > 5)&& (index!= 4) && (head.id ==='provisionalApproval'||head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour')
|
||||
?<TableCell className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
|
||||
:(head.id === "provisionalApproval" && index === 4)
|
||||
?<TableCell colSpan={6} className="text-right border border-[var(--border)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
|
||||
:((head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') && index === 4)
|
||||
?<TableCell colSpan={6} className="text-right border border-[var(--border)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{Math.round((Number(waterUsage1)+Number(waterUsage2)+Number(waterUsage3))/3 * 100)/100}</TableCell>
|
||||
:((head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') )
|
||||
?null
|
||||
:(head.id === 'item' && index != 5)?
|
||||
<TableCell className="text-center text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Gis from "@common/business_components/ui/map/Gis";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { SearchBar } from "./searchBar/searchBar";
|
||||
|
||||
interface FeatureData {
|
||||
fid?: string;
|
||||
@@ -13,7 +14,11 @@ interface FeatureData {
|
||||
}
|
||||
|
||||
interface MapInstance {
|
||||
getLayers: () => { getArray: () => Array<{ getSource?: () => { getFeatureById?: (id: string) => FeatureData | null } }> };
|
||||
getLayers: () => {
|
||||
getArray: () => Array<{
|
||||
getSource?: () => { getFeatureById?: (id: string) => FeatureData | null };
|
||||
}>;
|
||||
};
|
||||
getView: () => { fit: (extent: number[], options: object) => void };
|
||||
}
|
||||
|
||||
@@ -26,20 +31,20 @@ const Map = () => {
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`;
|
||||
const reqUrl = `${API_URL}/lyrStngs/FO_NETWORK`;
|
||||
const response = await fetch(reqUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP 오류! 상태: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json(); // 여기서 json 파싱
|
||||
const data = await response.json(); // 여기서 json 파싱
|
||||
setTreeData(data); // 데이터 설정
|
||||
} catch (error) {
|
||||
console.error("데이터 불러오기 실패:", error);
|
||||
@@ -60,7 +65,12 @@ const Map = () => {
|
||||
}
|
||||
|
||||
// 항상 같은 select 객체 사용
|
||||
const select = (GisApp.Module.select as unknown) as { resetSelectedFeature: () => void; selectFeature: (feature: FeatureData, selected: boolean) => void } | undefined;
|
||||
const select = GisApp.Module.select as unknown as
|
||||
| {
|
||||
resetSelectedFeature: () => void;
|
||||
selectFeature: (feature: FeatureData, selected: boolean) => void;
|
||||
}
|
||||
| undefined;
|
||||
if (!select) return;
|
||||
|
||||
// 1. 기존 선택 초기화
|
||||
@@ -114,21 +124,15 @@ const Map = () => {
|
||||
setIsSearchModalOpen(false);
|
||||
};
|
||||
|
||||
// onCheckedLayersChange handler
|
||||
const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: unknown[]) => {
|
||||
// Handle checked layers change if needed
|
||||
console.log('Checked layers changed:', checkedIds, checkedNodesData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
treeData={treeData}
|
||||
isSearch={false}
|
||||
gisMaps={gisMaps}
|
||||
isMiniMap={true}
|
||||
searchView={null}
|
||||
detailView={null}
|
||||
isMiniMap={false}
|
||||
searchView={undefined}
|
||||
detailView={undefined}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
setIsOpen={setIsOpen}
|
||||
setFeatureData={setFeatureData}
|
||||
@@ -136,7 +140,10 @@ const Map = () => {
|
||||
setIsSearchModalOpen={setIsSearchModalOpen}
|
||||
isOpen={isOpen}
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={handleCheckedLayersChange}
|
||||
onCheckedLayersChange={undefined}
|
||||
imgVisble={false}
|
||||
searchBar={() => <SearchBar onFeatureSelect={onFeatureSelect} gisMaps={gisMaps} />}
|
||||
waterDepth={undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
"use client";
|
||||
import { SelectBox, type SelectDataType } from "@common/business_components/ui";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
interface SearchBarProps {
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
gisMaps: React.RefObject<any>;
|
||||
}
|
||||
|
||||
export function SearchBar({ onFeatureSelect, gisMaps }: SearchBarProps) {
|
||||
const [sidoValue, setSidoValue] = useState("0");
|
||||
const [sigunValue, setSigunValue] = useState("0");
|
||||
const [sidoData, setSidoData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [sigunData, setSigunData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [facilityData, setFacilityData] = useState<SelectDataType[]>([{ text: "선택", val: "0" }]);
|
||||
const [facilityValue, setFacilityValue] = useState("0");
|
||||
|
||||
const fetchSido = async () => {
|
||||
const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_ctpv&outputFormat=application/json&propertyName=mctpv_korn_nm,mctpv_cd`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
const list = json.features.map((f: any) => ({
|
||||
text: f.properties.mctpv_korn_nm,
|
||||
val: f.properties.mctpv_cd,
|
||||
}));
|
||||
setSidoData([{ text: "선택", val: "0" }, ...list]);
|
||||
};
|
||||
|
||||
const fetchSigun = async () => {
|
||||
const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_ms_sgg&outputFormat=application/json&propertyName=sgg_cd,sgg_korn_nm`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
const list = json.features.map((f: any) => ({
|
||||
text: f.properties.sgg_korn_nm,
|
||||
val: f.properties.sgg_cd,
|
||||
}));
|
||||
setSigunData(list);
|
||||
};
|
||||
|
||||
const fetchFacilitiesBySggNm = async (sggNm: string) => {
|
||||
const encodedSggNm = encodeURIComponent(sggNm);
|
||||
const url = `/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_il_swage_prcs_fclt&outputFormat=application/json&cql_filter=sgg_nm='${encodedSggNm}'`;
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
const list = json.features.map((f: any) => ({
|
||||
text: f.properties.swtrmt_fclt_nm ?? "(이름없음)",
|
||||
val: f.id, // f.properties.swtrmt_fclt_cd 도 가능
|
||||
}));
|
||||
setFacilityData([{ text: "선택", val: "0" }, ...list]);
|
||||
setFacilityValue("0");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSido();
|
||||
fetchSigun();
|
||||
}, []);
|
||||
|
||||
const initializeSigunData = useCallback(
|
||||
(sidoCd: string) => {
|
||||
if (sidoCd === "0") {
|
||||
setFilteredSigunData([{ text: "선택", val: "0" }]);
|
||||
setSigunValue("0");
|
||||
return;
|
||||
}
|
||||
const prefix = sidoCd.substring(0, 2);
|
||||
const filtered = sigunData.filter((item) => item.val.startsWith(prefix));
|
||||
setFilteredSigunData([{ text: "선택", val: "0" }, ...filtered]);
|
||||
setSigunValue("0");
|
||||
},
|
||||
[sigunData]
|
||||
);
|
||||
|
||||
const onSelectValue = useCallback(
|
||||
(val: string, type: string) => {
|
||||
if (type === "ctpvCd") {
|
||||
setSidoValue(val);
|
||||
initializeSigunData(val);
|
||||
setFacilityData([{ text: "선택", val: "0" }]);
|
||||
} else if (type === "sggCd") {
|
||||
setSigunValue(val);
|
||||
const sggNm = sigunData.find((item) => item.val === val)?.text || "";
|
||||
if (sggNm) fetchFacilitiesBySggNm(sggNm);
|
||||
} else if (type === "fcltCd") {
|
||||
setFacilityValue(val);
|
||||
}
|
||||
},
|
||||
[initializeSigunData, sigunData]
|
||||
);
|
||||
|
||||
const handleSearch = () => {
|
||||
if (!gisMaps.current || facilityValue === "0") return;
|
||||
|
||||
const layers = gisMaps.current.getLayers().getArray();
|
||||
for (const layer of layers) {
|
||||
const source = layer.getSource?.();
|
||||
const feature = source?.getFeatureById?.(facilityValue);
|
||||
if (feature) {
|
||||
onFeatureSelect(feature);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alert("해당 처리시설을 지도에서 찾을 수 없습니다.");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div className="col-span-4">
|
||||
<div className="card card-border flex">
|
||||
<div className="col-span-10 flex flex-row">
|
||||
<SelectBox
|
||||
labelClass="w-[40px]"
|
||||
className="mr-10 w-[200px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시도"
|
||||
selectArray={sidoData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
||||
value={sidoValue}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[65px]"
|
||||
className="mr-10 w-[200px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시군구"
|
||||
selectArray={filteredSigunData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[140px]"
|
||||
className="mr-3 w-[250px]"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="하수처리시설"
|
||||
selectArray={facilityData}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "fcltCd")}
|
||||
value={facilityValue}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button
|
||||
color="primary"
|
||||
className="mx-2 mt-1 ml-10"
|
||||
size={"sm"}
|
||||
onClick={handleSearch}
|
||||
>
|
||||
조회
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1232,9 +1232,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
}
|
||||
|
||||
if(operationalStatusList[0] && operationalStatusList[7]){
|
||||
operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : data.yearData.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT) * 100) * 10) / 10+'%';
|
||||
operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : data.summer.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer) / Number(data.summer.AVG_INFL_AMNT) * 100) * 10) / 10+'%';
|
||||
operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : data.winter.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter) / Number(data.winter.AVG_INFL_AMNT) * 100) * 10) / 10+'%';
|
||||
operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : data.yearData && data.yearData.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg) / (data.yearData && data.yearData.AVG_INFL_AMNT ? Number(data.yearData.AVG_INFL_AMNT) : 0) * 100) * 10) / 10+'%';
|
||||
operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : data.summer && data.summer.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer) / (data.summer && data.summer.AVG_INFL_AMNT ? Number(data.summer.AVG_INFL_AMNT) : 0) * 100) * 10) / 10+'%';
|
||||
operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : data.winter && data.winter.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter) / (data.winter && data.winter.AVG_INFL_AMNT ? Number(data.winter.AVG_INFL_AMNT) : 0) * 100) * 10) / 10+'%';
|
||||
operationalStatusList[7].rdi = operationalStatusList[0].rdi === null ? '' : data.summerBod && data.summerBod.INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].rdi) / (data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0) * 100) * 10) / 10+'%';
|
||||
/* operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : Number(Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT)) * 100;
|
||||
operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : Number(Number(operationalStatusList[0].groundWaterSummer) / Number(data.summer.AVG_INFL_AMNT)) * 100;
|
||||
|
||||
@@ -446,7 +446,6 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy
|
||||
let maxWaterUsage = 0;
|
||||
let minWaterUsage = 9999999999;
|
||||
let avgWaterUsage = 0;
|
||||
console.log('monthData : ', monthData);
|
||||
monthData.forEach((item, index) => {
|
||||
item.usege = item.sum/item.cnt
|
||||
tableData.push({
|
||||
@@ -465,8 +464,6 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy
|
||||
|
||||
});
|
||||
avgWaterUsage = tot/cnt
|
||||
console.log('avgWaterUsage : ', avgWaterUsage);
|
||||
console.log('tableData : ', tableData);
|
||||
const newData= [
|
||||
{
|
||||
msrDt: '평균',
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 896 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 923 B |
@@ -17,8 +17,6 @@ export class LoginApiService implements LoginService {
|
||||
|
||||
public async login(payload: ILogin): Promise<void> {
|
||||
try {
|
||||
console.log(' ENDPOINT.USER_SERVICE.LOGIN ', ENDPOINT.USER_SERVICE.LOGIN)
|
||||
console.log(payload.userId+' '+payload.password)
|
||||
const response = await fetch( PROXY_HOST+ ENDPOINT.USER_SERVICE.LOGIN, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -5,7 +5,7 @@ NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver #TODO 운영에서 바꿔야 함
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
|
||||
@@ -5,7 +5,7 @@ NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
|
||||
@@ -9,5 +9,5 @@ NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://172.29.143.87:8080/geoserver_swl
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "@/entities/menu";
|
||||
import { useMenuStore } from "@/shared/lib";
|
||||
import useStore from '../store/useStore';
|
||||
|
||||
export default function MapLayout({
|
||||
children,
|
||||
@@ -17,6 +19,14 @@ export default function MapLayout({
|
||||
|
||||
const setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo);
|
||||
|
||||
const { fetchCtpvSggData, fetchBIZ_SE_CD, fetchBIZ_TY_CD } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchCtpvSggData();
|
||||
fetchBIZ_SE_CD();
|
||||
fetchBIZ_TY_CD();
|
||||
}, []);
|
||||
|
||||
const handleMenuClick = (item: MenuItem) => {
|
||||
setCurrentMenuInfo(item);
|
||||
router.push(item.urlPath);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type SelectDataType = {
|
||||
text: string;
|
||||
val: string;
|
||||
sido?: string; // 시도 코드 (선택 사항)
|
||||
}
|
||||
|
||||
interface CityCdStore {
|
||||
cityCd: string;
|
||||
setCityCd: (cityCd: string) => void;
|
||||
sidos: SelectDataType[];
|
||||
setSidos: (sidos: SelectDataType[]) => void;
|
||||
siguns : SelectDataType[];
|
||||
setSiguns: (siguns: SelectDataType[]) => void;
|
||||
}
|
||||
|
||||
export const useCityCdStore = create<CityCdStore>((set) => ({
|
||||
cityCd: '',
|
||||
setCityCd: (cityCd: string) => set({ cityCd }),
|
||||
sidos: [],
|
||||
setSidos: (sidos: SelectDataType[]) => set({ sidos }),
|
||||
siguns: [],
|
||||
setSiguns: (siguns: SelectDataType[]) => set({ siguns }),
|
||||
}));
|
||||
@@ -12,6 +12,9 @@ type LayerComponentProps = {
|
||||
rainMeasureData?: any;
|
||||
onSaveClick?: () => void;
|
||||
onFormDataChange?: (data: any) => void; // 추가
|
||||
chmbrXcnts: string;
|
||||
chmbrYdnts: string;
|
||||
gisObject?: Record<string, any>; // GIS 객체 추가
|
||||
};
|
||||
type LayerContentMap = {
|
||||
[key: string]: {
|
||||
@@ -22,7 +25,7 @@ type LayerContentMap = {
|
||||
// 레이어 이름과 컴포넌트를 매핑
|
||||
// 레이어 선택시 변경되는 팝업 컨텐츠 동적으로 변경하도록 작업
|
||||
const layerContentMap: LayerContentMap = {
|
||||
// "하수처리시설": { component: SwtrmtFclt, }, // 디지털 자료관 연계정보가 없으니 일단 주석 처리
|
||||
"하수처리시설": { component: SwtrmtFclt, }, // 디지털 자료관 연계정보가 없으니 일단 주석 처리
|
||||
"하수처리구역": {},
|
||||
"우수토실": { component: Ssc, },
|
||||
"중점우수토실": { component: Ssc, }, // 중점우수토실 여부 설정
|
||||
|
||||
@@ -28,20 +28,20 @@ export const MsSwagePplCol = [
|
||||
// { id: 'lyrTypeNm', title: '레이어타입명' },
|
||||
]
|
||||
export const MsSwagePplCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: 'fcltMngNo', title: '시설 관리번호' },
|
||||
{ id: 'instMngNo', title: '기관 관리번호' },
|
||||
{ id: 'rowNumber', title: '일련\n번호' },
|
||||
{ id: 'fcltMngNo', title: '시설\n관리번호' },
|
||||
{ id: 'instMngNo', title: '기관\n관리번호' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
// { id: 'mapshtNo', title: '도엽번호' },
|
||||
{ id: 'pdmt', title: '관경(mm)' },
|
||||
{ id: 'prlg', title: '연장(m)' },
|
||||
{ id: 'avgGrdn', title: '평균구배(m/m)' },
|
||||
{ id: 'tmptDpth', title: '시점깊이(m)' },
|
||||
{ id: 'tmptMhlMngNo', title: '시점맨홀 관리번호' },
|
||||
{ id: 'scendDpth', title: '종점깊이(m)' },
|
||||
{ id: 'scendMhlMngNo', title: '종점맨홀 관리번호' },
|
||||
{ id: 'vrtcLen', title: '세로길이(m)' },
|
||||
{ id: 'wdthLen', title: '가로길이(m)' },
|
||||
{ id: 'pdmt', title: '관경\n(mm)' },
|
||||
{ id: 'prlg', title: '연장\n(m)' },
|
||||
{ id: 'avgGrdn', title: '평균구배\n(m/m)' },
|
||||
{ id: 'tmptDpth', title: '시점깊이\n(m)' },
|
||||
{ id: 'tmptMhlMngNo', title: '시점맨홀\n관리번호' },
|
||||
{ id: 'scendDpth', title: '종점깊이\n(m)' },
|
||||
{ id: 'scendMhlMngNo', title: '종점맨홀\n관리번호' },
|
||||
{ id: 'vrtcLen', title: '세로길이\n(m)' },
|
||||
{ id: 'wdthLen', title: '가로길이\n(m)' },
|
||||
// { id: 'fcltyShpCd', title: '시설물형태코드' },
|
||||
// { id: 'fid', title: 'FID' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
@@ -297,9 +297,9 @@ export const SwtrmtFcltCol = [
|
||||
// { id: 'obsrvnSptCd', title: '관측지점코드' },
|
||||
];
|
||||
export const SwtrmtFcltCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
{ id: 'fcltCpct', title: '시설용량(㎥/d)' },
|
||||
{ id: 'rowNumber', title: '일련\n번호' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리\n시설명' },
|
||||
{ id: 'fcltCpct', title: '시설\n용량\n(㎥/d)' },
|
||||
{ id: 'rgnExpln', title: '지역설명' },
|
||||
// { id: 'cmcnYmd', title: '준공일자' },
|
||||
{ id: 'oprtngStrtYmd', title: '가동개시일자' },
|
||||
@@ -311,7 +311,7 @@ export const SwtrmtFcltCol2 = [
|
||||
// { id: 'sggNm', title: '시군구명' },
|
||||
// { id: 'sareaCd', title: '권역코드' },
|
||||
{ id: 'sareaNm', title: '권역명' },
|
||||
{ id: 'lrgeSareaNm', title: '대권역명' },
|
||||
// { id: 'lrgeSareaNm', title: '대권역명' },
|
||||
{ id: 'rvrNm', title: '하천명' },
|
||||
// { id: 'addr', title: '주소' },
|
||||
// { id: 'dtlAddr', title: '상세주소' },
|
||||
@@ -320,19 +320,19 @@ export const SwtrmtFcltCol2 = [
|
||||
{ id: 'dschgWtstNm', title: '방류수계명' },
|
||||
// { id: 'pllnArea', title: '부지면적(m²)' },
|
||||
// { id: 'mngRprtvBzentyNm', title: '관리대행업체명' },
|
||||
{ id: 'swtrmtPpltnCnt', title: '하수처리인구수(인)' },
|
||||
{ id: 'swtrmtPpltnCnt', title: '하수\n처리\n인구수\n(인)' },
|
||||
// { id: 'prcsZoneAreaSum', title: '처리구역면적합계(m²)' },
|
||||
// { id: 'xcrd', title: 'X좌표' },
|
||||
// { id: 'ycrd', title: 'Y좌표' },
|
||||
// { id: 'oprtngSttsCd', title: '가동상태코드' },
|
||||
{ id: 'rprsMsrcCn', title: '대표공법내용' },
|
||||
{ id: 'rprsMsrcCn', title: '대표\n공법\n내용' },
|
||||
// { id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량(mg/L)' },
|
||||
// { id: 'sttyCrtrToc', title: '법정기준TOC(mg/L)' },
|
||||
// { id: 'sttyCrtrSs', title: '법정기준SS(mg/L)' },
|
||||
// { id: 'sttyCrtrTn', title: '법정기준총질소(mg/L)' },
|
||||
// { id: 'sttyCrtrTph', title: '법정기준총인(mg/L)' },
|
||||
// { id: 'sttyCrtrClbclCnt', title: '법정기준대장균수(군수/100mL)' },
|
||||
{ id: 'bsnNm', title: '유역명' },
|
||||
// { id: 'bsnNm', title: '유역명' },
|
||||
// { id: 'obsrvnSptCd', title: '관측지점코드' },
|
||||
];
|
||||
|
||||
@@ -353,9 +353,9 @@ export const SwtrmtPrcsZoneCol2 = [
|
||||
{ id: 'swtrmtZoneNm', title: '하수처리구역명' },
|
||||
// { id: 'swtrmt_fclt_cd', title: '하수처리시설코드' },
|
||||
// { id: 'ctpv_cd', title: '시도코드' },
|
||||
{ id: 'ctpvNm', title: '시도명' },
|
||||
{ id: 'ctpvNm', title: '시도' },
|
||||
// { id: 'sgg_cd', title: '시군구코드' },
|
||||
{ id: 'sggNm', title: '시군구명' },
|
||||
{ id: 'sggNm', title: '시군구' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
];
|
||||
|
||||
@@ -686,5 +686,6 @@ export const numberFields = [
|
||||
'swrPplEqpAr', 'swrTrtPopl', 'weirHg', 'weirLt',
|
||||
'precdngDry', 'rainFall', 'cntncTime', 'rainIn',
|
||||
'mesureIntrvl', 'qltwtrCo', 'rnysnFvlc', 'clarFvlc',
|
||||
'bizAmt', 'count'
|
||||
'bizAmt', 'count',
|
||||
'chmbr_xcnts', 'chmbr_ydnts'
|
||||
];
|
||||
@@ -4,7 +4,7 @@ import React, { useState, useEffect } from 'react'
|
||||
import InputSection from '../InputSection';
|
||||
import { handleInputChange } from '../common';
|
||||
import { getData } from "@/components/lib/api";
|
||||
// import { useBizByFcltStore } from "../../../../store/StoreProvider";
|
||||
import useStore from '../../../../store/useStore';
|
||||
|
||||
// 하수관로
|
||||
const inputColumn = [
|
||||
@@ -30,21 +30,7 @@ const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClic
|
||||
inspection: { data: inspectionData, setData: setInspectionData },
|
||||
};
|
||||
|
||||
|
||||
// const BIZ_SE_CD = useBizByFcltStore(state => state.BIZ_SE_CD);
|
||||
const BIZ_SE_CD = [
|
||||
{ lwpoCd: "100", lwpoCdNm: "관로기술진단" },
|
||||
{ lwpoCd: "200", lwpoCdNm: "개대체" },
|
||||
{ lwpoCd: "201", lwpoCdNm: "유지보수" },
|
||||
{ lwpoCd: "203", lwpoCdNm: "청소, 준설" },
|
||||
{ lwpoCd: "204", lwpoCdNm: "단순보수" },
|
||||
];
|
||||
// const BIZ_TY_CD = useBizByFcltStore(state => state.BIZ_TY_CD);
|
||||
const BIZ_TY_CD = [
|
||||
{ lwpoCd: "SV", lwpoCdNm: "용역" },
|
||||
{ lwpoCd: "SF", lwpoCdNm: "자체" },
|
||||
{ lwpoCd: "SC", lwpoCdNm: "공사" },
|
||||
];
|
||||
const { BIZ_SE_CD, BIZ_TY_CD } = useStore();
|
||||
|
||||
// 마운트 및 업데이트 시 API 호출
|
||||
|
||||
|
||||
@@ -6,26 +6,125 @@ import { untrtSwgRainCol3, untrtSwgRainCol4, untrtSwgRainCol5 } from '../common/
|
||||
import { VerticalTable } from '@/components/ui/verticalTable';
|
||||
import { getData } from "@/components/lib/api";
|
||||
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
import { Label } from '@radix-ui/react-label';
|
||||
|
||||
const inputColumn = [
|
||||
{ id: "prrtymngYn", title: "중점여부", type: "select", options: ["우수토실", "중점우수토실"] },
|
||||
{ id: "swtrmtZoneCd", title: "처리구역", type: "input" },
|
||||
];
|
||||
|
||||
const Ssc = ({ featureData, isMainSsc, rainMeasureData, onSaveClick }: {
|
||||
const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
|
||||
featureData: any,
|
||||
isMainSsc?: boolean,
|
||||
rainMeasureData?: any,
|
||||
chmbrXcnts?: string,
|
||||
chmbrYdnts?: string,
|
||||
onSaveClick?: () => void
|
||||
}) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||
const [formData, setFormData] = useState<any>({});
|
||||
const [mntrngsData, setMntrngsData] = useState<any[]>([]); // 수량/수질 측정 정보
|
||||
const [rainMeasureData, setRainMeasureData] = useState<any>({}); // 강우, 측정 정보
|
||||
const [mntrngsData, setMntrngsData] = useState<any[]>([]); // 수량/수질측정 정보
|
||||
const [totalCount, setTotalCount] = useState<number>(0);
|
||||
const [totalDetailCount, setTotalDetailCount] = useState<number>(0);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number>(-1);
|
||||
const [curPage, setCurPage] = useState<number>(1);
|
||||
const [curDetailPage, setCurDetailPage] = useState<number>(1);
|
||||
const [isResetPage, setIsResetPage] = useState<boolean>(true);
|
||||
const [isResetDetailPage, setIsResetDetailPage] = useState<boolean>(true);
|
||||
const [isResetSelection, setIsResetSelection] = useState<boolean>(false);
|
||||
|
||||
// console.log('featureData data received:', featureData); // 추가 데이터 확인용
|
||||
// console.log('rainMeasureData received:', rainMeasureData); // 추가 데이터 확인용
|
||||
console.log('featureData', featureData);
|
||||
|
||||
// 강우, 측정 정보 API 호출 함수
|
||||
const fetchRainMeasureData = async (chmbrXcnts: string, chmbrYdnts: string) => {
|
||||
setIsResetDetailPage(true);
|
||||
setIsResetSelection(true);
|
||||
if (!chmbrXcnts || !chmbrYdnts) {
|
||||
console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { chmbrXcnts, chmbrYdnts });
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
chmbrXcnts: chmbrXcnts,
|
||||
chmbrYdnts: chmbrYdnts,
|
||||
page: `${curPage - 1}`,
|
||||
size: '3',
|
||||
});
|
||||
|
||||
console.log('queryParams', queryParams.toString());
|
||||
console.log(`추가 데이터 API 호출 시작: v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`);
|
||||
|
||||
const result = await getData(`v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`);
|
||||
|
||||
console.log('추가 데이터 API 호출 성공:', result);
|
||||
|
||||
const processedDataArray: any[] = result.content.map((data: any) => {
|
||||
// 🎯 formatDisplayData로 기본 포맷팅 적용
|
||||
const formattedItem = formatDisplayData(data);
|
||||
|
||||
// 🎯 추가 데이터 조합
|
||||
return {
|
||||
...formattedItem,
|
||||
flwmtrCombined: `${data.flwmtrMesureTpEtc || '-'} / ${data.flwmtrKnd || '-'}`,
|
||||
};
|
||||
});
|
||||
setRainMeasureData(processedDataArray);
|
||||
setTotalCount(result?.totalElements || 0);
|
||||
} catch (error) {
|
||||
console.error('추가 데이터 로딩 실패:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// 수량/수질측정 정보 API 호출 함수
|
||||
const fetchMntrngsData = async (adstDstrCd: string, manageNo: string) => {
|
||||
setIsResetDetailPage(true);
|
||||
if (!adstDstrCd || !manageNo) {
|
||||
console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { adstDstrCd, manageNo });
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
page: `${curDetailPage - 1}`,
|
||||
size: '3',
|
||||
});
|
||||
|
||||
console.log(`SSC 추가 데이터 API 호출 시작: v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`);
|
||||
|
||||
const result = await getData(`v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`);
|
||||
|
||||
console.log('SSC 추가 데이터 API 호출 성공:', result);
|
||||
|
||||
// 각 아이템 전처리
|
||||
const processedDataArray = result.content.map((data: any) => {
|
||||
// 기본 포맷팅 적용
|
||||
const formattedItem = formatDisplayData(data);
|
||||
return {
|
||||
...formattedItem,
|
||||
};
|
||||
});
|
||||
|
||||
console.log('processedDataArray', processedDataArray);
|
||||
|
||||
setMntrngsData(processedDataArray);
|
||||
setTotalDetailCount(result?.totalElements || 0);
|
||||
} catch (error) {
|
||||
console.error('추가 데이터 로딩 실패:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 🎯 추가 데이터는 별도 useEffect로 비동기 로딩
|
||||
useEffect(() => {
|
||||
// 강우/측정 정보 데이터 호출
|
||||
if (isMainSsc && chmbrXcnts && chmbrYdnts) {
|
||||
fetchRainMeasureData(chmbrXcnts, chmbrYdnts)
|
||||
}
|
||||
}, [chmbrXcnts, chmbrYdnts]);
|
||||
|
||||
const openModal = () => {
|
||||
setIsModalOpen(true);
|
||||
@@ -46,6 +145,19 @@ const Ssc = ({ featureData, isMainSsc, rainMeasureData, onSaveClick }: {
|
||||
}
|
||||
}, [featureData]);
|
||||
|
||||
// featureData가 변경될 때 formData 초기화
|
||||
useEffect(() => {
|
||||
// showTable이 true일 때만 fetch (최초 렌더 방지)
|
||||
setIsResetDetailPage(false);
|
||||
setSelectedRowIndex(-1);
|
||||
if (chmbrXcnts && chmbrYdnts) fetchRainMeasureData(chmbrXcnts, chmbrYdnts);
|
||||
}, [curPage]);
|
||||
|
||||
// 페이지 변경 시마다 데이터 fetch
|
||||
useEffect(() => {
|
||||
if (chmbrXcnts && chmbrYdnts && selectedRowIndex !== -1) fetchMntrngsData(rainMeasureData[selectedRowIndex].adstDstrCd, rainMeasureData[selectedRowIndex].manageNo);
|
||||
}, [curDetailPage]);
|
||||
|
||||
// 폼 데이터가 변경될 때 호출될 핸들러
|
||||
const handleFormDataChange = (newFormData: any) => {
|
||||
setFormData(newFormData);
|
||||
@@ -62,120 +174,70 @@ const Ssc = ({ featureData, isMainSsc, rainMeasureData, onSaveClick }: {
|
||||
// 테이블 셀 클릭 핸들러 추가
|
||||
const handleCellClick = async (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && rainMeasureData) {
|
||||
const data = await fetchMntrngsData(rainMeasureData[index].adstDstrCd, rainMeasureData[index].manageNo);
|
||||
|
||||
setMntrngsData(data);
|
||||
await fetchMntrngsData(rainMeasureData[index].adstDstrCd, rainMeasureData[index].manageNo);
|
||||
setSelectedRowIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
// 🎯 추가 데이터 API 호출 함수 (독립적으로 실행)
|
||||
const fetchMntrngsData = useCallback(async (adstDstrCd: string, manageNo: string) => {
|
||||
if (!adstDstrCd || !manageNo) {
|
||||
console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { adstDstrCd, manageNo });
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
pageable: `{}`,
|
||||
});
|
||||
|
||||
console.log(`SSC 추가 데이터 API 호출 시작: v1/onsemiro/swgRain?${queryParams.toString()}`);
|
||||
|
||||
const result = await getData(`v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`);
|
||||
|
||||
console.log('SSC 추가 데이터 API 호출 성공:', result);
|
||||
|
||||
// 🎯 result가 페이지네이션 객체인 경우 content 배열 추출
|
||||
let dataArray: any[] = [];
|
||||
if (result && typeof result === 'object' && 'content' in result && Array.isArray(result.content)) {
|
||||
dataArray = result.content;
|
||||
} else if (Array.isArray(result)) {
|
||||
dataArray = result;
|
||||
} else {
|
||||
console.warn('예상치 못한 API 응답 형식:', result);
|
||||
return [];
|
||||
}
|
||||
|
||||
// 🎯 각 아이템을 전처리하여 반환
|
||||
const processedDataArray = dataArray.map((data: any) => {
|
||||
// 🎯 formatDisplayData로 기본 포맷팅 적용
|
||||
const formattedItem = formatDisplayData(data);
|
||||
|
||||
// 🎯 추가 데이터 조합
|
||||
return {
|
||||
...formattedItem,
|
||||
};
|
||||
});
|
||||
|
||||
console.log('processedDataArray', processedDataArray);
|
||||
|
||||
return processedDataArray;
|
||||
} catch (error) {
|
||||
console.error('추가 데이터 로딩 실패:', error);
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
|
||||
const renderModalBody = () => (
|
||||
<div className='grid grid-cols-1 gap-4 p-4'>
|
||||
{/* 강우정보 테이블 */}
|
||||
<UiTable
|
||||
headName='강우정보'
|
||||
tableData={Array.isArray(rainMeasureData) ? rainMeasureData : []}
|
||||
headlist={untrtSwgRainCol3}
|
||||
pageSize={10}
|
||||
total={Array.isArray(rainMeasureData) ? rainMeasureData.length : 0}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={function (page: number): void {
|
||||
console.log('Rain data page change:', page);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<Label className='text-3xl font-medium text-label '>강우/측정 정보</Label>
|
||||
{/* 강우정보 테이블 */}
|
||||
<UiTable
|
||||
headName='강우정보'
|
||||
tableData={rainMeasureData}
|
||||
headlist={untrtSwgRainCol3}
|
||||
pageSize={3}
|
||||
total={0}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={setCurPage}
|
||||
resetPageNo={isResetPage}
|
||||
resetSelection={isResetSelection}
|
||||
index={selectedRowIndex}
|
||||
/>
|
||||
|
||||
{/* 측정정보 테이블 */}
|
||||
<UiTable
|
||||
headName='측정정보'
|
||||
tableData={Array.isArray(rainMeasureData) ? rainMeasureData : []}
|
||||
headlist={untrtSwgRainCol4}
|
||||
pageSize={10}
|
||||
total={Array.isArray(rainMeasureData) ? rainMeasureData.length : 0}
|
||||
cellClick={handleCellClick}
|
||||
// onPageChange={setCurPage}
|
||||
onPageChange={function (page: number): void {
|
||||
console.log('Rain data page change:', page);
|
||||
}}
|
||||
resetPageNo={false}
|
||||
// index={curPage}
|
||||
>
|
||||
{/* 커스텀 헤더 */}
|
||||
<TableRow>
|
||||
<TableHead colSpan={5} className="text-center bg-blue-100 font-bold">수량</TableHead>
|
||||
<TableHead colSpan={3} className="text-center bg-blue-100 font-bold">수질</TableHead>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">측정시작일시</TableHead>
|
||||
<TableHead className="text-center">측정종료일시</TableHead>
|
||||
<TableHead className="text-center">측정간격(분)</TableHead>
|
||||
<TableHead className="text-center">측정기기설치위치</TableHead>
|
||||
<TableHead className="text-center">유량계 종류 및 모델명</TableHead>
|
||||
<TableHead className="text-center">채수시작일시</TableHead>
|
||||
<TableHead className="text-center">채수종료일시</TableHead>
|
||||
<TableHead className="text-center">채수횟수</TableHead>
|
||||
</TableRow>
|
||||
</UiTable>
|
||||
{/* 측정정보 테이블 */}
|
||||
<UiTable
|
||||
headName='측정정보'
|
||||
tableData={rainMeasureData}
|
||||
headlist={untrtSwgRainCol4}
|
||||
pageSize={3}
|
||||
total={totalCount}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={setCurPage}
|
||||
resetPageNo={isResetPage}
|
||||
resetSelection={isResetSelection}
|
||||
index={selectedRowIndex}
|
||||
>
|
||||
{/* 커스텀 헤더 */}
|
||||
<TableRow>
|
||||
<TableHead colSpan={5} className="text-center bg-blue-100 font-bold">수량</TableHead>
|
||||
<TableHead colSpan={3} className="text-center bg-blue-100 font-bold">수질</TableHead>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">측정시작일시</TableHead>
|
||||
<TableHead className="text-center">측정종료일시</TableHead>
|
||||
<TableHead className="text-center">측정간격(분)</TableHead>
|
||||
<TableHead className="text-center">측정기기설치위치</TableHead>
|
||||
<TableHead className="text-center">유량계 종류 및 모델명</TableHead>
|
||||
<TableHead className="text-center">채수시작일시</TableHead>
|
||||
<TableHead className="text-center">채수종료일시</TableHead>
|
||||
<TableHead className="text-center">채수횟수</TableHead>
|
||||
</TableRow>
|
||||
</UiTable>
|
||||
</div>
|
||||
|
||||
{/* 수량/수질측정 정보 테이블 */}
|
||||
<UiTable
|
||||
headName='수량/수질측정 정보'
|
||||
tableData={Array.isArray(mntrngsData) ? mntrngsData : []}
|
||||
tableData={mntrngsData}
|
||||
headlist={untrtSwgRainCol5}
|
||||
pageSize={10}
|
||||
total={Array.isArray(mntrngsData) ? mntrngsData.length : 0}
|
||||
pageSize={3}
|
||||
total={totalDetailCount}
|
||||
cellClick={undefined}
|
||||
onPageChange={function (page: number): void {
|
||||
console.log('Quality data page change:', page);
|
||||
}}
|
||||
onPageChange={setCurDetailPage}
|
||||
resetPageNo={isResetDetailPage}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use client'
|
||||
import React, { useState,useEffect } from 'react'
|
||||
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'
|
||||
import { Button } from '@common/components/ui';
|
||||
import { useDcTree, flattenFolders, getFileDownload, addFileName, type FlattenedFolder} from '../../searchModal/searchModalReportApi';
|
||||
|
||||
// 하수처리시설
|
||||
const headList = [
|
||||
{ id: "title", title: "하수도정비 기본계획보고서" },
|
||||
{ id: "cad", title: "하수도대장 평면도" },]
|
||||
];
|
||||
const modalHeadList = [
|
||||
{ id: "no", title: "번호" },
|
||||
{ id: "year", title: "승인연도" },
|
||||
@@ -16,9 +18,10 @@ const modalHeadList = [
|
||||
{ id: "date", title: "입력시간" },
|
||||
]
|
||||
|
||||
const SwtrmtFclt = () => {
|
||||
const SwtrmtFclt = ({ gisObject }: { gisObject: any }) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [modalTitle, setModalTitle] = useState<string>('');
|
||||
const [dcDataOne, setDcDataOne] = useState<FlattenedFolder|undefined>({});
|
||||
const openModal = (title: string) => {
|
||||
setIsModalOpen(true);
|
||||
setModalTitle(title)
|
||||
@@ -26,22 +29,59 @@ const SwtrmtFclt = () => {
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
const ctpvNm = gisObject?.ctpv_nm;
|
||||
//const reportOriginData = useDcTree(ctpvNm);
|
||||
const reportOriginData = useDcTree("강원특별자치도");
|
||||
useEffect(() => {
|
||||
const fetch = async () => {
|
||||
|
||||
if (!reportOriginData || reportOriginData.length === 0) {
|
||||
return;
|
||||
}
|
||||
const reportData = flattenFolders(reportOriginData);
|
||||
let filterReportData = [...reportData];
|
||||
if (gisObject && gisObject.sgg_nm !== undefined && gisObject.sgg_nm !== "") { // 시군구검색
|
||||
//const sggNm = gisObject?.sgg_nm;
|
||||
const sggNm = "강릉시";
|
||||
filterReportData = filterReportData.filter(item => item.sigungu === sggNm);
|
||||
}
|
||||
addFileName(filterReportData).then(data => {
|
||||
filterReportData = data.filter(item => item.filePath !== undefined && item.filePath !== "");
|
||||
if (filterReportData !== undefined && filterReportData.length > 0) {
|
||||
setDcDataOne(filterReportData[0]);
|
||||
}
|
||||
});
|
||||
console.log("filterReportData", filterReportData);
|
||||
};
|
||||
fetch();
|
||||
}, [reportOriginData]);
|
||||
|
||||
// 하수도정비기본계획 filedownload 핸들러
|
||||
const handleFileDownloadClick = () => {
|
||||
if (dcDataOne && dcDataOne !== undefined && dcDataOne.fileSn !== undefined && dcDataOne.fileSn !== ""
|
||||
&& dcDataOne.filePath !== undefined && dcDataOne.filePath !== "" && dcDataOne.fileName !== undefined && dcDataOne.fileName !== "" ) {
|
||||
console.log("dcDataOne", dcDataOne);
|
||||
getFileDownload(String(dcDataOne.fileSn), String(dcDataOne.fileName), String(dcDataOne.filePath));
|
||||
}
|
||||
};
|
||||
|
||||
//cad: <Button onClick={() => openModal('하수도대장 평면도')}>평면도</Button>,
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable children={{
|
||||
title: <Button onClick={() => openModal('하수도정비 기본계획보고서')}>보고서</Button>,
|
||||
cad: <Button onClick={() => openModal('하수도대장 평면도')}>평면도</Button>,
|
||||
<VerticalTable
|
||||
children={{
|
||||
title: <Button className="gray" onClick={() => handleFileDownloadClick()}>다운로드</Button>
|
||||
}} headName='디지털 자료관 연계정보' tableData={[]} headlist={headList} pageSize={20} total={0} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
|
||||
{/* <Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
|
||||
<div className='grid grid-col-2'>
|
||||
<UiTable headName={modalTitle} tableData={[]} headlist={modalHeadList} pageSize={20} total={0} cellClick={undefined} children={undefined} onPageChange={function (page: number): void {
|
||||
<UiTable headName={modalTitle} tableData={[]} headlist={modalHeadList} pageSize={20} total={0} cellClick={undefined} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}></UiTable>
|
||||
</div>
|
||||
</Modal>
|
||||
</Modal> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import { VerticalTable } from "@/components/ui/verticalTable";
|
||||
import { useGisContext } from "@common/business_components/ui/map/Gis";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { useEffect, useRef, useState, useCallback, useMemo } from "react";
|
||||
import { getLayerContentComponent } from "./components/common";
|
||||
import { SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "./components/common/table";
|
||||
import InfilChart from './components/popupComponent/chart/infilChart';
|
||||
import RdiiChart from './components/popupComponent/chart/rdiiChart';
|
||||
import { useInfilChartModalStore, useRdiiChartModalStore } from "./components/popupComponent/chartModalStore";
|
||||
import { getLayerContentComponent } from "../common";
|
||||
import { SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "../common/table";
|
||||
import InfilChart from './chart/infilChart';
|
||||
import RdiiChart from './chart/rdiiChart';
|
||||
import { useInfilChartModalStore, useRdiiChartModalStore } from "./chartModalStore";
|
||||
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
import { codeMapper } from "./components/common/codeMapper";
|
||||
import { codeMapper } from "../common/codeMapper";
|
||||
import { searchUpperPpls } from '@common/business_components/ui/map/Gis';
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar';
|
||||
|
||||
@@ -21,7 +21,6 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
|
||||
const [layerSpecificNm, setLayerSpecificNm] = useState<string>(""); // 레이어 이름 상태
|
||||
const [tableData, setTableData] = useState<any>(null); // 피처 데이터
|
||||
const [rainMeasureData, setRainMeasureData] = useState<any[]>([]); // 강우/측정 측정 정보
|
||||
const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
|
||||
const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
|
||||
const [buttonChk, setButtonChk] = useState(true)
|
||||
@@ -33,18 +32,17 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
// 🎯 InfilChart 및 RdiiChart 관련 상태 추가
|
||||
const { setIsOpen: setIsModalInfil } = useInfilChartModalStore();
|
||||
const { setIsOpen: setIsModalRdii } = useRdiiChartModalStore();
|
||||
const [curPage, setCurPage] = useState(1); // 현재 페이지 상태
|
||||
const [isClicked, setIsClicked] = useState(true); // 클릭 여부 상태
|
||||
const [isLoading, setIsLoading] = useState(false); // 로딩 상태
|
||||
const [chmbrXcnts, setChmbrXcnts] = useState<string>(''); // 로딩 상태
|
||||
const [chmbrYdnts, setChmbrYdnts] = useState<string>(''); // 로딩 상태
|
||||
|
||||
// 🎯 관로관리번호 추출
|
||||
const pplMngNo = featureData?.values_?.fclt_mng_no || "";
|
||||
// const featureId = featureData.getId(); // 피처의 id 가져오기
|
||||
|
||||
const featureId = typeof featureData.getId === 'function'
|
||||
? featureData.getId()
|
||||
: featureData.fid;
|
||||
console.log('featureId', featureId);
|
||||
|
||||
// 🎯 데이터 전처리 메모이제이션
|
||||
const processedData = useMemo(() => {
|
||||
@@ -263,18 +261,9 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
data: { ...processedFormattedData }
|
||||
}));
|
||||
|
||||
// 강우/측정 정보 데이터 호출
|
||||
if (tableData?.chmbr_xcnts && tableData?.chmbr_ydnts) {
|
||||
fetchRainMeasureData(tableData.chmbr_xcnts, tableData.chmbr_ydnts)
|
||||
.then(response => {
|
||||
console.log('additionalResponse', response);
|
||||
setRainMeasureData(response || []);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('추가 데이터 로딩 실패:', error);
|
||||
setRainMeasureData([]);
|
||||
});
|
||||
}
|
||||
setChmbrXcnts(data.chmbr_xcnts);
|
||||
setChmbrYdnts(data.chmbr_ydnts);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -317,76 +306,6 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
fetchData();
|
||||
}, [featureId, processedData]);
|
||||
|
||||
// 🎯 추가 데이터 API 호출 함수
|
||||
const fetchRainMeasureData = useCallback(async (chmbrXcnts: string, chmbrYdnts: string) => {
|
||||
if (!chmbrXcnts || !chmbrYdnts) {
|
||||
console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { chmbrXcnts, chmbrYdnts });
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
chmbrXcnts: chmbrXcnts,
|
||||
chmbrYdnts: chmbrYdnts,
|
||||
pageable: `{}`,
|
||||
});
|
||||
|
||||
console.log(`추가 데이터 API 호출 시작: v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`);
|
||||
|
||||
const result = await getData(`v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`);
|
||||
|
||||
console.log('추가 데이터 API 호출 성공:', result);
|
||||
|
||||
// 🎯 result가 페이지네이션 객체인 경우 content 배열 추출
|
||||
let dataArray: any[] = [];
|
||||
if (result && typeof result === 'object' && 'content' in result && Array.isArray(result.content)) {
|
||||
dataArray = result.content;
|
||||
} else if (Array.isArray(result)) {
|
||||
dataArray = result;
|
||||
} else {
|
||||
console.warn('예상치 못한 API 응답 형식:', result);
|
||||
return [];
|
||||
}
|
||||
|
||||
// 🎯 각 아이템을 전처리하여 반환
|
||||
const processedDataArray = dataArray.map((data: any) => {
|
||||
// 🎯 formatDisplayData로 기본 포맷팅 적용
|
||||
const formattedItem = formatDisplayData(data);
|
||||
|
||||
// 🎯 추가 데이터 조합
|
||||
return {
|
||||
...formattedItem,
|
||||
flwmtrCombined: `${data.flwmtrMesureTpEtc || '-'} / ${data.flwmtrKnd || '-'}`,
|
||||
};
|
||||
});
|
||||
|
||||
return processedDataArray;
|
||||
} catch (error) {
|
||||
console.error('추가 데이터 로딩 실패:', error);
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 🎯 추가 데이터는 별도 useEffect로 비동기 로딩
|
||||
useEffect(() => {
|
||||
if (layerNm === '중점우수토실' && featureData?.values_) {
|
||||
const data = featureData.values_;
|
||||
|
||||
if (data?.chmbr_xcnts && data?.chmbr_ydnts) {
|
||||
// 🎯 추가 데이터를 비동기로 로딩 (UI 블로킹 없음)
|
||||
fetchRainMeasureData(data.chmbr_xcnts, data.chmbr_ydnts)
|
||||
.then(response => {
|
||||
console.log('추가 데이터 로딩 완료:', response);
|
||||
setRainMeasureData(response || []);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('추가 데이터 최종 로딩 실패:', error);
|
||||
setRainMeasureData([]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [layerNm, featureData?.values_, fetchRainMeasureData]);
|
||||
|
||||
// 저장 시 모달 띄우기
|
||||
const handleSaveClick = () => {
|
||||
setIsModalOpen(true); // 모달 열기
|
||||
@@ -434,7 +353,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
else if (layerNm === '하수펌프장') apiEndpoint = 'v1/fcltpump';
|
||||
else if (layerNm === '하수맨홀') apiEndpoint = 'v1/fcltmhl';
|
||||
|
||||
bodyData['fid'] = featureId.toString().split('.').pop() || null;
|
||||
bodyData['fcltMngNo'] = featureData.values_.fclt_mng_no.toString() || null;
|
||||
// 빈 키 제거
|
||||
if (bodyData[""] !== undefined) delete bodyData[""];
|
||||
|
||||
@@ -484,7 +403,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
{/* 🎯 하수관로일 때만 Infil/RDII 버튼 표시 */}
|
||||
{/* 하수관로일 때만 Infil/RDII 버튼 표시 */}
|
||||
{layerNm === '하수관로' && (
|
||||
<>
|
||||
<Button onClick={() => setIsModalInfil(true)}
|
||||
@@ -520,9 +439,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
</div>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
// className="text-white hover:text-gray-300 focus:outline-none"
|
||||
className="flex items-center space-x-4 text-white hover:text-gray-300 focus:outline-none"
|
||||
// className="absolute top-8 right-4 text-white hover:text-gray-300 focus:outline-none"
|
||||
size="xs"
|
||||
>
|
||||
✕
|
||||
@@ -555,13 +472,14 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
featureData={fetchData}
|
||||
isMainSsc={isMainSsc}
|
||||
isDrtwSewagePump={isDrtwSewagePump}
|
||||
rainMeasureData={rainMeasureData}
|
||||
onSaveClick={handleSaveClick}
|
||||
chmbrXcnts={chmbrXcnts}
|
||||
chmbrYdnts={chmbrYdnts}
|
||||
gisObject={featureData.values_}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
{/* 🎯 InfilChart 및 RdiiChart 컴포넌트 추가 */}
|
||||
<InfilChart pplMngNo={pplMngNo} />
|
||||
<RdiiChart pplMngNo={pplMngNo} />
|
||||
</div>
|
||||
@@ -5,7 +5,9 @@ import { getData } from "../../components/lib/api";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
import SearchModal from "./searchModal/searchModal";
|
||||
import FeatureClickEventPopup from "./featureClickEventPopup";
|
||||
import FeatureClickEventPopup from "./components/popupComponent/featureClickEventPopup";
|
||||
import { SearchBar } from "./searchBar/searchBar";
|
||||
import { WaterDepth } from "./waterDepth/waterDepth";
|
||||
|
||||
// 타입 정의 추가
|
||||
export type LyrStngProps = {
|
||||
@@ -39,6 +41,11 @@ const Map = () => {
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
|
||||
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getData("v1/map/lyrStngs/SN_NETWORK");
|
||||
@@ -48,9 +55,6 @@ const Map = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
// SearchModal, GisContent 등에서 호출
|
||||
const onFeatureSelect = (data: any) => {
|
||||
let map = gisMaps.current;
|
||||
@@ -113,6 +117,8 @@ const Map = () => {
|
||||
setIsOpen(true);
|
||||
setIsSearchModalOpen(false);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
@@ -130,6 +136,9 @@ const Map = () => {
|
||||
isOpen={isOpen}
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={undefined}
|
||||
searchBar={SearchBar}
|
||||
// waterDepth={WaterDepth}
|
||||
waterDepth={undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { CtpvSggPrcsFcltSelectBox } from "@/components/ui/ctpvSggPrcsFcltSelectBox";
|
||||
|
||||
|
||||
export function SearchBar() {
|
||||
const [ctpv, setCtpv] = useState<number>(0);
|
||||
const [sgg, setSgg] = useState<number>(0);
|
||||
const [sggName, setSggName] = useState<string>('');
|
||||
const [selectedSwagePrcsFclt, setSelectedSwagePrcsFclt] = useState<string | number>('0');
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-4 bg-white min-h-[48px] min-w-[600px] px-5 py-3 rounded-lg shadow">
|
||||
<CtpvSggPrcsFcltSelectBox
|
||||
sido={ctpv}
|
||||
sigungu={sgg}
|
||||
sggName={sggName}
|
||||
onSidoChange={(sidoId) => {
|
||||
setCtpv(sidoId);
|
||||
setSgg(0);
|
||||
}}
|
||||
onSigunguChange={setSgg}
|
||||
setSggName={setSggName}
|
||||
selectedSwagePrcsFclt={selectedSwagePrcsFclt}
|
||||
onSwagePrcsFcltChange={setSelectedSwagePrcsFclt}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MS_SWAGE_MHL_USE_CODES, MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES } from '../components/common/table';
|
||||
import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox';
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
@@ -21,7 +19,6 @@ export const BASIC_TYPES = [
|
||||
];
|
||||
|
||||
interface BasicTabProps {
|
||||
open?: boolean;
|
||||
basicType: string;
|
||||
city: number;
|
||||
sgg: number;
|
||||
@@ -33,13 +30,11 @@ interface BasicTabProps {
|
||||
var2: string;
|
||||
yearFilter: { start: string; end: string; };
|
||||
handleSearch: () => void;
|
||||
// onOpenChange?: (open: boolean) => void;
|
||||
// onFeatureSelect?: (row: any) => void;
|
||||
// setTableData?: (data: any[]) => void;
|
||||
// setTableCount?: (count: number) => void;
|
||||
setBasicType: (type: string) => void;
|
||||
setCity: (city: number) => void;
|
||||
setCtpvName: (name: string) => void;
|
||||
setSgg: (sgg: number) => void;
|
||||
setSggName: (name: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
setStart1: (value: string) => void;
|
||||
setStart2: (value: string) => void;
|
||||
@@ -50,7 +45,6 @@ interface BasicTabProps {
|
||||
}
|
||||
|
||||
const BasicTab: React.FC<BasicTabProps> = ({
|
||||
// open,
|
||||
basicType,
|
||||
city,
|
||||
sgg,
|
||||
@@ -62,13 +56,11 @@ const BasicTab: React.FC<BasicTabProps> = ({
|
||||
var2,
|
||||
yearFilter,
|
||||
handleSearch,
|
||||
// onOpenChange,
|
||||
// onFeatureSelect,
|
||||
// setTableData,
|
||||
// setTableCount,
|
||||
setBasicType,
|
||||
setCity,
|
||||
setCtpvName,
|
||||
setSgg,
|
||||
setSggName,
|
||||
setYearFilter,
|
||||
setStart1,
|
||||
setStart2,
|
||||
@@ -78,7 +70,7 @@ const BasicTab: React.FC<BasicTabProps> = ({
|
||||
setVar2,
|
||||
}) => {
|
||||
return (
|
||||
<div className="relative border rounded-lg p-6 w-full h-[10vh]">
|
||||
<div className="relative border rounded-lg p-5 w-full h-[10vh]">
|
||||
{/* 구분 */}
|
||||
<div className="mb-3 flex items-center justify-between gap-4">
|
||||
<div className="flex items-center">
|
||||
@@ -124,12 +116,18 @@ const BasicTab: React.FC<BasicTabProps> = ({
|
||||
start2={start2}
|
||||
end1={end1}
|
||||
end2={end2}
|
||||
city={city}
|
||||
sgg={sgg}
|
||||
yearFilter={yearFilter}
|
||||
setVar1={setVar1}
|
||||
setStart1={setStart1}
|
||||
setStart2={setStart2}
|
||||
setEnd1={setEnd1}
|
||||
setEnd2={setEnd2}
|
||||
setCity={setCity}
|
||||
setCtpvName={setCtpvName}
|
||||
setSgg={setSgg}
|
||||
setSggName={setSggName}
|
||||
setYearFilter={setYearFilter}
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
|
||||
@@ -1,40 +1,15 @@
|
||||
import React from 'react';
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
|
||||
// import { useBizByFcltStore } from "../../../store/StoreProvider";
|
||||
import useStore from '../../../store/useStore';
|
||||
|
||||
// 운영정보 '구분' 선택 목록
|
||||
// const WORK_TYPES = [
|
||||
// { val: "maintenance", text: "보수 및 갱신" },
|
||||
// { val: "cleaning", text: "청소 및 준설" },
|
||||
// { val: "inspection", text: "점검 및 진단" },
|
||||
// ];
|
||||
export const WORK_TYPES = [
|
||||
{ val: "['200', '201', '204']", text: "보수 및 갱신" },
|
||||
{ val: "['100']", text: "점검 및 진단" },
|
||||
{ val: "['203']", text: "청소 및 준설" },
|
||||
];
|
||||
// const BIZ_SE_CD = useBizByFcltStore(state => state.BIZ_SE_CD);
|
||||
export const BIZ_SE_CD = [
|
||||
{ lwpoCd: "100", lwpoCdNm: "관로기술진단" },
|
||||
{ lwpoCd: "200", lwpoCdNm: "개대체" },
|
||||
{ lwpoCd: "201", lwpoCdNm: "유지보수" },
|
||||
{ lwpoCd: "203", lwpoCdNm: "청소, 준설" },
|
||||
{ lwpoCd: "204", lwpoCdNm: "단순보수" },
|
||||
];
|
||||
// const BIZ_TY_CD = useBizByFcltStore(state => state.BIZ_TY_CD);
|
||||
export const BIZ_TY_CD = [
|
||||
{ lwpoCd: "0", lwpoCdNm: "전체" },
|
||||
{ lwpoCd: "SV", lwpoCdNm: "용역" },
|
||||
{ lwpoCd: "SF", lwpoCdNm: "자체" },
|
||||
{ lwpoCd: "SC", lwpoCdNm: "공사" },
|
||||
];
|
||||
export const BIZ_TY_CD_SELECT_ARRAY = BIZ_TY_CD.map(item => ({
|
||||
val: item.lwpoCd,
|
||||
text: item.lwpoCdNm,
|
||||
}));
|
||||
// 운영정보 - 사업 현황
|
||||
export const operationTableHeadlist = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
@@ -80,15 +55,11 @@ export const operationInspectionTableHeadlist = [
|
||||
];
|
||||
|
||||
interface OperationTabProps {
|
||||
open: boolean;
|
||||
workType: string;
|
||||
var1: string;
|
||||
var2: string;
|
||||
yearFilter: { start: string; end: string; };
|
||||
handleSearch: () => void;
|
||||
// onOpenChange: (open: boolean) => void;
|
||||
// setTableData: (data: any[]) => void;
|
||||
// setTableCount: (count: number) => void;
|
||||
setWorkType: (type: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
setVar1: (value: string) => void;
|
||||
@@ -96,26 +67,30 @@ interface OperationTabProps {
|
||||
}
|
||||
|
||||
const OperationTab: React.FC<OperationTabProps> = ({
|
||||
open,
|
||||
workType,
|
||||
var1,
|
||||
var2,
|
||||
yearFilter,
|
||||
handleSearch,
|
||||
// onOpenChange,
|
||||
// setTableData,
|
||||
// setTableCount,
|
||||
setWorkType,
|
||||
setVar1,
|
||||
setVar2,
|
||||
setYearFilter,
|
||||
}) => {
|
||||
|
||||
const { BIZ_TY_CD } = useStore();
|
||||
|
||||
const BIZ_TY_CD_SELECT_ARRAY = BIZ_TY_CD.map(item => ({
|
||||
val: item.lwpoCd,
|
||||
text: item.lwpoCdNm,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 w-full h-[10vh]">
|
||||
<div className="relative flex flex-col border rounded-lg p-5 w-full h-[10vh]">
|
||||
<div className="flex items-center mb-3">
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">업무구분</Label>
|
||||
<SelectBox
|
||||
className="w-80 mr-8"
|
||||
className="w-40 mr-8"
|
||||
selectClass="rounded-lg bg-white text-xl"
|
||||
label=""
|
||||
selectArray={WORK_TYPES.map((w) => ({
|
||||
|
||||
@@ -165,7 +165,7 @@ const ReportTab: React.FC<BasicTabProps> = ({ open }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex flex-col border rounded-lg p-6 w-full h-[10vh]">
|
||||
<div className="relative flex flex-col border rounded-lg p-5 w-full h-[10vh]">
|
||||
<div className="flex items-center mb-3">
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시도</Label>
|
||||
<SelectBox className="w-60 mr-8"
|
||||
|
||||
@@ -3,24 +3,22 @@ import { UiModal, UITab, UiTable } from '@common/business_components/ui';
|
||||
import axios from 'axios';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { ExclnSewagePumpCol2, MsSwageMhlCol2, MsSwagePplCol2, stofcbrCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2 } from '../components/common/table';
|
||||
import { type SelectDataType } from '../commonStore';
|
||||
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES } from '../components/common/table';
|
||||
import { getData } from "@/components/lib/api";
|
||||
import BasicTab, { BASIC_TYPES } from './basicTab';
|
||||
import OperationTab, { BIZ_TY_CD, operationCleaningTableHeadlist, operationInspectionTableHeadlist, operationMaintenanceTableHeadlist, operationTableHeadlist, WORK_TYPES } from './operationTab';
|
||||
import ReportTab, { reportTableHeadlist } from './reportTab';
|
||||
import OperationTab, { operationCleaningTableHeadlist, operationInspectionTableHeadlist, operationMaintenanceTableHeadlist, operationTableHeadlist, WORK_TYPES } from './operationTab';
|
||||
import ReportTab from './reportTab';
|
||||
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
import useStore from '../../../store/useStore';
|
||||
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
|
||||
|
||||
// 탭
|
||||
const TABS = [
|
||||
{ val: "basic", text: "기본정보" },
|
||||
{ val: "operation", text: "운영정보" },
|
||||
{ val: "report", text: "하수도정비기본계획보고서" },
|
||||
];
|
||||
|
||||
|
||||
// 날짜 조합 함수
|
||||
const getDateString = (
|
||||
year: string,
|
||||
@@ -63,12 +61,10 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const [isResetSelection, setIsResetSelection] = useState<boolean>(false);
|
||||
const [isResetDetailSelection, setIsResetDetailSelection] = useState<boolean>(false);
|
||||
const [detailBizId, setDetailBizId] = useState(0);
|
||||
const [city, setCity] = useState(0);
|
||||
const [sgg, setSgg] = useState(0);
|
||||
const [ctpvCd, setCtpvCd] = useState('0'); // 하수정비기본계획 report
|
||||
const [sigungu, setSigungu] = useState('0'); // 하수정비기본계획 report
|
||||
const [gubun, setGubun] = useState(""); // 하수정비기본계획 report
|
||||
const [reportGubun, setReportGubun] = useState("0"); // 하수정비기본계획 report
|
||||
const [city, setCity] = useState<number>(0);
|
||||
const [ctpvName, setCtpvName] = useState<string>('');
|
||||
const [sgg, setSgg] = useState<number>(0);
|
||||
const [sggName, setSggName] = useState<string>('');
|
||||
const [start1, setStart1] = useState("");
|
||||
const [start2, setStart2] = useState("");
|
||||
const [end1, setEnd1] = useState("");
|
||||
@@ -79,7 +75,8 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const [var4, setVar4] = useState("");
|
||||
// 시작, 종료
|
||||
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
|
||||
const [filteredSiguns, setFilteredSiguns] = useState<SelectDataType[]>([{ val: '0', text: '전체', sido: '0' }]);
|
||||
|
||||
const { BIZ_TY_CD } = useStore();
|
||||
|
||||
|
||||
// 오늘 날짜 구하기
|
||||
@@ -341,13 +338,17 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
cqlFilters.push(`fclt_cpct BETWEEN ${start1} AND ${end1}`);
|
||||
else if (start1) cqlFilters.push(`fclt_cpct >= ${start1}`);
|
||||
else if (end1) cqlFilters.push(`fclt_cpct <= ${end1}`);
|
||||
// 처리구역 면적
|
||||
if (start2 && end2)
|
||||
cqlFilters.push(`prcsZoneAreaSum BETWEEN ${start2} AND ${end2}`);
|
||||
else if (start2) cqlFilters.push(`prcsZoneAreaSum >= ${start2}`);
|
||||
else if (end2) cqlFilters.push(`prcsZoneAreaSum <= ${end2}`);
|
||||
// 하수처리시설명
|
||||
if (var1) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${var1}%'`);
|
||||
|
||||
// 시도
|
||||
if (ctpvName) cqlFilters.push(`ctpv_nm ILIKE '%${ctpvName}%'`);
|
||||
if (sggName) cqlFilters.push(`sgg_nm ILIKE '%${sggName}%'`);
|
||||
// // 처리구역 면적
|
||||
// if (start2 && end2)
|
||||
// cqlFilters.push(`prcsZoneAreaSum BETWEEN ${start2} AND ${end2}`);
|
||||
// else if (start2) cqlFilters.push(`prcsZoneAreaSum >= ${start2}`);
|
||||
// else if (end2) cqlFilters.push(`prcsZoneAreaSum <= ${end2}`);
|
||||
// // 하수처리시설명
|
||||
// if (var1) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${var1}%'`);
|
||||
return cqlFilters;
|
||||
};
|
||||
// 하수처리구역용 CQL 필터 생성
|
||||
@@ -561,7 +562,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
{/* 기본정보 탭 */}
|
||||
{activeTab === 'basic' && (
|
||||
<BasicTab
|
||||
open={open}
|
||||
basicType={basicType}
|
||||
city={city}
|
||||
sgg={sgg}
|
||||
@@ -575,7 +575,9 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
handleSearch={handleSearch}
|
||||
setBasicType={setBasicType}
|
||||
setCity={setCity}
|
||||
setCtpvName={setCtpvName}
|
||||
setSgg={setSgg}
|
||||
setSggName={setSggName}
|
||||
setYearFilter={setYearFilter}
|
||||
setStart1={setStart1}
|
||||
setStart2={setStart2}
|
||||
@@ -588,7 +590,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
{/* 운영정보 탭 */}
|
||||
{activeTab === 'operation' && (
|
||||
<OperationTab
|
||||
open={open}
|
||||
workType={workType}
|
||||
var1={var1}
|
||||
var2={var2}
|
||||
|
||||
@@ -79,7 +79,11 @@ export function flattenFolders(data: Folder[]): FlattenedFolder[] {
|
||||
}
|
||||
}
|
||||
|
||||
data.forEach((root) => traverse(root, {}, 0));
|
||||
if (data && data.length > 0 && data[0].clsfSystmNm == "기본계획") { // data가 비어있지 않고 children이 있는 경우에만 처리
|
||||
data.forEach((root) => traverse(root, {}, 0));
|
||||
} else {
|
||||
data.forEach((root) => traverse(root, {}, 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -96,13 +100,15 @@ export const useDcTree = (clsfSystmNm:string) => {
|
||||
if (result) {
|
||||
setData(result);
|
||||
}
|
||||
console.log("useDcTree호출");
|
||||
console.log("clsfSystmNm:", clsfSystmNm, "data:", result);
|
||||
};
|
||||
fetch();
|
||||
console.log("useDcTree호출");
|
||||
}, [clsfSystmNm]);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getDcFile = async (clsfSystmSn:string) => {
|
||||
const queryString = 'clsfSystmSn='+clsfSystmSn;
|
||||
const result = await getDcData(`/api/v1/dc/file${queryString ? `?${queryString}` : ''}`);
|
||||
@@ -119,7 +125,7 @@ export const addFileName = async (data:FlattenedFolder[]) => {
|
||||
})
|
||||
);
|
||||
return fileReportData;
|
||||
}
|
||||
};
|
||||
export const getDcFileprcs = async (fileSn:string) => {
|
||||
const queryString = 'fileSn='+fileSn;
|
||||
const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// 하수처리시설
|
||||
|
||||
import React from 'react';
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox';
|
||||
|
||||
|
||||
interface SwagePrcsFcltProps {
|
||||
var1: string;
|
||||
@@ -10,36 +12,53 @@ interface SwagePrcsFcltProps {
|
||||
start2: string;
|
||||
end1: string;
|
||||
end2: string;
|
||||
city: number;
|
||||
sgg: number;
|
||||
yearFilter: { start: string; end: string };
|
||||
setVar1: (value: string) => void;
|
||||
setStart1: (value: string) => void;
|
||||
setStart2: (value: string) => void;
|
||||
setEnd1: (value: string) => void;
|
||||
setEnd2: (value: string) => void;
|
||||
setCity: (value: number) => void;
|
||||
setCtpvName: (name: string) => void;
|
||||
setSgg: (value: number) => void;
|
||||
setSggName: (name: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
var1,
|
||||
start1,
|
||||
start2,
|
||||
end1,
|
||||
end2,
|
||||
city,
|
||||
sgg,
|
||||
yearFilter,
|
||||
setVar1,
|
||||
setStart1,
|
||||
setStart2,
|
||||
setEnd1,
|
||||
setEnd2,
|
||||
setCity,
|
||||
setCtpvName,
|
||||
setSgg,
|
||||
setSggName,
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
{/* 가동개시일자 */}
|
||||
<div className="mb-2 flex items-center">
|
||||
<CtpvSggPrcsFcltSelectBox
|
||||
sido={city}
|
||||
sigungu={sgg}
|
||||
onSidoChange={(sidoId) => {
|
||||
setCity(sidoId);
|
||||
setSgg(0);
|
||||
}}
|
||||
onSigunguChange={setSgg}
|
||||
setCtpvName={setCtpvName}
|
||||
setSggName={setSggName}
|
||||
/>
|
||||
{/* 가동개시일자 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">가동개시일자</Label>
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
@@ -89,57 +108,6 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* 처리구역 면적 */}
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">처리구역 면적(m²)</Label>
|
||||
<Input
|
||||
className="w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart2(val);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault(); // Enter 키 기본 동작 방지
|
||||
handleSearch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd2(val);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault(); // Enter 키 기본 동작 방지
|
||||
handleSearch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* 하수처리시설명 */}
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">하수처리시설명</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault(); // Enter 키 기본 동작 방지
|
||||
handleSearch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 하수처리구역
|
||||
|
||||
import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox';
|
||||
import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
|
||||
@@ -31,30 +31,15 @@ const swagePrcsZone: React.FC<swagePrcsZoneProps> = ({
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="mb-2 flex items-center">
|
||||
<SidoSigunguSelectBox
|
||||
<CtpvSggPrcsFcltSelectBox
|
||||
sido={city}
|
||||
sigungu={sgg}
|
||||
onSidoChange={(sidoId) => {
|
||||
onSidoChange={(sidoId: number) => {
|
||||
setCity(sidoId);
|
||||
setSgg(0);
|
||||
}}
|
||||
onSigunguChange={setSgg}
|
||||
/>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">하수처리구역명</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault(); // Enter 키 기본 동작 방지
|
||||
handleSearch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">하수처리시설명</Label>
|
||||
<Input
|
||||
className='!text-xl w-[120px] placeholder:text-center mr-8'
|
||||
@@ -70,6 +55,21 @@ const swagePrcsZone: React.FC<swagePrcsZoneProps> = ({
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">하수처리구역명</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault(); // Enter 키 기본 동작 방지
|
||||
handleSearch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import { Label } from "@common/components/ui/label";
|
||||
|
||||
export function WaterDepth({ onClose }: { onClose?: () => void }) {
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fixed top-8 left-8 z-50 bg-white rounded-lg shadow-lg border border-gray-200 w-[160px] px-4 py-3">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<Label className="text-3xl text-gray-900 font-bold">침수심 범례</Label>
|
||||
<button
|
||||
type="button"
|
||||
className="ml-2"
|
||||
onClick={() => setIsPlaying((prev) => !prev)}
|
||||
>
|
||||
{isPlaying ? (
|
||||
<img
|
||||
src="/images/map/toolbar/pause_button.png"
|
||||
alt="pause"
|
||||
className="w-6 h-6 object-contain"
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src="/images/map/toolbar/play_button_arrowhead.png"
|
||||
alt="play"
|
||||
className="w-6 h-6 object-contain"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div className="border-b border-gray-300 mb-2" />
|
||||
<div className="w-[140px] mx-auto flex flex-col items-center">
|
||||
<div className="bg-blue-900 text-white text-2xl text-center py-1 mb-2 rounded w-full">침 수 심</div>
|
||||
<div className="space-y-1 w-full">
|
||||
<LegendItem color="#0000ff" label="0.3~0.5m 미만" />
|
||||
<LegendItem color="#00e8fe" label="0.5~1.0m 미만" />
|
||||
<LegendItem color="#00fdb7" label="1.0~1.5m 미만" />
|
||||
<LegendItem color="#00fe4e" label="1.5~2.0m 미만" />
|
||||
<LegendItem color="#fce800" label="2.0~3.0m 미만" />
|
||||
<LegendItem color="#fe0000" label="3.0m 이상" />
|
||||
</div>
|
||||
<div className="border-t-4 border-gray-900 mt-2 mx-auto w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LegendItem({ color, label }: { color: string; label: string }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
<span
|
||||
className="inline-block w-16 h-6"
|
||||
style={{ backgroundColor: color, border: "1px solid #ccc" }}
|
||||
/>
|
||||
<span className="text-xl text-gray-900 font-bold">{label}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
'use client'
|
||||
import { getData } from '@/components/lib/api'
|
||||
import ContentLayout from '@/components/ui/contentLayout'
|
||||
import { GovSidoSigunguSearchForm } from '@/components/ui/govSidoSigunguSearchForm'
|
||||
import { DatePicker, DateMonthPicker, Form, SearchFormRight, SelectBox } from '@common/business_components/ui'
|
||||
import { DateMonthPicker, Form, SelectBox } from '@common/business_components/ui'
|
||||
import ListDiv from '@common/business_components/ui/list_div'
|
||||
import { Button, Label } from '@common/components/ui'
|
||||
import { useEffect, useState, type ReactNode } from 'react'
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||
import { useState, type ReactNode } from 'react'
|
||||
import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'
|
||||
|
||||
type TitleName = {
|
||||
mainTitle: string
|
||||
@@ -40,37 +37,26 @@ type BaseLayout = {
|
||||
firstDivBtn?: boolean
|
||||
selectBox?: SelectBox
|
||||
showDatePicker?: boolean;
|
||||
enoCd?: string | number
|
||||
setEnoCd?: (enoCd: string) => void
|
||||
ctpvCd?: string | number
|
||||
setCtpvCd?: (ctpvCd: string) => void
|
||||
ctpvCd: number;
|
||||
sggCd: number;
|
||||
sggName?: string;
|
||||
sigungu?: ApiSelectOption
|
||||
setSigungu?: (sigungu: ApiSelectOption) => void
|
||||
selectedSwageTreatmentPlant?: string | number;
|
||||
onSwageTreatmentPlantChange?: (value: string | number) => void;
|
||||
selectedSwageProcessingZone?: string | number;
|
||||
onSwageProcessingZoneChange?: (value: string | number) => void;
|
||||
selectedSwagePrcsFclt?: string | number;
|
||||
onSwagePrcsFcltChange?: (value: string | number) => void;
|
||||
aplcnYmFrom?: string
|
||||
setAplcnYmFrom?: (aplcnYmFrom: string) => void;
|
||||
aplcnYmTo?: string
|
||||
setAplcnYmTo?: (aplcnYmTo: string) => void;
|
||||
isLoading?: boolean;
|
||||
// Excel 다운로드 관련 props 추가
|
||||
setCtpvCd: (ctpv: number) => void;
|
||||
setSggCd: (sgg: number) => void;
|
||||
setSggName?: (sggName: string) => void;
|
||||
setIsLoading?: (isLoading: boolean) => void;
|
||||
onExcelDownload?: () => void;
|
||||
onDeleteFile?: () => void;
|
||||
onAnalysis?: () => void;
|
||||
selectedRowData?: any;
|
||||
}
|
||||
// TODO: 나중에 이벤트 추가되면 파라미터 추가 필요 ?
|
||||
/**
|
||||
*
|
||||
* @param children
|
||||
* @param titleName : 메인 타이틀, 첫번째 요소 타이틀, 두번째 요소 타이틀
|
||||
* @param form : zod 스키마
|
||||
* @param onSubmitEvent : 폼 이벤트 처리
|
||||
* @param graphBtn : 하단에 그래프 버튼 유무 처리
|
||||
* @returns
|
||||
*/
|
||||
|
||||
const BaseLayout = ({
|
||||
children,
|
||||
titleName,
|
||||
@@ -80,113 +66,25 @@ const BaseLayout = ({
|
||||
analysisBtn,
|
||||
selectBox,
|
||||
showDatePicker = false,
|
||||
enoCd,
|
||||
setEnoCd,
|
||||
ctpvCd,
|
||||
setCtpvCd,
|
||||
sggCd,
|
||||
sggName,
|
||||
sigungu,
|
||||
setSigungu,
|
||||
selectedSwageTreatmentPlant,
|
||||
onSwageTreatmentPlantChange,
|
||||
selectedSwageProcessingZone,
|
||||
onSwageProcessingZoneChange,
|
||||
selectedSwagePrcsFclt,
|
||||
onSwagePrcsFcltChange,
|
||||
aplcnYmFrom,
|
||||
setAplcnYmFrom,
|
||||
aplcnYmTo,
|
||||
setAplcnYmTo,
|
||||
isLoading,
|
||||
// Excel 다운로드 관련 props 추가
|
||||
setCtpvCd,
|
||||
setSggCd,
|
||||
setSggName,
|
||||
setIsLoading,
|
||||
onExcelDownload,
|
||||
onDeleteFile,
|
||||
onAnalysis,
|
||||
selectedRowData
|
||||
}: BaseLayout) => {
|
||||
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '전체' };
|
||||
|
||||
const [processingZoneOptions, setProcessingZoneOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [treatmentFacilityOptions, setTreatmentFacilityOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [isLoadingAreaData, setIsLoadingAreaData] = useState<boolean>(isLoading || false);
|
||||
const [atflId, setAtflId] = useState('')
|
||||
const [fileNm, setFileNm] = useState('')
|
||||
const [dcrpPplMtncBizSn, setDcrpPplMtncBizSn] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!sigungu || sigungu.val === '0' || !sigungu.val) {
|
||||
console.log("BaseLayout: Skipping fetchData because sigungu.val is:", sigungu?.val);
|
||||
setProcessingZoneOptions([defaultOption]);
|
||||
setTreatmentFacilityOptions([defaultOption]);
|
||||
setIsLoadingAreaData(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoadingAreaData(true);
|
||||
try {
|
||||
// const queryParamsZone = new URLSearchParams({
|
||||
// lyrTypeNm: 'tb_ms_swage_prcs_zone',
|
||||
// page: '0',
|
||||
// size: '100000',
|
||||
// sort: 'string',
|
||||
// });
|
||||
// const cqlFiltersZone: string[] = [];
|
||||
// if (sigungu.val && sigungu.val !== '0') {
|
||||
// // console.log("시군구 코드", sigungu.val);
|
||||
// cqlFiltersZone.push(`sgg_cd='${sigungu.val}'`);
|
||||
// }
|
||||
// if (cqlFiltersZone.length > 0) {
|
||||
// queryParamsZone.append('cqlFiter', cqlFiltersZone.join(' and '));
|
||||
// }
|
||||
|
||||
const queryParamsFacility = new URLSearchParams({
|
||||
lyrTypeNm: 'tb_il_swage_prcs_fclt',
|
||||
page: '0',
|
||||
size: '100000', // 요청하신 URL에 맞게 50으로 변경 가능
|
||||
sort: 'string',
|
||||
});
|
||||
const cqlFiltersFacility: string[] = [];
|
||||
if (sigungu.text && sigungu.val !== '0') {
|
||||
cqlFiltersFacility.push(`sgg_nm ILIKE '%${sigungu.text}%'`);
|
||||
}
|
||||
if (cqlFiltersFacility.length > 0) {
|
||||
queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and ')); // API 명세에 따라 cqlFilter 또는 cqlFiter
|
||||
}
|
||||
|
||||
// console.log("BaseLayout API URL for Zones:", `v1/fclts?${queryParamsZone.toString()}`);
|
||||
console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
|
||||
|
||||
// const [zoneRes, facilityRes] = await Promise.all([
|
||||
// getData(`v1/fclts?${queryParamsZone.toString()}`),
|
||||
// getData(`v1/fclts?${queryParamsFacility.toString()}`)
|
||||
// ]);
|
||||
const facilityRes = await getData(`v1/fclts?${queryParamsFacility.toString()}`);
|
||||
// const zones = (zoneRes.data || []).map((item: any) => ({
|
||||
// text: item.swtrmtZoneNm || item.name || `구역 ${item.id || item.gid || item.objectid}`,
|
||||
// val: item.fid || item.id || item.gid || item.objectid
|
||||
// }));
|
||||
// setProcessingZoneOptions([defaultOption, ...zones]);
|
||||
// console.log("BaseLayout Fetched Processing Zones:", [defaultOption, ...zones]);
|
||||
|
||||
|
||||
const facilities = (facilityRes.data || []).map((item: any) => ({
|
||||
text: item.swtrmtFcltNm || item.name || `시설 ${item.id || item.gid || item.objectid}`,
|
||||
val: item.fid || item.id || item.gid || item.objectid
|
||||
}));
|
||||
setTreatmentFacilityOptions([defaultOption, ...facilities]);
|
||||
console.log("BaseLayout Fetched Treatment Facilities:", [defaultOption, ...facilities]);
|
||||
|
||||
} catch (error) {
|
||||
console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
|
||||
setProcessingZoneOptions([defaultOption]);
|
||||
setTreatmentFacilityOptions([defaultOption]);
|
||||
} finally {
|
||||
setIsLoadingAreaData(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [sigungu]); // sigungu 상태가 변경될 때마다 실행
|
||||
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
||||
@@ -207,7 +105,6 @@ const BaseLayout = ({
|
||||
|
||||
return (
|
||||
<Form {...form} className="min-h-screen flex flex-col">
|
||||
<LoadingOverlay isLoading={isLoading ?? false} />
|
||||
<ListDiv>
|
||||
{/* searchBox.title이 있으면 h3 태그로 제목을 표시합니다. */}
|
||||
{selectBox?.title && (
|
||||
@@ -215,10 +112,10 @@ const BaseLayout = ({
|
||||
<h3 className="text-2xl font-medium">{selectBox.title}</h3>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center w-full">
|
||||
<div className="ml-2 mt-2 flex items-center w-full">
|
||||
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
||||
<>
|
||||
<Label className="ml-2 mr-4 w-20 text-xl font-medium text-gray-600 whitespace-nowrap">상수사용기간</Label>
|
||||
<Label className="mr-4 w-20 text-xl font-medium text-gray-600 whitespace-nowrap">상수사용기간</Label>
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
value: aplcnYmFrom || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
|
||||
@@ -253,24 +150,20 @@ const BaseLayout = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<GovSidoSigunguSearchForm
|
||||
isShowPart={true}
|
||||
setRtnEnvPartValue={(val) => setEnoCd?.(val)}
|
||||
setRtnEnvPartSidoValue={(val) => setCtpvCd?.(val)}
|
||||
setRtnEnvPartSigunValue={setSigungu} // 선택된 시군구 값을 받을 콜백 함수 전달
|
||||
>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-4">하수처리장</Label>
|
||||
<SelectBox
|
||||
label={''}
|
||||
value={selectedSwageTreatmentPlant}
|
||||
selectArray={treatmentFacilityOptions}
|
||||
className="w-[160px]"
|
||||
selectClass="rounded-lg bg-white truncate"
|
||||
onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
{/* <SelectBox label={"하수처리구역"} value={selectedSwageProcessingZone} selectArray={processingZoneOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageProcessingZoneChange?.(val)} selectContentClass="z-[101]" /> */}
|
||||
</GovSidoSigunguSearchForm>
|
||||
<CtpvSggPrcsFcltSelectBox
|
||||
sido={ctpvCd}
|
||||
sigungu={sggCd}
|
||||
sggName={sggName}
|
||||
onSidoChange={(sidoId: number) => {
|
||||
setCtpvCd(sidoId);
|
||||
setSggCd(0);
|
||||
}}
|
||||
onSigunguChange={setSggCd}
|
||||
setSggName={setSggName}
|
||||
selectedSwagePrcsFclt={selectedSwagePrcsFclt}
|
||||
onSwagePrcsFcltChange={onSwagePrcsFcltChange}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
{selectBox?.searchChk && (
|
||||
<div className='flex flex-1 justify-end mr-6'>
|
||||
<Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button>
|
||||
|
||||
@@ -47,15 +47,14 @@ interface ApiSelectOption {
|
||||
}
|
||||
|
||||
const IrSetting = () => {
|
||||
const [input1, setInput1] = useState('');
|
||||
const [input1, setInput1] = useState('0.8'); // 오수전환율 초기값
|
||||
const [input2, setInput2] = useState('');
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드
|
||||
const [select2, setSelect2] = useState('0'); // 시도코드
|
||||
const [select3, setSelect3] = useState<ApiSelectOption>({ val: '0', text: '전체' }); // 시군구 상태를 객체로 변경
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장코드
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역코드
|
||||
const [select1, setSelect1] = useState<string | number>('0'); // 하수처리장코드
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
||||
|
||||
const { data: userInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS],
|
||||
@@ -115,11 +114,7 @@ const IrSetting = () => {
|
||||
setSelectedDate(`${year}${month}`);
|
||||
setInput1(''); // 입력 필드 초기화
|
||||
setInput2(''); // 입력 필드 초기화
|
||||
setSelect1('0'); // 선택 필드 초기화
|
||||
setSelect2('0');
|
||||
setSelect3({ val: '0', text: '전체' });
|
||||
setSelect4(0);
|
||||
setSelect5(0);
|
||||
setSelect1(0);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
};
|
||||
|
||||
@@ -164,7 +159,7 @@ const IrSetting = () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
|
||||
// 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장)
|
||||
if (!validateBasicSelectionsWithAlert(select1, select2, select3, select4, {
|
||||
if (!validateBasicSelectionsWithAlert(select1, {
|
||||
requireEnv: true,
|
||||
requireSido: true,
|
||||
requireSigungu: true,
|
||||
@@ -299,15 +294,11 @@ const IrSetting = () => {
|
||||
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
? select4.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
ctpvCd: (ctpvCd ? String(ctpvCd) : null),
|
||||
sggCd: (sggCd ? String(sggCd) : null), // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select1 && select1 !== '0' && select1 !== 0)
|
||||
? select1.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리장코드 (선택값)
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
aplcnYm: selectedDate, // 적용연월
|
||||
drtwSwtcRt: parseFloat(input1) || null, // 오수전환율 (숫자로 변환)
|
||||
anlsInfo: input2, // 분석정보
|
||||
@@ -346,17 +337,15 @@ const IrSetting = () => {
|
||||
graphBtn={false}
|
||||
analysisBtn={'침입수분석 실행'}
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
enoCd={select1}
|
||||
setEnoCd={setSelect1}
|
||||
ctpvCd={select2}
|
||||
setCtpvCd={setSelect2}
|
||||
sigungu={select3}
|
||||
setSigungu={setSelect3}
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
isLoading={isLoadingResult}
|
||||
ctpvCd={ctpvCd}
|
||||
sggCd={sggCd}
|
||||
sggName={sggName}
|
||||
selectedSwagePrcsFclt={select1}
|
||||
onSwagePrcsFcltChange={setSelect1}
|
||||
setCtpvCd={setCtpvCd}
|
||||
setSggCd={setSggCd}
|
||||
setSggName={setSggName}
|
||||
setIsLoading={setIsLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
{[<div key="irsetting-layout-key" className="grid gap-4">
|
||||
@@ -387,10 +376,7 @@ const IrSetting = () => {
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={2} />
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='inputdate'
|
||||
form={form}
|
||||
titleNm='▷ 상수사용량 적용월'>
|
||||
<FormSection name='inputdate' form={form} titleNm='▷ 상수사용량 적용월'>
|
||||
{/* 연월 선택 라이브러리로 수정 필요 */}
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
@@ -403,10 +389,7 @@ const IrSetting = () => {
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='rtkSource'
|
||||
form={form}
|
||||
titleNm='▷ 오수전환율'>
|
||||
<FormSection name='rtkSource' form={form} titleNm='▷ 오수전환율'>
|
||||
<Input
|
||||
value={input1}
|
||||
onChange={(e) => {
|
||||
@@ -434,10 +417,7 @@ const IrSetting = () => {
|
||||
placeholder="예: 0.75"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='analysis information'
|
||||
form={form}
|
||||
titleNm='▷ 분석 정보'>
|
||||
<FormSection name='analysis information' form={form} titleNm='▷ 분석 정보'>
|
||||
<Input
|
||||
value={input2}
|
||||
onChange={(e) => setInput2(e.target.value)}
|
||||
|
||||
@@ -114,11 +114,10 @@ export default function RTKForm() {
|
||||
initialFileInfo, // 2: 강우자료
|
||||
]);
|
||||
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드 (enoCd)
|
||||
const [select2, setSelect2] = useState('0'); // 시도코드 (ctpvCd)
|
||||
const [select3, setSelect3] = useState<ApiSelectOption>({ val: '0', text: '전체' }); // 시군구 (sggCd)
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장 (swageTmplntCd)
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역 (swtrmtZoneCd)
|
||||
const [select1, setSelect1] = useState<string | number>('0'); // 하수처리장 (swageTmplntCd)
|
||||
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
||||
|
||||
const { data: userInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS],
|
||||
@@ -142,10 +141,6 @@ export default function RTKForm() {
|
||||
]);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
setSelect1('0');
|
||||
setSelect2('0');
|
||||
setSelect3({ val: '0', text: '전체' });
|
||||
setSelect4('0');
|
||||
setSelect5('0');
|
||||
};
|
||||
|
||||
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
|
||||
@@ -182,7 +177,7 @@ export default function RTKForm() {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
|
||||
// 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장)
|
||||
if (!validateBasicSelectionsWithAlert(select1, select2, select3, select4, {
|
||||
if (!validateBasicSelectionsWithAlert(select1, {
|
||||
requireEnv: true,
|
||||
requireSido: true,
|
||||
requireSigungu: true,
|
||||
@@ -295,16 +290,12 @@ export default function RTKForm() {
|
||||
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
aplcnYm: formData.date, // 적용연월 (YYYYMM 형식)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
? select4.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
ctpvCd: ctpvCd ? String(ctpvCd) : null, // 시도코드 (선택값)
|
||||
sggCd: sggCd ? String(sggCd) : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select1 && select1 !== '0' && select1 !== 0)
|
||||
? select1.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리장코드 (선택값)
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
|
||||
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
|
||||
atflInpNm: file1?.file?.name || "", // 오수관망도 파일명
|
||||
@@ -395,7 +386,6 @@ export default function RTKForm() {
|
||||
}
|
||||
|
||||
return (
|
||||
// <BaseLayout searchBox={{ title: '분석 대상지역 선택' }} titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }} form={form} onSubmitEvent={() => { }} graphBtn={false} >
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }}
|
||||
@@ -403,17 +393,15 @@ export default function RTKForm() {
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={false}
|
||||
analysisBtn={'RDII분석 실행'}
|
||||
enoCd={select1}
|
||||
setEnoCd={setSelect1}
|
||||
ctpvCd={select2}
|
||||
setCtpvCd={setSelect2}
|
||||
sigungu={select3}
|
||||
setSigungu={setSelect3}
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
isLoading={isLoadingResult}
|
||||
ctpvCd={ctpvCd}
|
||||
sggCd={sggCd}
|
||||
sggName={sggName}
|
||||
selectedSwagePrcsFclt={select1}
|
||||
onSwagePrcsFcltChange={setSelect1}
|
||||
setCtpvCd={setCtpvCd}
|
||||
setSggCd={setSggCd}
|
||||
setSggName={setSggName}
|
||||
setIsLoading={setIsLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
{[<div className="space-y-[1.2rem] overflow-x-auto pt-4 pb-4 px-2" key="form-section">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { deleteData, getData, useCookieAuth } from '@/components/lib/api'
|
||||
import { deleteData, getData } from '@/components/lib/api'
|
||||
import { UiTable, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useState, useEffect } from 'react'
|
||||
@@ -11,12 +11,6 @@ import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
|
||||
|
||||
const formSchema = z.object({
|
||||
enoCd: z.string().default('0'), // 환경청코드
|
||||
ctpvCd: z.string().default('0'), // 시도코드
|
||||
sggCd: z.object({ // 시군구
|
||||
val: z.string().default('0'),
|
||||
text: z.string().default('전체'),
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
||||
@@ -54,14 +48,10 @@ const tableColumn = [
|
||||
]
|
||||
|
||||
const Result = () => {
|
||||
const AUTH = useCookieAuth();
|
||||
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
enoCd: '0',
|
||||
ctpvCd: '0',
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
@@ -88,6 +78,9 @@ const Result = () => {
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
||||
const [curPage, setCurPage] = useState(1); // 현재 페이지
|
||||
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
||||
|
||||
useEffect(() => {
|
||||
// setCurPage(1);
|
||||
@@ -127,11 +120,9 @@ const Result = () => {
|
||||
let inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"ctpvCd": ctpvCd === 0 ? null : ctpvCd,
|
||||
"sggCd": sggCd === 0 ? null : sggCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
"auth": AUTH,
|
||||
};
|
||||
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
@@ -163,7 +154,8 @@ const Result = () => {
|
||||
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
|
||||
};
|
||||
});
|
||||
})
|
||||
.filter((item: any) => item.scsYn === 'Y');
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
@@ -289,21 +281,19 @@ const Result = () => {
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={true}
|
||||
showDatePicker={true}
|
||||
enoCd={form.watch("enoCd")}
|
||||
setEnoCd={(val) => form.setValue("enoCd", val)}
|
||||
ctpvCd={form.watch("ctpvCd")}
|
||||
setCtpvCd={(val) => form.setValue("ctpvCd", val)}
|
||||
sigungu={form.watch("sggCd")}
|
||||
setSigungu={(val) => form.setValue("sggCd", val)}
|
||||
selectedSwageTreatmentPlant={form.watch("swageTmplntCd")}
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
ctpvCd={ctpvCd}
|
||||
sggCd={sggCd}
|
||||
sggName={sggName}
|
||||
selectedSwagePrcsFclt={form.watch("swageTmplntCd")}
|
||||
onSwagePrcsFcltChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
aplcnYmFrom={form.watch("aplcnYmFrom")}
|
||||
setAplcnYmFrom={(val: string) => { form.setValue("aplcnYmFrom", val) }}
|
||||
aplcnYmTo={form.watch("aplcnYmTo")}
|
||||
setAplcnYmTo={(val: string) => { form.setValue("aplcnYmTo", val) }}
|
||||
isLoading={isLoadingResult}
|
||||
setCtpvCd={setCtpvCd}
|
||||
setSggCd={setSggCd}
|
||||
setSggName={setSggName}
|
||||
setIsLoading={setIsLoadingResult}
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
onDeleteFile={handleFileDelete}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { deleteData, getData, useCookieAuth } from '@/components/lib/api'
|
||||
import { UiTable, downloadExcel, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
||||
import { deleteData, getData } from '@/components/lib/api'
|
||||
import { UiTable, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
@@ -11,12 +11,6 @@ import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
|
||||
|
||||
const formSchema = z.object({
|
||||
enoCd: z.string().default('0'), // 환경청코드
|
||||
ctpvCd: z.string().default('0'), // 시도코드
|
||||
sggCd: z.object({ // 시군구
|
||||
val: z.string().default('0'),
|
||||
text: z.string().default('전체'),
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
||||
@@ -55,9 +49,6 @@ const Result = () => {
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
enoCd: '0',
|
||||
ctpvCd: '0',
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
@@ -76,8 +67,6 @@ const Result = () => {
|
||||
}
|
||||
})
|
||||
|
||||
const AUTH = useCookieAuth();
|
||||
|
||||
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수l
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
@@ -85,6 +74,9 @@ const Result = () => {
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
||||
const [curPage, setCurPage] = useState(1); // 현재 페이지
|
||||
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
||||
|
||||
useEffect(() => {
|
||||
// setCurPage(1);
|
||||
@@ -124,12 +116,9 @@ const Result = () => {
|
||||
let inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"ctpvCd": ctpvCd === 0 ? null : ctpvCd,
|
||||
"sggCd": sggCd === 0 ? null : sggCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
// "swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
"auth": AUTH,
|
||||
};
|
||||
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
@@ -162,7 +151,8 @@ const Result = () => {
|
||||
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
|
||||
};
|
||||
});
|
||||
})
|
||||
.filter((item: any) => item.scsYn === 'Y');
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
@@ -289,21 +279,19 @@ const Result = () => {
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={true}
|
||||
showDatePicker={true}
|
||||
enoCd={form.watch("enoCd")}
|
||||
setEnoCd={(val) => form.setValue("enoCd", val)}
|
||||
ctpvCd={form.watch("ctpvCd")}
|
||||
setCtpvCd={(val) => form.setValue("ctpvCd", val)}
|
||||
sigungu={form.watch("sggCd")}
|
||||
setSigungu={(val) => form.setValue("sggCd", val)}
|
||||
selectedSwageTreatmentPlant={form.watch("swageTmplntCd")}
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
ctpvCd={ctpvCd}
|
||||
sggCd={sggCd}
|
||||
sggName={sggName}
|
||||
selectedSwagePrcsFclt={form.watch("swageTmplntCd")}
|
||||
onSwagePrcsFcltChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
aplcnYmFrom={form.watch("aplcnYmFrom")}
|
||||
setAplcnYmFrom={(val: string) => { form.setValue("aplcnYmFrom", val) }}
|
||||
aplcnYmTo={form.watch("aplcnYmTo")}
|
||||
setAplcnYmTo={(val: string) => { form.setValue("aplcnYmTo", val) }}
|
||||
isLoading={isLoadingResult} // 로딩 상태 전달
|
||||
setCtpvCd={setCtpvCd}
|
||||
setSggCd={setSggCd}
|
||||
setSggName={setSggName}
|
||||
setIsLoading={setIsLoadingResult}
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
onDeleteFile={handleFileDelete}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect} from 'react';
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "@/entities/menu";
|
||||
import { useMenuStore } from "@/shared/lib";
|
||||
import { PageLayout } from "@/shared/ui/page-layout";
|
||||
import useStore from '../store/useStore';
|
||||
|
||||
export default function WithBaseLayout({
|
||||
children,
|
||||
@@ -18,6 +20,14 @@ export default function WithBaseLayout({
|
||||
|
||||
const setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo);
|
||||
|
||||
const { fetchCtpvSggData, fetchBIZ_SE_CD, fetchBIZ_TY_CD } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchCtpvSggData();
|
||||
fetchBIZ_SE_CD();
|
||||
fetchBIZ_TY_CD();
|
||||
}, []);
|
||||
|
||||
const handleMenuClick = (item: MenuItem) => {
|
||||
setCurrentMenuInfo(item);
|
||||
router.push(item.urlPath);
|
||||
|
||||
@@ -87,10 +87,7 @@ export const validateMultipleSelectionsWithAlert = (
|
||||
* @returns 검증 결과
|
||||
*/
|
||||
export const validateBasicSelections = (
|
||||
select1: string,
|
||||
select2: string,
|
||||
select3: ApiSelectOption,
|
||||
select4: string | number,
|
||||
select1: string | number,
|
||||
options: {
|
||||
requireEnv?: boolean;
|
||||
requireSido?: boolean;
|
||||
@@ -108,21 +105,6 @@ export const validateBasicSelections = (
|
||||
const validationConfigs: SelectionValidationConfig[] = [
|
||||
{
|
||||
value: select1,
|
||||
description: '환경청',
|
||||
required: requireEnv
|
||||
},
|
||||
{
|
||||
value: select2,
|
||||
description: '시도',
|
||||
required: requireSido
|
||||
},
|
||||
{
|
||||
value: select3,
|
||||
description: '시군구',
|
||||
required: requireSigungu
|
||||
},
|
||||
{
|
||||
value: select4,
|
||||
description: '하수처리장',
|
||||
required: requireTreatmentPlant
|
||||
}
|
||||
@@ -133,18 +115,12 @@ export const validateBasicSelections = (
|
||||
|
||||
/**
|
||||
* 기본 선택 항목들 검증 및 alert 표시
|
||||
* @param select1 환경청코드
|
||||
* @param select2 시도코드
|
||||
* @param select3 시군구 객체
|
||||
* @param select4 하수처리장코드
|
||||
* @param select1 하수처리장코드
|
||||
* @param options 각 항목의 필수 여부 설정
|
||||
* @returns 검증 결과
|
||||
*/
|
||||
export const validateBasicSelectionsWithAlert = (
|
||||
select1: string,
|
||||
select2: string,
|
||||
select3: ApiSelectOption,
|
||||
select4: string | number,
|
||||
select1: string | number,
|
||||
options: {
|
||||
requireEnv?: boolean;
|
||||
requireSido?: boolean;
|
||||
@@ -152,7 +128,7 @@ export const validateBasicSelectionsWithAlert = (
|
||||
requireTreatmentPlant?: boolean;
|
||||
} = {}
|
||||
): boolean => {
|
||||
const result = validateBasicSelections(select1, select2, select3, select4, options);
|
||||
const result = validateBasicSelections(select1, options);
|
||||
if (!result.isValid && result.errorMessage) {
|
||||
alert(result.errorMessage);
|
||||
return false;
|
||||
|
||||
@@ -41,8 +41,6 @@ export const postData = async (url: string, data: object): Promise<any> => {
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
console.log('res', res);
|
||||
console.log('res.status', res.status);
|
||||
if (res.ok) {
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
@@ -85,7 +83,7 @@ export const deleteData = async (url: string, data?: object): Promise<any> => {
|
||||
return res;
|
||||
};
|
||||
|
||||
// RSONE DC API 호출
|
||||
// DC API 호출
|
||||
export const getDcData = async (url: string, data?: object): Promise<any> => {
|
||||
const res = await fetch('/dc-service' + url, {
|
||||
method: "GET",
|
||||
@@ -97,9 +95,8 @@ export const getDcData = async (url: string, data?: object): Promise<any> => {
|
||||
throw new Error('Failed to fetch data');
|
||||
};
|
||||
|
||||
// RSONE DC API 호출
|
||||
// CMS API 호출
|
||||
export const getCmsData = async (url: string, data?: object): Promise<any> => {
|
||||
console.log(url);
|
||||
const res = await fetch('/cms-service' + url, {
|
||||
method: "GET",
|
||||
});
|
||||
@@ -110,6 +107,18 @@ export const getCmsData = async (url: string, data?: object): Promise<any> => {
|
||||
throw new Error('Failed to fetch data');
|
||||
};
|
||||
|
||||
// GeoServer API 호출
|
||||
export const getGeoData = async (url: string, data?: object): Promise<any> => {
|
||||
const res = await fetch('/geoserver' + url, {
|
||||
method: "GET",
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
return data;
|
||||
}
|
||||
throw new Error('Failed to fetch data');
|
||||
};
|
||||
|
||||
export function getCookie(name: string) {
|
||||
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||
if (match && match[2]) {
|
||||
|
||||
@@ -103,8 +103,10 @@ export const formatDateString = (dateStr: string): string => {
|
||||
}
|
||||
|
||||
const year = cleanStr.substring(0, 4);
|
||||
const month = parseInt(cleanStr.substring(4, 6)); // parseInt로 앞의 0 제거
|
||||
const day = parseInt(cleanStr.substring(6, 8)); // parseInt로 앞의 0 제거
|
||||
const month = cleanStr.substring(4, 6); // parseInt로 앞의 0 제거
|
||||
// const month = parseInt(cleanStr.substring(4, 6)); // parseInt로 앞의 0 제거
|
||||
// const day = parseInt(cleanStr.substring(6, 8)); // parseInt로 앞의 0 제거
|
||||
const day = cleanStr.substring(6, 8); // parseInt로 앞의 0 제거
|
||||
|
||||
return `${year}년 ${month}월 ${day}일`;
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
@@ -21,7 +21,7 @@ export const formatDisplayData = (data: { [key: string]: any }): { [key: string]
|
||||
if (/^\d{6}$/.test(dateStr)) {
|
||||
const year = dateStr.substring(0, 4);
|
||||
const month = parseInt(dateStr.substring(4, 6));
|
||||
formattedData[fieldId] = `${year}년 ${month}월`;
|
||||
formattedData[fieldId] = `${year}-${month}`;
|
||||
} else {
|
||||
formattedData[fieldId] = String(value);
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export const formatDisplayData = (data: { [key: string]: any }): { [key: string]
|
||||
// yyyy-mm-dd hh:mm:ss 형식 확인
|
||||
const dateTimeRegex = /^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})$/;
|
||||
const match = dateTimeStr.match(dateTimeRegex);
|
||||
|
||||
|
||||
if (match && match.length >= 6) {
|
||||
const year = match[1];
|
||||
const month = match[2];
|
||||
@@ -42,12 +42,13 @@ export const formatDisplayData = (data: { [key: string]: any }): { [key: string]
|
||||
const hour = match[4];
|
||||
const minute = match[5];
|
||||
const second = match[6];
|
||||
|
||||
|
||||
// 🎯 undefined 체크 후 parseInt 적용
|
||||
if (year && month && day && hour && minute) {
|
||||
const formattedMonth = parseInt(month);
|
||||
const formattedDay = parseInt(day);
|
||||
formattedData[fieldId] = `${year}년 ${formattedMonth}월 ${formattedDay}일 ${hour}시 ${minute}분 ${second}초`;
|
||||
// const formattedMonth = parseInt(month);
|
||||
// const formattedDay = parseInt(day);
|
||||
// formattedData[fieldId] = `${year}-${formattedMonth}-${formattedDay} ${hour}:${minute}:${second}`;
|
||||
formattedData[fieldId] = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||
} else {
|
||||
formattedData[fieldId] = String(value);
|
||||
}
|
||||
@@ -60,7 +61,7 @@ export const formatDisplayData = (data: { [key: string]: any }): { [key: string]
|
||||
// 🎯 기존 날짜 포맷팅 (yyyymmdd -> yyyy년 m월 d일)
|
||||
if (dateFields.includes(fieldId)) {
|
||||
const dateStr = String(value).trim();
|
||||
|
||||
|
||||
// 숫자 타입이거나 숫자로만 구성된 문자열인지 확인
|
||||
if (/^\d+$/.test(dateStr)) {
|
||||
formattedData[fieldId] = formatDateString(dateStr);
|
||||
@@ -74,7 +75,12 @@ export const formatDisplayData = (data: { [key: string]: any }): { [key: string]
|
||||
if (numberFields.includes(fieldId)) {
|
||||
const numValue = Number(value);
|
||||
if (!isNaN(numValue) && isFinite(numValue)) {
|
||||
formattedData[fieldId] = numValue.toLocaleString('ko-KR');
|
||||
// 소수점이 있는 경우 4자리까지 표시
|
||||
if (!Number.isInteger(numValue)) {
|
||||
formattedData[fieldId] = numValue.toLocaleString('ko-KR', { maximumFractionDigits: 4 });
|
||||
} else {
|
||||
formattedData[fieldId] = numValue.toLocaleString('ko-KR');
|
||||
}
|
||||
} else {
|
||||
formattedData[fieldId] = String(value);
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { SelectBox } from '@common/business_components/ui'
|
||||
import { Label } from '@common/components';
|
||||
|
||||
interface Option {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface SidoSigunguSelectBoxProps {
|
||||
sido: number;
|
||||
sigungu: number;
|
||||
onSidoChange: (id: number) => void;
|
||||
onSigunguChange: (id: number) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const sidoList: { id: number, name: string }[] = [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 26, name: '부산광역시' },
|
||||
{ id: 28, name: '인천광역시' },
|
||||
{ id: 30, name: '대전광역시' },
|
||||
{ id: 41, name: '경기도' },
|
||||
{ id: 42, name: '강원도' },
|
||||
{ id: 44, name: '충청남도' },
|
||||
{ id: 45, name: '전라북도' },
|
||||
{ id: 46, name: '전라남도' },
|
||||
{ id: 47, name: '경상북도' },
|
||||
{ id: 48, name: '경상남도' },
|
||||
];
|
||||
const sigunguMap: Record<number, { id: number; name: string }[]> = {
|
||||
26: [
|
||||
{ id: 0, name: '전체' },
|
||||
// ...부산산광역시의 시군구 추가
|
||||
],
|
||||
28: [
|
||||
{ id: 0, name: '전체' },
|
||||
// ...인천광역시의 시군구 추가
|
||||
],
|
||||
30: [
|
||||
{ id: 0, name: '전체' },
|
||||
// ...대전광역시의 시군구 추가
|
||||
],
|
||||
41: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 41820, name: '가평군' },
|
||||
{ id: 41550, name: '안성시' },
|
||||
{ id: 41830, name: '양평군' },
|
||||
{ id: 41670, name: '여주시' },
|
||||
{ id: 41500, name: '이천시' },
|
||||
{ id: 41430, name: '의왕시' },
|
||||
{ id: 41150, name: '의정부시' },
|
||||
{ id: 41480, name: '파주시' },
|
||||
{ id: 41650, name: '포천시' },
|
||||
// ...경기도의 시군구 추가
|
||||
],
|
||||
42: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 42170, name: '동해시' },
|
||||
{ id: 42110, name: '춘천시' },
|
||||
{ id: 42720, name: '홍천군' },
|
||||
// ...강원도의 시군구 추가
|
||||
],
|
||||
44: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 44770, name: '서천군' },
|
||||
// ...충청남도의 시군구 추가
|
||||
],
|
||||
45: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 45130, name: '군산시' },
|
||||
{ id: 45190, name: '남원시' },
|
||||
{ id: 45730, name: '무주군' },
|
||||
{ id: 45800, name: '부안군' },
|
||||
{ id: 45770, name: '순창군' },
|
||||
{ id: 45140, name: '익산시' },
|
||||
{ id: 45750, name: '임실군' },
|
||||
{ id: 45710, name: '완주군' },
|
||||
{ id: 45110, name: '전주시' },
|
||||
// ...전라북도의 시군구 추가
|
||||
],
|
||||
46: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 46810, name: "강진군" },
|
||||
{ id: 46770, name: "고흥군" },
|
||||
{ id: 46720, name: "곡성군" },
|
||||
{ id: 46730, name: "구례군" },
|
||||
{ id: 46230, name: "광양시" },
|
||||
{ id: 46170, name: "나주시" },
|
||||
{ id: 46110, name: "목포시" },
|
||||
{ id: 46840, name: "무안군" },
|
||||
{ id: 46150, name: "순천시" },
|
||||
{ id: 46910, name: "신안군" },
|
||||
{ id: 46130, name: "여수시" },
|
||||
{ id: 46890, name: "완도군" },
|
||||
// ...전라남도의 시군구 추가
|
||||
],
|
||||
47: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 47130, name: "경주시" },
|
||||
{ id: 47830, name: "고령군" },
|
||||
{ id: 47190, name: "구미시" },
|
||||
{ id: 47720, name: "군위군" },
|
||||
{ id: 47150, name: "김천시" },
|
||||
{ id: 47280, name: "문경시" },
|
||||
{ id: 47920, name: "봉화군" },
|
||||
{ id: 47250, name: "상주시" },
|
||||
{ id: 47770, name: "영덕군" },
|
||||
{ id: 47760, name: "영양군" },
|
||||
{ id: 47210, name: "영주시" },
|
||||
{ id: 47900, name: "예천군" },
|
||||
{ id: 47930, name: "울진군" },
|
||||
{ id: 47730, name: "의성군" },
|
||||
{ id: 47750, name: "청송군" },
|
||||
{ id: 47850, name: "칠곡군" },
|
||||
{ id: 47110, name: "포항시" },
|
||||
// ...경상북도의 시군구 추가
|
||||
],
|
||||
48: [
|
||||
{ id: 0, name: '전체' },
|
||||
{ id: 48310, name: "거제시" },
|
||||
{ id: 48250, name: "김해시" },
|
||||
{ id: 48270, name: "밀양시" },
|
||||
{ id: 48220, name: "통영시" },
|
||||
// ...경상남도의 시군구 추가
|
||||
],
|
||||
// ...나머지 시도별 시군구 추가
|
||||
};
|
||||
|
||||
export function SidoSigunguSelectBox({
|
||||
sido,
|
||||
sigungu,
|
||||
onSidoChange,
|
||||
onSigunguChange,
|
||||
className = "",
|
||||
}: SidoSigunguSelectBoxProps) {
|
||||
// 선택된 시도에 따라 시군구 리스트를 가져옴
|
||||
const sigunguList = useMemo(() => sigunguMap[sido] || [{ id: 0, name: '전체' }], [sido]);
|
||||
|
||||
return (
|
||||
<div className={`flex items-center justify-center ${className} mr-8`}>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시도</Label>
|
||||
<SelectBox
|
||||
className="w-60 mr-8"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={sidoList.map(opt => ({ val: opt.id, text: opt.name }))}
|
||||
value={sido}
|
||||
onSelectValue={val => onSidoChange(Number(val))}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시군구</Label>
|
||||
<SelectBox
|
||||
className="w-48"
|
||||
selectClass="w-48 rounded-lg bg-white truncate"
|
||||
label=""
|
||||
selectArray={sigunguList.map(opt => ({ val: opt.id, text: opt.name }))}
|
||||
value={sigungu}
|
||||
onSelectValue={val => onSigunguChange(Number(val))}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import React, { useState, useMemo, useEffect } from "react";
|
||||
import { SelectBox } from '@common/business_components/ui'
|
||||
import { Label } from '@common/components';
|
||||
import useStore from '../../store/useStore';
|
||||
import { getData } from '@/components/lib/api/index';
|
||||
|
||||
|
||||
interface ApiSelectOption {
|
||||
text: string;
|
||||
val: string;
|
||||
}
|
||||
interface CtpvSggPrcsFcltSelectBoxProps {
|
||||
sido: number;
|
||||
sigungu: number;
|
||||
sggName?: string;
|
||||
onSidoChange: (id: number) => void;
|
||||
onSigunguChange: (id: number) => void;
|
||||
setCtpvName?: (name: string) => void;
|
||||
setSggName?: (name: string) => void;
|
||||
className?: string;
|
||||
selectedSwagePrcsFclt?: string | number;
|
||||
onSwagePrcsFcltChange?: (value: string | number) => void;
|
||||
setIsLoading?: (isLoading: boolean) => void;
|
||||
}
|
||||
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '전체' };
|
||||
|
||||
export function CtpvSggPrcsFcltSelectBox({
|
||||
sido,
|
||||
sigungu,
|
||||
sggName,
|
||||
onSidoChange,
|
||||
setCtpvName,
|
||||
onSigunguChange,
|
||||
setSggName,
|
||||
className = "",
|
||||
selectedSwagePrcsFclt,
|
||||
onSwagePrcsFcltChange,
|
||||
setIsLoading,
|
||||
}: CtpvSggPrcsFcltSelectBoxProps) {
|
||||
|
||||
const [prcsFcltOptions, setPrcsFcltOptions] = useState<ApiSelectOption[]>([]);
|
||||
const { ctpv, sgg } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
onSidoChange(0);
|
||||
onSigunguChange(0);
|
||||
if (setCtpvName && setSggName) {
|
||||
setCtpvName("");
|
||||
setSggName("");
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
onSwagePrcsFcltChange?.('0');
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!sggName) {
|
||||
console.log("BaseLayout: Skipping fetchData because sigungu.val is:", sggName);
|
||||
setPrcsFcltOptions([]);
|
||||
if (setIsLoading) setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (setIsLoading) setIsLoading(true);
|
||||
try {
|
||||
|
||||
const queryParamsFacility = new URLSearchParams({
|
||||
lyrTypeNm: 'tb_il_swage_prcs_fclt',
|
||||
page: '0',
|
||||
size: '100000', // 요청하신 URL에 맞게 50으로 변경 가능
|
||||
sort: 'string',
|
||||
});
|
||||
const cqlFiltersFacility: string[] = [];
|
||||
if (sggName) cqlFiltersFacility.push(`sgg_nm ILIKE '%${sggName}%'`);
|
||||
if (cqlFiltersFacility.length > 0) queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and '));
|
||||
|
||||
console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
|
||||
|
||||
const facilityRes = await getData(`v1/fclts?${queryParamsFacility.toString()}`);
|
||||
|
||||
console.log('facilityRes.data', facilityRes.data);
|
||||
|
||||
const facilities = (facilityRes.data || [])
|
||||
.map((item: any) => ({
|
||||
text: item.swtrmtFcltNm,
|
||||
val: item.fid
|
||||
}))
|
||||
.sort((a: ApiSelectOption, b: ApiSelectOption) => a.text.localeCompare(b.text, 'ko'));
|
||||
setPrcsFcltOptions(facilities);
|
||||
} catch (error) {
|
||||
console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
|
||||
setPrcsFcltOptions([]);
|
||||
} finally {
|
||||
if (setIsLoading) setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [sggName]);
|
||||
|
||||
|
||||
return (
|
||||
<div className={`flex items-center justify-center ${className} mr-8`}>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시도</Label>
|
||||
<SelectBox
|
||||
className="w-60 mr-8"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={
|
||||
[defaultOption,
|
||||
...ctpv.map(opt => ({ val: opt.id, text: opt.name }))
|
||||
]
|
||||
}
|
||||
value={sido}
|
||||
onSelectValue={val => {
|
||||
onSidoChange(Number(val));
|
||||
if (setCtpvName) setCtpvName(ctpv.find(opt => Number(opt.id) === Number(val))?.name || "");
|
||||
}}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시군구</Label>
|
||||
<SelectBox
|
||||
className="w-48"
|
||||
selectClass="w-48 rounded-lg bg-white truncate"
|
||||
label=""
|
||||
selectArray={
|
||||
[defaultOption,
|
||||
...sgg
|
||||
.filter(opt => String(opt.id).slice(0, 2) === String(sido))
|
||||
.map(opt => ({ val: opt.id, text: opt.name }))
|
||||
]
|
||||
}
|
||||
value={sigungu}
|
||||
onSelectValue={val => {
|
||||
onSigunguChange(Number(val))
|
||||
if (setSggName) setSggName(sgg.find(opt => Number(opt.id) === Number(val))?.name || "");
|
||||
}}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
{selectedSwagePrcsFclt && onSwagePrcsFcltChange && (
|
||||
<>
|
||||
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap ml-8 mr-4">하수처리시설명</Label>
|
||||
<SelectBox
|
||||
className="w-[160px]"
|
||||
selectClass="rounded-lg bg-white truncate"
|
||||
label=""
|
||||
selectArray={[defaultOption, ...prcsFcltOptions]}
|
||||
value={selectedSwagePrcsFclt}
|
||||
onSelectValue={(val) => onSwagePrcsFcltChange?.(val)}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { getCmsData } from "@/components/lib/api";
|
||||
|
||||
|
||||
export interface Option { id: number, name: string }
|
||||
|
||||
export type BIZ_CD_TYPE = {
|
||||
lwpoCd: string,
|
||||
upCd: string,
|
||||
lwpoCdNm: string,
|
||||
unit: string,
|
||||
prntLwpoCd: null
|
||||
};
|
||||
|
||||
export const fetchCtpvSggData = async (set: (state: any) => void) => {
|
||||
try {
|
||||
const ctpvData = await getCmsData('/api/v1/code/cache/ADSTDSTRCD/Y');
|
||||
const ctpvList: Option[] = ctpvData
|
||||
.filter((item: any) => typeof item.lwpoCd === 'string' && item.lwpoCd.length === 2)
|
||||
.map((feature: any) => ({
|
||||
id: feature.lwpoCd,
|
||||
name: feature.lwpoCdNm
|
||||
}));
|
||||
|
||||
const sggList: Option[] = ctpvData
|
||||
.filter((item: any) => typeof item.lwpoCd === 'string' && item.lwpoCd.length !== 2)
|
||||
.map((item: any) => ({
|
||||
id: item.lwpoCd,
|
||||
name: item.lwpoCdNm
|
||||
}));
|
||||
|
||||
set({ ctpv: ctpvList, sgg: sggList });
|
||||
} catch (e: any) {
|
||||
// alert("시도, 시군구 데이터 로드 실패: " + e.message);
|
||||
console.error("CTPV 데이터 로드 실패:", e);
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchBIZ_SE_CD = async (set: (state: any) => void) => {
|
||||
try {
|
||||
const res = await getCmsData(`/api/v1/code/cache/BIZSECD`);
|
||||
set({ BIZ_SE_CD: res });
|
||||
} catch (e: any) {
|
||||
// alert("BIZ_SE_CD 데이터 로드 실패: " + e.message);
|
||||
console.error("BIZ_SE_CD 데이터 로드 실패:", e);
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchBIZ_TY_CD = async (set: (state: any) => void) => {
|
||||
try {
|
||||
const res = await getCmsData(`/api/v1/code/cache/BIZTYCD`);
|
||||
set({ BIZ_TY_CD: res });
|
||||
} catch (e: any) {
|
||||
// alert("BIZ_TY_CD 데이터 로드 실패: " + e.message);
|
||||
console.error("BIZ_TY_CD 데이터 로드 실패:", e);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import { create } from "zustand";
|
||||
import { getGeoData, getCmsData } from "@/components/lib/api";
|
||||
import { fetchCtpvSggData, fetchBIZ_SE_CD, fetchBIZ_TY_CD } from "./index";
|
||||
|
||||
interface Option { id: number, name: string }
|
||||
|
||||
export type BIZ_CD_TYPE = {
|
||||
lwpoCd: string,
|
||||
upCd: string,
|
||||
lwpoCdNm: string,
|
||||
unit: string,
|
||||
prntLwpoCd: null
|
||||
};
|
||||
|
||||
interface SnStoreState {
|
||||
ctpv: Option[];
|
||||
sgg: Option[];
|
||||
BIZ_SE_CD: BIZ_CD_TYPE[];
|
||||
BIZ_TY_CD: BIZ_CD_TYPE[];
|
||||
fetchCtpvSggData: () => Promise<void>;
|
||||
fetchBIZ_SE_CD: () => Promise<void>;
|
||||
fetchBIZ_TY_CD: () => Promise<void>;
|
||||
}
|
||||
|
||||
const useStore = create<SnStoreState>((set) => ({
|
||||
ctpv: [],
|
||||
sgg: [],
|
||||
BIZ_SE_CD: [],
|
||||
BIZ_TY_CD: [],
|
||||
fetchCtpvSggData: () => fetchCtpvSggData(set),
|
||||
fetchBIZ_SE_CD: () => fetchBIZ_SE_CD(set),
|
||||
fetchBIZ_TY_CD: () => fetchBIZ_TY_CD(set),
|
||||
}));
|
||||
|
||||
export default useStore;
|
||||
|
After Width: | Height: | Size: 919 B |
|
After Width: | Height: | Size: 810 B |
|
After Width: | Height: | Size: 896 B |
|
After Width: | Height: | Size: 837 B |
|
After Width: | Height: | Size: 790 B |
|
After Width: | Height: | Size: 855 B |
|
After Width: | Height: | Size: 923 B |
|
After Width: | Height: | Size: 479 B |
|
After Width: | Height: | Size: 774 B |
@@ -2,5 +2,6 @@ export { useMutationWrapper } from "./use-mutation-wrapper";
|
||||
export { useAlertStore } from "./use-alert-store";
|
||||
export { useMenuStore } from "./use-menu-store";
|
||||
export { useUserStore } from "./use-user-store";
|
||||
export { useDataStore } from "./use-data-store";
|
||||
export { usePrompt } from "./use-prompt";
|
||||
export { getCookie, setCookie, removeCookie } from "./cookie";
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useStore } from "zustand";
|
||||
import { useContext } from "react";
|
||||
import type { DataStore } from "../store/data-store";
|
||||
import { StoreContext } from "../providers/store-provider";
|
||||
|
||||
export const useDataStore = <T>(selector: (store: DataStore) => T): T => {
|
||||
const storeContext = useContext(StoreContext);
|
||||
|
||||
if (!storeContext) {
|
||||
throw new Error("useDataStore must be used within StoreProvider");
|
||||
}
|
||||
|
||||
return useStore(storeContext.dataStore, selector);
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
createAttachmentStore,
|
||||
createMenuStore,
|
||||
createUserStore,
|
||||
createDataStore,
|
||||
} from "../store";
|
||||
|
||||
type StoreContextType = {
|
||||
@@ -13,6 +14,7 @@ type StoreContextType = {
|
||||
attachmentStore: ReturnType<typeof createAttachmentStore>;
|
||||
menuStore: ReturnType<typeof createMenuStore>;
|
||||
userStore: ReturnType<typeof createUserStore>;
|
||||
dataStore: ReturnType<typeof createDataStore>;
|
||||
};
|
||||
|
||||
export const StoreContext = createContext<StoreContextType | undefined>(
|
||||
@@ -27,6 +29,7 @@ export const StoreProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
attachmentStore: createAttachmentStore(),
|
||||
menuStore: createMenuStore(),
|
||||
userStore: createUserStore(),
|
||||
dataStore: createDataStore(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { create } from "zustand";
|
||||
import { getCmsData } from "@/components/lib/api";
|
||||
|
||||
export interface Option { id: number, name: string }
|
||||
|
||||
export type BIZ_CD_TYPE = {
|
||||
lwpoCd: string,
|
||||
upCd: string,
|
||||
lwpoCdNm: string,
|
||||
unit: string,
|
||||
prntLwpoCd: null
|
||||
};
|
||||
|
||||
export type DataState = {
|
||||
ctpv: Option[];
|
||||
sgg: Option[];
|
||||
BIZ_SE_CD: BIZ_CD_TYPE[];
|
||||
BIZ_TY_CD: BIZ_CD_TYPE[];
|
||||
}
|
||||
|
||||
export type FetchData = {
|
||||
fetchCtpvSggData: () => Promise<void>;
|
||||
fetchBIZ_SE_CD: () => Promise<void>;
|
||||
fetchBIZ_TY_CD: () => Promise<void>;
|
||||
};
|
||||
|
||||
export type DataStore = DataState & FetchData;
|
||||
|
||||
export const defaultInitState: DataState = {
|
||||
ctpv: [],
|
||||
sgg: [],
|
||||
BIZ_SE_CD: [],
|
||||
BIZ_TY_CD: [],
|
||||
};
|
||||
|
||||
const fetchCtpvSggData = async (set: (state: any) => void) => {
|
||||
try {
|
||||
const ctpvData = await getCmsData('/api/v1/code/cache/ADSTDSTRCD');
|
||||
console.log("CTPV Data:", ctpvData);
|
||||
const ctpvList: Option[] = ctpvData
|
||||
.filter((item: any) => typeof item.lwpoCd === 'string' && item.lwpoCd.length === 2)
|
||||
.map((feature: any) => ({
|
||||
id: feature.lwpoCd,
|
||||
name: feature.lwpoCdNm
|
||||
}))
|
||||
.sort((a: Option, b: Option) => a.name.localeCompare(b.name, 'ko'));
|
||||
|
||||
const sggList: Option[] = ctpvData
|
||||
.filter((item: any) => typeof item.lwpoCd === 'string' && item.lwpoCd.length !== 2)
|
||||
.map((item: any) => ({
|
||||
id: item.lwpoCd,
|
||||
name: item.lwpoCdNm
|
||||
}))
|
||||
.sort((a: Option, b: Option) => a.name.localeCompare(b.name, 'ko'));
|
||||
|
||||
set({ ctpv: ctpvList, sgg: sggList });
|
||||
} catch (e: any) {
|
||||
// alert("시도, 시군구 데이터 로드 실패: " + e.message);
|
||||
console.error("CTPV 데이터 로드 실패:", e);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchBIZ_SE_CD = async (set: (state: any) => void) => {
|
||||
try {
|
||||
const res = await getCmsData(`/api/v1/code/cache/BIZSECD`);
|
||||
set({ BIZ_SE_CD: res });
|
||||
} catch (e: any) {
|
||||
// alert("BIZ_SE_CD 데이터 로드 실패: " + e.message);
|
||||
console.error("BIZ_SE_CD 데이터 로드 실패:", e);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchBIZ_TY_CD = async (set: (state: any) => void) => {
|
||||
try {
|
||||
const res = await getCmsData(`/api/v1/code/cache/BIZTYCD`);
|
||||
set({ BIZ_TY_CD: res });
|
||||
} catch (e: any) {
|
||||
// alert("BIZ_TY_CD 데이터 로드 실패: " + e.message);
|
||||
console.error("BIZ_TY_CD 데이터 로드 실패:", e);
|
||||
}
|
||||
};
|
||||
|
||||
export const createDataStore = (initState: DataState = defaultInitState) => {
|
||||
return create<DataStore>((set) => ({
|
||||
...initState,
|
||||
fetchCtpvSggData: () => fetchCtpvSggData(set),
|
||||
fetchBIZ_SE_CD: () => fetchBIZ_SE_CD(set),
|
||||
fetchBIZ_TY_CD: () => fetchBIZ_TY_CD(set),
|
||||
}));
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export { createAlertStore } from "./alert-store";
|
||||
export { createAttachmentStore } from "./attachment-store";
|
||||
export { createMenuStore, type BbsType } from "./menu-store";
|
||||
export { createUserStore, type IUserInfo } from "./user-store";
|
||||
export { createDataStore, type DataStore } from "./data-store";
|
||||
@@ -10,6 +10,8 @@ NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useWrStore } from "@/app/store";
|
||||
import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
|
||||
import { tableData } from "@/components/lib/func/commom";
|
||||
import { Title, UiTable } from "@common/business_components/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
interface Props {
|
||||
seletectedLayerNm: string;
|
||||
viewMode: 'list' | 'detail' | 'nothing';
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
onClose: () => void;
|
||||
isRuseReg: { data: any | undefined };
|
||||
}
|
||||
const HasuFacility = ({ seletectedLayerNm, viewMode, onClose, onFeatureSelect, isRuseReg }: Props) => {
|
||||
const {
|
||||
code,
|
||||
pbnst,
|
||||
pubHasuInit,
|
||||
} = useWrStore((state) => state);
|
||||
const { pubFacHeadList, pbnstHasuRes, pbnstHasuResCount } = pbnst;
|
||||
const [hasuTable, setHasuTable] = useState<pubHasuType[]>();
|
||||
const [hasuPage, setHasuPage] = useState<number>(1);
|
||||
useEffect(() => {
|
||||
if (code.sggCd.length && pbnstHasuRes.length > 0) {
|
||||
setHasuTable(tableData(pbnstHasuRes, { code }));
|
||||
}
|
||||
}, [code, pbnstHasuRes]);
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '하수처리시설' || viewMode != 'list') return;
|
||||
if (!pbnst.isInit) {
|
||||
pubHasuInit("1", "0");
|
||||
}
|
||||
}, [pbnst.isInit, pubHasuInit]);
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '하수처리시설' || viewMode != 'list') return;
|
||||
pubHasuInit(String(hasuPage), "0");
|
||||
}, [hasuPage, pubHasuInit]);
|
||||
|
||||
const hasuCellClick = (index: number | undefined) => {
|
||||
if (index !== undefined && pbnstHasuRes !== undefined) {
|
||||
onFeatureSelect(pbnstHasuRes[index]);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
return (viewMode == 'list' ? (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수처리시설 현황" />
|
||||
</div>
|
||||
{/* 높이 조정 */}
|
||||
<UiTable
|
||||
tableData={hasuTable || []}
|
||||
headlist={pubFacHeadList}
|
||||
pageSize={11}
|
||||
total={pbnstHasuResCount || 0}
|
||||
headName={""}
|
||||
cellClick={hasuCellClick}
|
||||
onPageChange={setHasuPage}
|
||||
/>
|
||||
</div>
|
||||
) :
|
||||
(
|
||||
<div style={{ overflowX: 'hidden' }}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수처리시설 상세정보" />
|
||||
</div>
|
||||
{/* 높이 조정 */}
|
||||
<UiTable
|
||||
tableData={isRuseReg?.data || []}
|
||||
headlist={pubFacHeadList.slice(0, 4)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"위치정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
<UiTable
|
||||
tableData={isRuseReg?.data || []}
|
||||
headlist={pubFacHeadList.slice(4)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"기본정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
export default HasuFacility;
|
||||
@@ -0,0 +1,83 @@
|
||||
import { useWrStore } from '@/app/store';
|
||||
import type { customHtdflBdst } from '@/app/store/ruseFcltMng/fcltReg/htdflReg';
|
||||
import { tableData } from '@/components/lib/func/commom';
|
||||
import SttnDetailModal from '@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal';
|
||||
import { Title, UiTable } from '@common/business_components/ui';
|
||||
import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan';
|
||||
import React, { useEffect, useState } from 'react'
|
||||
interface Props {
|
||||
seletectedLayerNm: string;
|
||||
viewMode: 'list' | 'detail' | 'nothing';
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
onClose: () => void;
|
||||
htdflDetailModal: { isOpen: boolean; data: customHtdflBdst | undefined };
|
||||
}
|
||||
const HtdflFacility = ({ seletectedLayerNm, viewMode, onFeatureSelect, onClose, htdflDetailModal }: Props) => {
|
||||
const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state);
|
||||
const { htdflSttnRes, htdflSttnResCount } = htdflSttn;
|
||||
const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>();
|
||||
const [htdflPage, setHtdflPage] = useState(1)
|
||||
// 온배수시설 cellClick 이벤트 (상세정보)
|
||||
const htdflCellClick = (index: number | undefined) => {
|
||||
if (index !== undefined && htdflTable !== undefined) {
|
||||
onFeatureSelect(htdflTable[index]);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '온배수시설' || viewMode != 'list') return;
|
||||
if (!htdflSttn.isInit) {
|
||||
initHtdfl(false, 1, "20", "0", '', code.cityCd, code.sggCd);
|
||||
}
|
||||
}, [code.cityCd, code.sggCd, htdflSttn.isInit, initHtdfl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '온배수시설' || viewMode != 'list') return;
|
||||
const data = tableData(htdflSttnRes, { code });
|
||||
const chData = data
|
||||
.map((item: any) => ({
|
||||
...item,
|
||||
...item.reuseBuilding[0],
|
||||
...item.reuseBuilding[0].htdflObj,
|
||||
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
|
||||
}))
|
||||
.filter((item: any) => item.htdflObj !== undefined);
|
||||
setHtdflTable(chData);
|
||||
}, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '온배수시설' || viewMode != 'list') return;
|
||||
htdflSttnSearch(true, htdflPage, "20", '', '', code.cityCd, code.sggCd);
|
||||
}, [code.cityCd, code.sggCd, htdflPage, htdflSttnSearch]);
|
||||
return (
|
||||
viewMode == 'detail' ? (
|
||||
<SttnDetailModal type={"HTB"} formObj={htdflDetailModal.data as customHtdflBdst} />
|
||||
) : (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<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={htdflTable || []}
|
||||
headlist={htdflSttn.htdflSttHeadList}
|
||||
pageSize={20}
|
||||
total={htdflSttnResCount || 0}
|
||||
cellClick={(index: number | undefined) => {
|
||||
htdflCellClick(index ?? undefined);
|
||||
}}
|
||||
onPageChange={setHtdflPage}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />
|
||||
</UiTable>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export default HtdflFacility
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useWrStore } from "@/app/store";
|
||||
import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
|
||||
import { tableData } from "@/components/lib/func/commom";
|
||||
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
|
||||
import { Title, UiTable } from "@common/business_components/ui";
|
||||
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
|
||||
import { useEffect, useState } from "react";
|
||||
interface Props {
|
||||
seletectedLayerNm: string;
|
||||
viewMode: 'list' | 'detail' | 'nothing';
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
onClose: () => void;
|
||||
rainDetailModal: { isOpen: boolean; data: customRainBdst | undefined };
|
||||
}
|
||||
const RainFacility = ({seletectedLayerNm,viewMode,onFeatureSelect,onClose,rainDetailModal}:Props) => {
|
||||
// 빗물재이용시설 변수
|
||||
const { code, rainSttn, rainSttnSearch, rainSttnInit } = useWrStore(
|
||||
(state) => state
|
||||
);
|
||||
const { rainSttnRes, rainSttnResCount } = rainSttn;
|
||||
const [rainTable, setRainTable] = useState<customRainBdst[]>();
|
||||
const [rainPage, setRainPage] = useState(1)
|
||||
|
||||
// 빗물재이용시설 cellClick 이벤트 (상세정보)
|
||||
const rainCellClick = (index: number | undefined, headId: string) => {
|
||||
if (
|
||||
index !== undefined &&
|
||||
rainTable !== undefined &&
|
||||
headId !== 'chBuildingYn') {
|
||||
onFeatureSelect(rainTable[index]);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '빗물이용시설' || viewMode != 'list') return;
|
||||
console.log('빗물이용시설 초기값 호출', viewMode)
|
||||
if (!rainSttn.isInit) {
|
||||
rainSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd);
|
||||
}
|
||||
}, [code.cityCd, code.sggCd, rainSttn.isInit, rainSttnInit]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = tableData(rainSttnRes, { code });
|
||||
const chData = data
|
||||
.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
...item.reuseBuilding[0],
|
||||
...item.reuseBuilding[0].rainyObj,
|
||||
dtfcltCpct: item.reuseBuilding[0].rainyObj.dtfcltCpct === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].rainyObj.dtfcltCpct,
|
||||
instlCst: item.reuseBuilding[0].instlCst === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].instlCst,
|
||||
instlCstSprtAmt: item.reuseBuilding[0].instlCstSprtAmt === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].instlCstSprtAmt,
|
||||
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
|
||||
pntfColArea: item.reuseBuilding[0].rainyObj.pntfColArea === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].rainyObj.pntfColArea,
|
||||
filtrTrtmfctCpct: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct,
|
||||
archArea: Math.floor(item.archArea) === -1 ? "확인불가" : item.archArea,
|
||||
};
|
||||
})
|
||||
.filter((item: any) => item.rainyObj !== undefined);
|
||||
setRainTable(chData);
|
||||
}, [code, rainSttnRes, rainSttn.rainSttHeadList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '빗물이용시설' || viewMode != 'list') return;
|
||||
rainSttnSearch(false, rainPage, "20", '', '', code.cityCd, code.sggCd);
|
||||
}, [code.cityCd, code.sggCd, rainPage, rainSttnSearch]);
|
||||
return (viewMode == 'detail' ?
|
||||
(<SttnDetailModal type={"RNB"} formObj={rainDetailModal.data as customRainBdst} />)
|
||||
: (
|
||||
<>
|
||||
<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 || []}
|
||||
headlist={rainSttn.rainSttHeadList}
|
||||
pageSize={20}
|
||||
total={rainSttnResCount}
|
||||
cellClick={(index: number | undefined, headId: string) => {
|
||||
rainCellClick(index ?? undefined, headId);
|
||||
}}
|
||||
onPageChange={setRainPage}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />
|
||||
</UiTable>
|
||||
</>
|
||||
))
|
||||
}
|
||||
export default RainFacility;
|
||||
@@ -0,0 +1,146 @@
|
||||
import { useWrStore } from "@/app/store";
|
||||
import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
|
||||
import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type";
|
||||
import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal";
|
||||
import { Title, UITab, UiTable } from "@common/business_components/ui";
|
||||
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
interface Props {
|
||||
seletectedLayerNm: string;
|
||||
viewMode: 'list' | 'detail' | 'nothing';
|
||||
setFcltPk: (pk: string) => void;
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
onClose: () => void;
|
||||
actTabData: { id: string; name: string; data: any } | undefined;
|
||||
tabData: { id: string; name: string; data: any }[];
|
||||
}
|
||||
const ResueFacility = ({ viewMode, seletectedLayerNm, tabData, actTabData, setFcltPk, onFeatureSelect, onClose}: Props) => {
|
||||
const [activeTab, setActiveTab] = useState<string>(''); // 현재 활성화된 탭
|
||||
// 하수재이용시설 변수
|
||||
const {
|
||||
pbnstSttn,
|
||||
pbnstSttnSearch,
|
||||
pbnSttnInit,
|
||||
getFlcts
|
||||
} = useWrStore((state) => state);
|
||||
const { fclts, pbnstSttnResCount } = pbnstSttn
|
||||
const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>()
|
||||
// 재이용시설 cellClick 이벤트 (상세정보)
|
||||
const pbnstCellClick = (index: number | undefined) => {
|
||||
if (
|
||||
index !== undefined &&
|
||||
pbnstSttn.pbnstSttnRes &&
|
||||
pbnstSttn.pbnstSttnRes[index]
|
||||
) {
|
||||
setFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltId);
|
||||
onFeatureSelect(pbnstSttn.pbnstSttnRes[index]);
|
||||
onClose()
|
||||
}
|
||||
};
|
||||
|
||||
// 재이용 현황 초기값
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return;
|
||||
if (!pbnstSttn.isInit) {
|
||||
getFlcts()
|
||||
}
|
||||
}, [seletectedLayerNm, pbnstSttn.isInit]);
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return;
|
||||
pbnSttnInit("1", "20", {
|
||||
partValue: "A",
|
||||
sidoValue: '0',
|
||||
sigunValue: "0",
|
||||
regionValue: "",
|
||||
unitWaterShedValue: "",
|
||||
middleRegionValue: "",
|
||||
envPartValue: "",
|
||||
sidoText: "",
|
||||
sigunText: ""
|
||||
}, String(new Date().getFullYear()), pbnstSttn.fclts);
|
||||
}, [fclts, pbnSttnInit, pbnstSttn.fclts, seletectedLayerNm])
|
||||
useEffect(() => {
|
||||
setBdstTable(pbnstSttn.pbnstSttnRes);
|
||||
}, [seletectedLayerNm, pbnstSttn.pbnstSttnRes]);
|
||||
|
||||
// 재이용시설 조회
|
||||
// 재이용시설 페이징처리 관련 함수
|
||||
const onPageChangeHandler = useCallback((page: number) => {
|
||||
if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return;
|
||||
pbnstSttnSearch(
|
||||
false,
|
||||
String(page),
|
||||
String(20),
|
||||
{
|
||||
partValue: "A",
|
||||
sidoValue: "0",
|
||||
sigunValue: "0",
|
||||
regionValue: "",
|
||||
unitWaterShedValue: "",
|
||||
middleRegionValue: "",
|
||||
envPartValue: "",
|
||||
sidoText: "",
|
||||
sigunText: "",
|
||||
},
|
||||
"",
|
||||
String(new Date().getFullYear()), fclts
|
||||
);
|
||||
}, [fclts, pbnstSttnSearch]);
|
||||
// FIXME: 윤선화차장님 요구사항으로 엑셀 다운로드 삭제.
|
||||
// const excelClick = useCallback(() => {
|
||||
// // if (codeData) pbnstSttnSearch(1, "10000", codeData);
|
||||
// excelDownload(
|
||||
// [[]],
|
||||
// "하수재이용시설 현황",
|
||||
// "sample_pbnstSttn.xlsx",
|
||||
// "A1:U2"
|
||||
// );
|
||||
// }, [pbnstSttnSearch]);
|
||||
return (
|
||||
viewMode == 'detail' ?
|
||||
// 재이용시설 상세
|
||||
<>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수재이용시설 상세정보" />
|
||||
</div>
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<UITab
|
||||
defaultValue={activeTab}
|
||||
tabList={tabData.map((tab) => ({
|
||||
value: tab.id,
|
||||
label: tab.name,
|
||||
onClick: () => setActiveTab(tab.id),
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
<PbnstSttnModal
|
||||
data={actTabData?.data[0] || ({} as customPbnst)}
|
||||
titleVisible={false}
|
||||
/>
|
||||
</> :
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<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 || []}
|
||||
headlist={pbnstSttn.pbnstSttHeadList}
|
||||
pageSize={20}
|
||||
total={pbnstSttnResCount || 0}
|
||||
cellClick={(index: number | undefined) => {
|
||||
pbnstCellClick(index);
|
||||
}}
|
||||
onPageChange={onPageChangeHandler}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />
|
||||
</UiTable>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ResueFacility;
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useWrStore } from '@/app/store';
|
||||
import { tableData, formatPhoneNumber } from '@/components/lib/func/commom';
|
||||
import { Title, UiTable } from '@common/business_components/ui';
|
||||
import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form';
|
||||
import moment from 'moment';
|
||||
import React, { useEffect, useState } from 'react'
|
||||
interface Props {
|
||||
seletectedLayerNm: string;
|
||||
viewMode: 'list' | 'detail' | 'nothing';
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
onClose: () => void;
|
||||
isDmdModal: { data: any | undefined };
|
||||
}
|
||||
const ReuseMngFacility = ({seletectedLayerNm, viewMode, onFeatureSelect, onClose, isDmdModal}:Props) => {
|
||||
const { code,
|
||||
coSttn,
|
||||
coSttnInit,
|
||||
coSttnSearch,
|
||||
} = useWrStore((state) => state);
|
||||
const [mngTable, setMngTable] = useState<DemandCompType[]>();
|
||||
const [mngPage, setMngPage] = useState(1);
|
||||
const { coSttnRes, coSttnResCount, coSttHeadList } = coSttn;
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '수요처' || viewMode != 'list') return;
|
||||
if (!coSttn.isInit) {
|
||||
coSttnInit("1", "20", "0");
|
||||
}
|
||||
}, [coSttn.isInit, coSttnInit]);
|
||||
const coMngCellClick = (index: number | undefined) => {
|
||||
if (index !== undefined && mngTable !== undefined) {
|
||||
onFeatureSelect(mngTable[index]);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '수요처' || viewMode!='list') return;
|
||||
if (coSttnRes) {
|
||||
if (coSttnRes?.length > 0 && code.cityCd.length > 0) {
|
||||
const data = tableData(coSttnRes, { code });
|
||||
const chData = data.map((item: any) => ({
|
||||
...item,
|
||||
telno: formatPhoneNumber(item.telno || undefined),
|
||||
regDt: moment(item.regDt).format("YYYY-MM-DD"),
|
||||
dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5, 10)}-${item.dmdInfoId.slice(10)}`
|
||||
}));
|
||||
// .filter((item: any) => item !== undefined);
|
||||
setMngTable(chData);
|
||||
}
|
||||
}
|
||||
}, [coSttnRes, code]);
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '수요처' || viewMode!='list') return;
|
||||
coSttnSearch(String(mngPage), "20", String(0));
|
||||
}, [coSttnSearch, mngPage]);
|
||||
return (
|
||||
viewMode == 'detail'
|
||||
? (
|
||||
<div style={{ overflowX: 'hidden' }}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="수요처 상세정보" />
|
||||
</div>
|
||||
{/* 높이 조정 */}
|
||||
<UiTable
|
||||
tableData={isDmdModal?.data || []}
|
||||
headlist={coSttHeadList.slice(0, 3)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"위치정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
<UiTable
|
||||
tableData={isDmdModal?.data || []}
|
||||
headlist={coSttHeadList.slice(3)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"기본정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
</div>
|
||||
) : (<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="수요처 현황" />
|
||||
</div>
|
||||
<UiTable
|
||||
headName={[]}
|
||||
tableData={mngTable || []}
|
||||
headlist={coSttHeadList}
|
||||
pageSize={20}
|
||||
total={coSttnResCount + 10 || 0}
|
||||
cellClick={(index: number | undefined) => {
|
||||
coMngCellClick(index);
|
||||
// setUpdateData(coSttnRes[index as number]);
|
||||
}}
|
||||
onPageChange={(page) => {
|
||||
setMngPage(page);
|
||||
}}
|
||||
></UiTable></div>)
|
||||
)
|
||||
}
|
||||
|
||||
export default ReuseMngFacility
|
||||
@@ -0,0 +1,107 @@
|
||||
import { useWrStore } from "@/app/store";
|
||||
import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
|
||||
import { tableData } from "@/components/lib/func/commom";
|
||||
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
|
||||
import { Title, UiTable } from "@common/business_components/ui";
|
||||
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
|
||||
import { useEffect, useState } from "react";
|
||||
interface Props {
|
||||
seletectedLayerNm: string;
|
||||
viewMode: 'list' | 'detail' | 'nothing';
|
||||
onFeatureSelect: (feature: any) => void;
|
||||
onClose: () => void;
|
||||
rswtrDetailModal: { isOpen: boolean; data: customRswtBdst | undefined };
|
||||
}
|
||||
const RswtFacility = ({ seletectedLayerNm, viewMode, onFeatureSelect, onClose, rswtrDetailModal }: Props) => {
|
||||
const { code, rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore(
|
||||
(state) => state
|
||||
);
|
||||
const { rswtSttnRes, rswtSttnResCount } = rswtSttn;
|
||||
const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>()
|
||||
const [rswtrPage, setRswtrPage] = useState(1);
|
||||
// 중수도시설 cellClick 이벤트 (상세정보)
|
||||
const rswtCellClick = (index: number | undefined, headId: string) => {
|
||||
if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) {
|
||||
// setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] });
|
||||
// setViewMode('detail')
|
||||
onFeatureSelect(rswtrTable[index]);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '중수도시설' || viewMode != 'list') return;
|
||||
if (!rswtSttn.isInit) {
|
||||
rswtSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd);
|
||||
}
|
||||
}, [code.cityCd, code.sggCd, rswtSttn.isInit, rswtSttnInit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '중수도시설' || viewMode != 'list') return;
|
||||
const data = tableData(rswtSttnRes, { code });
|
||||
const chData = data
|
||||
.map((item: customRswtBdst) => {
|
||||
return {
|
||||
...item,
|
||||
...item.reuseBuilding[0],
|
||||
...item.reuseBuilding[0]?.rswtrObj,
|
||||
chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N',
|
||||
instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst,
|
||||
instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt,
|
||||
archArea: item?.archArea.split('.')[0] !== '-1' ? item?.archArea.split('.')[0] : '확인불가'
|
||||
}
|
||||
})
|
||||
.filter(
|
||||
(item: customRswtBdst) =>
|
||||
item.reuseBuilding[0]?.rswtrObj !== undefined
|
||||
);
|
||||
setRswtrTable(chData);
|
||||
}, [code, rswtSttnRes]);
|
||||
// FIXME: 윤선화 차장님 요구사항으로 엑셀 다운로드 삭제.
|
||||
// const rswtSttnExcelDownload = useCallback(() => {
|
||||
// if (rswtSttn.excelList.length > 0) {
|
||||
// if (rswtSttn.excelList.length === 0) return;
|
||||
// excelDownload(
|
||||
// rswtSttn.excelList,
|
||||
// "중수도시설 현황",
|
||||
// "sample_rswtSttn.xlsx",
|
||||
// "A1:W3",
|
||||
// );
|
||||
// }
|
||||
// }, [rswtSttn.excelList])
|
||||
|
||||
useEffect(() => {
|
||||
if (seletectedLayerNm !== '중수도시설' || viewMode != 'list') return;
|
||||
rswtSttnSearch(false, rswtrPage, "20", '', '', code.cityCd, code.sggCd);
|
||||
}, [code.cityCd, code.sggCd, rswtSttnSearch, rswtrPage]);
|
||||
return (
|
||||
viewMode == 'detail' ?
|
||||
// 중수도시설 상세
|
||||
(
|
||||
<SttnDetailModal type={"RST"} formObj={rswtrDetailModal.data as customRswtBdst} />
|
||||
) : (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="중수도시설 현황" />
|
||||
{/* <div className="flex gap-2 mt-8">
|
||||
<Button className={btnClass} color="primary" onClick={rswtSttnExcelDownload}>
|
||||
엑셀다운로드
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
<UiTable
|
||||
headName={[]}
|
||||
tableData={rswtrTable || []}
|
||||
headlist={rswtSttn.rswtSttHeadList}
|
||||
pageSize={20}
|
||||
total={rswtSttnResCount}
|
||||
cellClick={(index: number | undefined, headId: string) => {
|
||||
rswtCellClick(index, headId);
|
||||
}}
|
||||
onPageChange={setRswtrPage}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />
|
||||
</UiTable>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
export default RswtFacility;
|
||||
@@ -0,0 +1,183 @@
|
||||
"use client"
|
||||
import NewWindowPortal from '@/app/map/components/newWindowPortal';
|
||||
import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
|
||||
import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
|
||||
import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
|
||||
import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
|
||||
import { useGisContext } from "@common/business_components/ui/map/Gis";
|
||||
import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import ResueFacility from './layerPopup/reuseFacility';
|
||||
import { detainSttn, fetchHasuDetail, fetchpbnstDetail } from '../util';
|
||||
import HasuFacility from './layerPopup/hasuFacility';
|
||||
import RainFacility from './layerPopup/rainFacility';
|
||||
import RswtFacility from './layerPopup/rswtFacility';
|
||||
import HtdflFacility from './layerPopup/htdflFacility';
|
||||
import ReuseMngFacility from './layerPopup/reuseMngFacility';
|
||||
|
||||
interface NewWindowPopupProps {
|
||||
featureData: any;
|
||||
seletectedLayerNm?: string
|
||||
onFeatureSelect: (data: any) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
featureData,
|
||||
seletectedLayerNm,
|
||||
onFeatureSelect,
|
||||
onClose
|
||||
}) => {
|
||||
const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; };
|
||||
const [tabData, setTabData] = useState<{ id: string; name: string; data: any }[]>([]);
|
||||
const [activeTab, setActiveTab] = useState<string>('0');
|
||||
const [viewMode, setViewMode] = useState<'list' | 'detail' | 'nothing'>('list');
|
||||
const [fcltPk, setFcltPk] = useState<string>('');
|
||||
|
||||
|
||||
// onClose 함수를 useCallback으로 메모이제이션
|
||||
const memoizedOnClose = useCallback(() => {
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
// 빗물재이용시설 변수
|
||||
const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false });
|
||||
const [rainList, setRainList] = useState<customRainBdst[]>([]);
|
||||
|
||||
// 중수도시설 변수
|
||||
const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false });
|
||||
const [rswtrList, setRswtrList] = useState<customRswtBdst[]>([]);
|
||||
|
||||
// 온배수시설 변수
|
||||
const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false });
|
||||
const [htdflList, setHtdflList] = useState<customHtdflBdst[]>([]);
|
||||
|
||||
// 하수처리시설 변수
|
||||
const [hasuList, setHasuList] = useState<pubHasuType[]>([]);
|
||||
const [isRuseReg, setIsRuseReg] = useState<{
|
||||
data: any | undefined;
|
||||
}>({ data: undefined });
|
||||
|
||||
// 수요처관리 변수
|
||||
const [isDmdModal, setIsDmdModal] = useState<{
|
||||
isMoal: boolean;
|
||||
data: any | undefined;
|
||||
}>({ isMoal: false, data: undefined });
|
||||
const [mngList, setMngList] = useState<DemandCompType[]>([]);
|
||||
|
||||
///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 ///////////////////////////////////////////////
|
||||
const getSelectedLayers = () => {
|
||||
console.trace('호출확인', 1)
|
||||
let result = null;
|
||||
const layers = gisMap ? gisMap.getLayers().getArray().filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기
|
||||
let featureLayer: { get: (key: string) => any } = { get: () => null };
|
||||
layers.forEach((layer: any) => {
|
||||
const source = layer.getSource();
|
||||
if (source && source.hasFeature && featureData
|
||||
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
|
||||
featureLayer = layer; // 피처가 속한 레이어 찾기)
|
||||
}
|
||||
});
|
||||
result = featureLayer.get('lyrNm');
|
||||
return result;
|
||||
};
|
||||
// 상세 목록 데이터 상태 처리
|
||||
useEffect(() => {
|
||||
if (hasuList.length > 0) setIsRuseReg({ data: [hasuList[0]] });
|
||||
}, [hasuList])
|
||||
useEffect(() => {
|
||||
if (rainList.length > 0) setRainDetailModal({ isOpen: true, data: rainList[0] });
|
||||
}, [rainList])
|
||||
useEffect(() => {
|
||||
if (rswtrList.length > 0) setRswtrDetailModal({ isOpen: true, data: rswtrList[0] });
|
||||
}, [rswtrList])
|
||||
useEffect(() => {
|
||||
if (htdflList.length > 0) setHtdflDetailModal({ isOpen: true, data: htdflList[0] });
|
||||
}, [htdflList])
|
||||
useEffect(() => {
|
||||
if (mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] });
|
||||
}, [mngList])
|
||||
|
||||
const lastCalledIdRef = useRef<string | null>(null);
|
||||
const [layerId, setLayerId] = useState<string | null>();
|
||||
|
||||
useEffect(() => {
|
||||
const layers = getSelectedLayers();
|
||||
setLayerId(layers);
|
||||
}, []); // 최초 렌더링 시 호출
|
||||
|
||||
// 피쳐 데이터가 변경될 때마다 호출
|
||||
useEffect(() => {
|
||||
if (!featureData || typeof featureData.getProperties !== "function" || featureData.getProperties() == '') return;
|
||||
const selectedProps = featureData.getProperties();
|
||||
const currentId = featureData.getId?.() || selectedProps?.id || selectedProps?.fid;
|
||||
// 유효하지 않은 ID는 아예 무시
|
||||
if (!currentId || typeof currentId !== 'string') return;
|
||||
// 이전과 동일하면 무시
|
||||
if (!currentId || currentId === lastCalledIdRef.current) return;
|
||||
lastCalledIdRef.current = currentId // 새로운 피처일 때만 API 호출
|
||||
const layerId = getSelectedLayers?.();
|
||||
if (!layerId || typeof layerId !== 'string') return;
|
||||
const featureCd =
|
||||
layerId === '하수재이용시설' ? selectedProps?.swtrmt_fclt_cd :
|
||||
['빗물이용시설', '중수도시설', '온배수시설', '수요처'].includes(layerId) ? selectedProps?.bdst_nm :
|
||||
layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm :
|
||||
null;
|
||||
if (!featureCd || featureCd === fcltPk) return;
|
||||
if (layerId === '하수재이용시설') {
|
||||
fetchpbnstDetail(featureCd, setTabData);
|
||||
} else if (layerId === '하수처리시설') {
|
||||
fetchHasuDetail(featureCd, selectedProps?.swtrmt_fclt_cd).then((data) => { setHasuList(data) })
|
||||
} else {
|
||||
detainSttn(featureCd, layerId).then((res) => {
|
||||
if (layerId === '빗물이용시설') setRainList(res);
|
||||
else if (layerId === '중수도시설') setRswtrList(res);
|
||||
else if (layerId === '온배수시설') { setHtdflList(res); setHtdflDetailModal({ isOpen: true, data: res[0] }); }
|
||||
else if (layerId === '수요처') setMngList(res);
|
||||
});
|
||||
}
|
||||
setFcltPk(featureCd);
|
||||
setActiveTab(featureCd);
|
||||
setViewMode("detail");
|
||||
}, [featureData]);
|
||||
///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 ///////////////////////////////////////////////
|
||||
|
||||
// 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수
|
||||
const renderConditionalContent = () => {
|
||||
const actTabData = tabData.find((tab) => tab.id === activeTab);
|
||||
if (layerId === null || layerId === undefined) {
|
||||
setLayerId(seletectedLayerNm || '');
|
||||
}
|
||||
switch (layerId) {
|
||||
case '하수재이용시설':
|
||||
return (<ResueFacility viewMode={viewMode} seletectedLayerNm={layerId} tabData={tabData} actTabData={actTabData} setFcltPk={setFcltPk} onFeatureSelect={onFeatureSelect} onClose={onClose} />)
|
||||
case '하수처리시설':
|
||||
return (<HasuFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} isRuseReg={isRuseReg} />)
|
||||
case '빗물이용시설':
|
||||
return (<RainFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} rainDetailModal={rainDetailModal} />)
|
||||
case '중수도시설':
|
||||
return (<RswtFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} rswtrDetailModal={rswtrDetailModal} />)
|
||||
case '온배수시설':
|
||||
return (<HtdflFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} htdflDetailModal={htdflDetailModal} />)
|
||||
case '수요처':
|
||||
return(<ReuseMngFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} isDmdModal={isDmdModal} />)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null; // 조건에 맞는 콘텐츠가 없을 경우
|
||||
};
|
||||
return (
|
||||
viewMode == 'nothing' ? <></> :
|
||||
<NewWindowPortal
|
||||
onClose={memoizedOnClose}
|
||||
title="시설 상세 정보"
|
||||
scrollbars='yes'
|
||||
viewMode={viewMode}
|
||||
resizable='no'>
|
||||
{/* 체크된 레이어에 따른 조건부 렌더링 */}
|
||||
{renderConditionalContent()}
|
||||
</NewWindowPortal>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewWindowPopup;
|
||||
@@ -31,9 +31,10 @@ interface NewWindowPortalProps {
|
||||
height?: number;
|
||||
resizable?: string;
|
||||
scrollbars?: string;
|
||||
viewMode?: 'list' | 'detail' | 'nothing';
|
||||
}
|
||||
|
||||
const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "", width = 900, height = 800, resizable = "yes", scrollbars = "yes" }) => {
|
||||
const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "", resizable = "yes", scrollbars = "yes", viewMode }) => {
|
||||
const [container, setContainer] = useState<HTMLElement | null>(null);
|
||||
const newWindowRef = useRef<Window | null>(null);
|
||||
const onCloseRef = useRef(onClose);
|
||||
@@ -46,7 +47,15 @@ const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, ti
|
||||
const handleUnload = useCallback(() => {
|
||||
onCloseRef.current();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (newWindowRef.current) {
|
||||
if (viewMode == 'detail') {
|
||||
newWindowRef.current.resizeTo(1100, 700);
|
||||
} else {
|
||||
newWindowRef.current.resizeTo(screen.availWidth, screen.availHeight);
|
||||
}
|
||||
}
|
||||
}, [viewMode]);
|
||||
useEffect(() => {
|
||||
// 이미 창이 열려있으면 새로 열지 않음
|
||||
if (newWindowRef.current && !newWindowRef.current.closed) return;
|
||||
@@ -57,11 +66,11 @@ const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, ti
|
||||
const screenHeight = window.innerHeight ?? document.documentElement.clientHeight;
|
||||
|
||||
// 중앙 위치 계산
|
||||
const left = screenLeft + (screenWidth - width) / 2;
|
||||
const top = screenTop + (screenHeight - height) / 2;
|
||||
const left = screenLeft + (screenWidth - window.screen.width) / 2;
|
||||
const top = screenTop + (screenHeight - window.screen.height) / 2;
|
||||
|
||||
// 1. 새 창을 열고 참조를 저장합니다.
|
||||
const popupOptions = `width=${width},height=${height},left=${left}, top=${top}, resizable=${resizable},scrollbars=${scrollbars}`;
|
||||
const popupOptions = `width=${window.screen.width},height=${window.screen.height},left=${left}, top=${top}, resizable=${resizable},scrollbars=${scrollbars}`;
|
||||
newWindowRef.current = window.open('', title, popupOptions);
|
||||
|
||||
if (newWindowRef.current) {
|
||||
@@ -3,15 +3,16 @@
|
||||
import Gis from "@common/business_components/ui/map/Gis";
|
||||
import { useEffect, useRef, useState, useCallback } from "react";
|
||||
import { mapService } from "../service";
|
||||
import NewWindowPopup from "../newWindowPopup";
|
||||
import NewWindowPopup from "../components/newWindowPopup";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
import { SearchBar } from "../searchBar/searchBar";
|
||||
|
||||
const Map = () => {
|
||||
const [treeData, setTreeData] = 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 [featureData, setFeatureData] = useState<any>(null); // 선택된 피쳐 데이터
|
||||
const [checkedLayers, setCheckedLayers] = useState<string[]>([]);
|
||||
const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]);
|
||||
|
||||
@@ -63,25 +64,26 @@ const Map = () => {
|
||||
let foundFeature: any = null;
|
||||
let featureId = data.fid;
|
||||
const layerName = data.layerNm;
|
||||
if (layerName === "tb_wr_bdst_ruse_fclt") {
|
||||
console.log("선택된 레이어 이름:", data, layerName);
|
||||
if (layerName === "하수재이용시설") {
|
||||
// 재이용시설
|
||||
featureId = `${layerName}.${data.searchPk}`;
|
||||
} else if ( //빗물이용시설, 중수도시설
|
||||
layerName === "tb_wr_rnwtr_utztn_fclt" ||
|
||||
layerName == "tb_wr_rswtr_fclt"
|
||||
} else if (
|
||||
//빗물이용시설, 중수도시설
|
||||
layerName === "빗물이용시설" ||
|
||||
layerName == "중수도시설"
|
||||
) {
|
||||
featureId = `${layerName}.${data.bdstInfoSn}`;
|
||||
} else if (layerName === "tb_il_swage_prcs_fclt") { //하수처리시설
|
||||
} else if (layerName === "하수처리시설") { //하수처리시설
|
||||
featureId = `${layerName}.${data.bscFctCd}`;
|
||||
} else if (layerName === "tb_wr_htdfl_fclt") { //온배수시설
|
||||
} else if (layerName === "온배수시설") { //온배수시설
|
||||
featureId = `${layerName}.${data.bdstRuseFcltSn}`;
|
||||
} else if (layerName == "tb_wr_ruse_fclt_dmd_co") { //수요처
|
||||
} else if (layerName == "수요처") { //수요처
|
||||
featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`;
|
||||
} else {
|
||||
console.warn("해당 레이어에 대한 featureId 매핑 없음", layerName);
|
||||
featureId = `${layerName}.${data.fid || data.id || data.ftrIdn}`;
|
||||
}
|
||||
|
||||
map
|
||||
.getLayers()
|
||||
.getArray()
|
||||
@@ -111,7 +113,9 @@ const Map = () => {
|
||||
} else {
|
||||
console.warn("OpenLayers Feature를 찾을 수 없습니다:", data);
|
||||
}
|
||||
|
||||
const currentId = data?.fid || data?.id || data?.ftrIdn;
|
||||
const lastId = featureData?.fid || featureData?.id || featureData?.ftrIdn;
|
||||
if (featureData && currentId === lastId) return; // 이전과 같으면 무시
|
||||
setFeatureData(data);
|
||||
setIsOpen(true);
|
||||
setIsSearchModalOpen(false);
|
||||
@@ -127,7 +131,18 @@ const Map = () => {
|
||||
// />
|
||||
// );
|
||||
// };
|
||||
|
||||
const renderDetailView = useCallback(
|
||||
(props: any) => (
|
||||
<NewWindowPopup
|
||||
key={featureData?.fid || featureData?.id || featureData?.ftrIdn}
|
||||
onClose={handlePopupClose}
|
||||
checkedLayers={checkedLayers}
|
||||
checkedLayersData={checkedLayersData}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
[checkedLayers, checkedLayersData] // 필요 시 의존성 넣기
|
||||
);
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
@@ -136,24 +151,7 @@ const Map = () => {
|
||||
gisMaps={gisMaps}
|
||||
isMiniMap={false}
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
detailView={renderDetailView}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
setIsOpen={setIsOpen}
|
||||
setFeatureData={setFeatureData}
|
||||
@@ -163,6 +161,24 @@ const Map = () => {
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={handleCheckedLayersChange}
|
||||
imgVisble={true}
|
||||
searchBar={() => {
|
||||
const facilityTypes = [
|
||||
{ text: "하수처리시설", val: "tb_il_swage_prcs_fclt" },
|
||||
{ text: "하수재이용시설", val: "tb_wr_bdst_ruse_fclt" },
|
||||
{ text: "빗물이용시설", val: "tb_wr_rnwtr_utztn_fclt" },
|
||||
{ text: "중수도시설", val: "tb_wr_rswtr_fclt" },
|
||||
{ text: "온배수시설", val: "tb_wr_htdfl_fclt" },
|
||||
{ text: "수요처", val: "tb_wr_ruse_fclt_dmd_co" },
|
||||
{ text: "잠재수요처", val: "tb_wr_tmpr_dmd_co" },
|
||||
];
|
||||
return (
|
||||
<SearchBar
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
facilityTypes={facilityTypes}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
waterDepth={undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,795 +0,0 @@
|
||||
"use client"
|
||||
import NewWindowPortal from '@/app/map/newWindowPortal';
|
||||
import { useWrStore } from "@/app/store";
|
||||
import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
|
||||
import type { customPbnst, pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
|
||||
import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
|
||||
import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
|
||||
import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type";
|
||||
import { fetchType, getFetch, postFectch } from "@/components/lib/api";
|
||||
import { formatPhoneNumber, tableData } from "@/components/lib/func/commom";
|
||||
import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal";
|
||||
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
|
||||
import { Title, UITab, UiTable } from '@common/business_components';
|
||||
import { useGisContext } from "@common/business_components/ui/map/Gis";
|
||||
import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form';
|
||||
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
|
||||
import moment from 'moment';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
interface NewWindowPopupProps {
|
||||
featureData: any;
|
||||
seletectedLayerNm?: string
|
||||
viewMode?: 'list' | 'detail' | 'nothing'
|
||||
onFeatureSelect: (data: any) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
featureData,
|
||||
seletectedLayerNm,
|
||||
onFeatureSelect,
|
||||
onClose
|
||||
}) => {
|
||||
const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; };
|
||||
const [tabData, setTabData] = useState<{ id: string; name: string; data: any }[]>([]);
|
||||
const [activeTab, setActiveTab] = useState<string>('0');
|
||||
const [viewMode, setViewMode] = useState<'list' | 'detail' | 'nothing'>('list');
|
||||
const [fcltPk, setFcltPk] = useState<string>('');
|
||||
|
||||
|
||||
// onClose 함수를 useCallback으로 메모이제이션
|
||||
const memoizedOnClose = useCallback(() => {
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
// 하수재이용시설 변수
|
||||
const {
|
||||
code,
|
||||
pbnstSttn,
|
||||
pbnstSttnSearch,
|
||||
pbnSttnInit,
|
||||
getFlcts
|
||||
} = useWrStore((state) => state);
|
||||
const { fclts, pbnstSttnResCount } = pbnstSttn
|
||||
const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>()
|
||||
|
||||
// 빗물재이용시설 변수
|
||||
const { rainSttn, rainSttnSearch, rainSttnInit } = useWrStore(
|
||||
(state) => state
|
||||
);
|
||||
const { rainSttnRes, rainSttnResCount } = rainSttn;
|
||||
const [rainTable, setRainTable] = useState<customRainBdst[]>();
|
||||
const [rainPage, setRainPage] = useState(1)
|
||||
const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false });
|
||||
const [rainList, setRainList] = useState<customRainBdst[]>(rainSttnRes || []);
|
||||
|
||||
// 중수도시설 변수
|
||||
const { rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore(
|
||||
(state) => state
|
||||
);
|
||||
const { rswtSttnRes, rswtSttnResCount } = rswtSttn;
|
||||
const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>();
|
||||
const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false });
|
||||
const [rswtrPage, setRswtrPage] = useState(1);
|
||||
const [rswtrList, setRswtrList] = useState<customRswtBdst[]>(rswtSttnRes || []);
|
||||
|
||||
// 온배수시설 변수
|
||||
const { htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state);
|
||||
const { htdflSttnRes, htdflSttnResCount } = htdflSttn;
|
||||
const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>();
|
||||
const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false });
|
||||
const [htdflPage, setHtdflPage] = useState(1)
|
||||
const [htdflList, setHtdflList] = useState<customHtdflBdst[]>(htdflSttnRes || []);
|
||||
|
||||
//하수처리시설 변수
|
||||
const {
|
||||
pbnst,
|
||||
pubHasuInit,
|
||||
} = useWrStore((state) => state);
|
||||
const { pubFacHeadList, pbnstHasuRes } = pbnst;
|
||||
const [hasuList, setHasuList] = useState<pubHasuType[]>(pbnstHasuRes || []);
|
||||
const [isRuseReg, setIsRuseReg] = useState<{
|
||||
data: any | undefined;
|
||||
}>({ data: undefined });
|
||||
const [hasuTable, setHasuTable] = useState<pubHasuType[]>();
|
||||
const [hasuPage, setHasuPage] = useState<number>(1);
|
||||
|
||||
// 수요처관리 변수
|
||||
const {
|
||||
coSttn,
|
||||
coSttnInit,
|
||||
coSttnSearch,
|
||||
// getDemandCompList,
|
||||
// regDemandCompany,
|
||||
// updateDemandCompany,
|
||||
// coSttnTxtSearch,
|
||||
// demandCompList,
|
||||
} = useWrStore((state) => state);
|
||||
const [mngTable, setMngTable] = useState<DemandCompType[]>();
|
||||
const [mngPage, setMngPage] = useState(1);
|
||||
const { coSttnRes, coSttnResCount, coSttHeadList } = coSttn;
|
||||
const [isDmdModal, setIsDmdModal] = useState<{
|
||||
isMoal: boolean;
|
||||
data: any | undefined;
|
||||
}>({ isMoal: false, data: undefined});
|
||||
const [mngList, setMngList] = useState<DemandCompType[]>(coSttnRes || []);
|
||||
///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 ///////////////////////////////////////////////
|
||||
const getSelectedLayers = () => {
|
||||
let result = null;
|
||||
const layers = gisMap ? gisMap.getLayers().getArray().filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기
|
||||
let featureLayer: { get: (key: string) => any } = { get: () => null };
|
||||
layers.forEach((layer: any) => {
|
||||
const source = layer.getSource();
|
||||
if (source && source.hasFeature && featureData
|
||||
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
|
||||
featureLayer = layer; // 피처가 속한 레이어 찾기)
|
||||
}
|
||||
});
|
||||
result = featureLayer.get('lyrNm');
|
||||
|
||||
return result;
|
||||
};
|
||||
const fetchpbnstDetail = async (featureCd: string) => {
|
||||
try {
|
||||
const response = await getFetch("wr", `/reuseFc/pubFc/${featureCd}`);
|
||||
// 중복 데이터 추가 방지
|
||||
setTabDataFunc(response, featureCd);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
}
|
||||
};
|
||||
const fetchHasuDetail = async (featureCd: string, data: string) => {
|
||||
try {
|
||||
const rainRes = await postFectch(`/prcsFclt/ruseInfo/1/10/A/0/0/1/SW/${featureCd}`, [data], fetchType.COMMON)
|
||||
setHasuList(
|
||||
rainRes.data.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
fctCpc: Number(item.fctCpc) || 0,
|
||||
ruse: item.ruse === false ? "N" : "Y",
|
||||
};
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
}
|
||||
};
|
||||
const detainSttn = async (featureCd: string, layerNm: string) => {
|
||||
if (layerNm == 'tb_wr_rnwtr_utztn_fclt') { //빗물이용시설
|
||||
const res = await getFetch(fetchType.WR, `/reuseFc/rainyInfoList/1/20/0/${featureCd}`)
|
||||
setRainList(res)
|
||||
}
|
||||
else if (layerNm == 'tb_wr_rswtr_fclt') { //중수도 시설
|
||||
const res = await getFetch(fetchType.WR, `/reuseFc/waterreuseFc/1/20/0/${featureCd}`)
|
||||
setRswtrList(res)
|
||||
}
|
||||
else if(layerNm =='tb_wr_ruse_fclt_dmd_co'){ //수요처
|
||||
const res = await getFetch(fetchType.WR, `/reuseFc/request/1/20/0/${featureCd}`)
|
||||
setMngList(res)
|
||||
}
|
||||
else {
|
||||
const res = await getFetch(fetchType.WR, `/drain/facilityList/1/20/0/${featureCd}`)
|
||||
setHtdflList(res)
|
||||
setHtdflDetailModal({ isOpen: true, data: htdflSttnRes[0] });
|
||||
}
|
||||
}
|
||||
// 상세 목록 데이터 상태 처리
|
||||
useEffect(() => {
|
||||
if (hasuList.length > 0) setIsRuseReg({ data: [hasuList[0]] });
|
||||
}, [hasuList])
|
||||
useEffect(() => {
|
||||
if (rainList.length > 0) setRainDetailModal({ isOpen: true, data: rainList[0] });
|
||||
}, [rainList])
|
||||
useEffect(() => {
|
||||
if (rswtrList.length > 0) setRswtrDetailModal({ isOpen: true, data: rswtrList[0] });
|
||||
}, [rswtrList])
|
||||
useEffect(() => {
|
||||
if (htdflList.length > 0) setHtdflDetailModal({ isOpen: true, data: htdflList[0] });
|
||||
}, [htdflList])
|
||||
useEffect(() => {
|
||||
if(mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] });
|
||||
},[mngList])
|
||||
|
||||
const layerId = getSelectedLayers();
|
||||
if (featureData && typeof featureData.getProperties === "function") {
|
||||
const selectedVal = featureData.getProperties();
|
||||
let featureCd = layerId == '하수재이용시설' ? selectedVal?.swtrmt_fclt_cd : layerId == '빗물이용시설' || layerId == '중수도시설' || layerId == '온배수시설' || layerId == '수요처' ? selectedVal?.bdst_nm : layerId == '하수처리시설' ? selectedVal?.swtrmt_fclt_nm : null;
|
||||
if (featureCd && featureCd !== fcltPk) {
|
||||
if (layerId == '하수재이용시설') {
|
||||
featureCd = selectedVal?.swtrmt_fclt_cd;
|
||||
setFcltPk(featureCd); // 현재 호출된 featureCd 저장
|
||||
setActiveTab(featureCd)
|
||||
fetchpbnstDetail(featureCd); // 상세정보
|
||||
}
|
||||
else if (layerId == '하수처리시설') {
|
||||
featureCd = selectedVal?.swtrmt_fclt_nm;
|
||||
setFcltPk(featureCd); // 현재 호출된 featureCd 저장
|
||||
fetchHasuDetail(featureCd, selectedVal?.swtrmt_fclt_cd);
|
||||
}
|
||||
else {
|
||||
featureCd = selectedVal?.bdst_nm;
|
||||
setFcltPk(featureCd)
|
||||
if (layerId) {
|
||||
detainSttn(featureCd, layerId);
|
||||
}
|
||||
}
|
||||
setViewMode("detail")
|
||||
}
|
||||
} else if (layerId === null && seletectedLayerNm) { setViewMode("list") }
|
||||
///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 ///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////// 하수재이용시설 //////////////////////////////////////////////////
|
||||
// 재이용시설 cellClick 이벤트 (상세정보)
|
||||
const pbnstCellClick = (index: number | undefined) => {
|
||||
if (
|
||||
index !== undefined &&
|
||||
pbnstSttn.pbnstSttnRes &&
|
||||
pbnstSttn.pbnstSttnRes[index]
|
||||
) {
|
||||
setFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltId);
|
||||
onFeatureSelect(pbnstSttn.pbnstSttnRes[index]);
|
||||
onClose()
|
||||
}
|
||||
};
|
||||
|
||||
// 재이용 현황 초기값
|
||||
useEffect(() => {
|
||||
if (!pbnstSttn.isInit) {
|
||||
getFlcts()
|
||||
}
|
||||
}, [seletectedLayerNm, pbnstSttn.isInit, getFlcts]);
|
||||
useEffect(() => {
|
||||
pbnSttnInit("1", "15", {
|
||||
partValue: "A",
|
||||
sidoValue: '0',
|
||||
sigunValue: "0",
|
||||
regionValue: "",
|
||||
unitWaterShedValue: "",
|
||||
middleRegionValue: "",
|
||||
envPartValue: "",
|
||||
sidoText: "",
|
||||
sigunText: ""
|
||||
}, String(new Date().getFullYear()), pbnstSttn.fclts);
|
||||
}, [fclts, pbnSttnInit, pbnstSttn.fclts])
|
||||
useEffect(() => {
|
||||
setBdstTable(pbnstSttn.pbnstSttnRes);
|
||||
}, [seletectedLayerNm, pbnstSttn.pbnstSttnRes]);
|
||||
|
||||
// 재이용시설 상세정보
|
||||
const setTabDataFunc = (res: any, pk: string) => {
|
||||
setTabData((prev) => {
|
||||
if (prev.some((tab) => tab.id === pk)) {
|
||||
return prev; // 이미 존재하는 탭이면 추가하지 않음
|
||||
}
|
||||
return [...prev, { id: pk, name: `재이용시설 ${prev.length + 1}`, data: res }];
|
||||
});
|
||||
}
|
||||
// 재이용시설 조회
|
||||
// 재이용시설 페이징처리 관련 함수
|
||||
const onPageChangeHandler = useCallback((page: number) => {
|
||||
pbnstSttnSearch(
|
||||
false,
|
||||
String(page),
|
||||
String(13),
|
||||
{
|
||||
partValue: "A",
|
||||
sidoValue: "0",
|
||||
sigunValue: "0",
|
||||
regionValue: "",
|
||||
unitWaterShedValue: "",
|
||||
middleRegionValue: "",
|
||||
envPartValue: "",
|
||||
sidoText: "",
|
||||
sigunText: "",
|
||||
},
|
||||
"",
|
||||
String(new Date().getFullYear()), fclts
|
||||
);
|
||||
}, [fclts, pbnstSttnSearch]);
|
||||
// FIXME: 윤선화차장님 요구사항으로 엑셀 다운로드 삭제.
|
||||
// const excelClick = useCallback(() => {
|
||||
// // if (codeData) pbnstSttnSearch(1, "10000", codeData);
|
||||
// excelDownload(
|
||||
// [[]],
|
||||
// "하수재이용시설 현황",
|
||||
// "sample_pbnstSttn.xlsx",
|
||||
// "A1:U2"
|
||||
// );
|
||||
// }, [pbnstSttnSearch]);
|
||||
///////////////////////////////////////////// 하수재이용시설 //////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////// 빗물 재이용 //////////////////////////////////////////////////
|
||||
// 빗물재이용시설 cellClick 이벤트 (상세정보)
|
||||
const rainCellClick = (index: number | undefined, headId: string) => {
|
||||
if (
|
||||
index !== undefined &&
|
||||
rainTable !== undefined &&
|
||||
headId !== 'chBuildingYn') {
|
||||
// setRainDetailModal({ isOpen: true, data: rainTable[index] })
|
||||
// setViewMode('detail')
|
||||
onFeatureSelect(rainTable[index]);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!rainSttn.isInit) {
|
||||
rainSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd);
|
||||
}
|
||||
}, [code.cityCd, code.sggCd, rainSttn.isInit, rainSttnInit]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = tableData(rainSttnRes, { code });
|
||||
const chData = data
|
||||
.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
...item.reuseBuilding[0],
|
||||
...item.reuseBuilding[0].rainyObj,
|
||||
dtfcltCpct: item.reuseBuilding[0].rainyObj.dtfcltCpct === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].rainyObj.dtfcltCpct,
|
||||
instlCst: item.reuseBuilding[0].instlCst === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].instlCst,
|
||||
instlCstSprtAmt: item.reuseBuilding[0].instlCstSprtAmt === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].instlCstSprtAmt,
|
||||
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
|
||||
pntfColArea: item.reuseBuilding[0].rainyObj.pntfColArea === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].rainyObj.pntfColArea,
|
||||
filtrTrtmfctCpct: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1
|
||||
? "확인불가"
|
||||
: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct,
|
||||
archArea: Math.floor(item.archArea) === -1 ? "확인불가" : item.archArea,
|
||||
};
|
||||
})
|
||||
.filter((item: any) => item.rainyObj !== undefined);
|
||||
setRainTable(chData);
|
||||
}, [code, rainSttnRes, rainSttn.rainSttHeadList]);
|
||||
|
||||
useEffect(() => {
|
||||
rainSttnSearch(false, rainPage, "20", '', '', code.cityCd, code.sggCd);
|
||||
}, [code.cityCd, code.sggCd, rainPage, rainSttnSearch]);
|
||||
///////////////////////////////////////////// 빗물 재이용 //////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////// 중수도 시설 //////////////////////////////////////////////////
|
||||
// 중수도시설 cellClick 이벤트 (상세정보)
|
||||
const rswtCellClick = (index: number | undefined, headId: string) => {
|
||||
if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) {
|
||||
// setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] });
|
||||
// setViewMode('detail')
|
||||
onFeatureSelect(rswtrTable[index]);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!rswtSttn.isInit) {
|
||||
rswtSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd);
|
||||
}
|
||||
}, [code.cityCd, code.sggCd, rswtSttn.isInit, rswtSttnInit]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = tableData(rswtSttnRes, { code });
|
||||
const chData = data
|
||||
.map((item: customRswtBdst) => {
|
||||
return {
|
||||
...item,
|
||||
...item.reuseBuilding[0],
|
||||
...item.reuseBuilding[0]?.rswtrObj,
|
||||
chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N',
|
||||
instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst,
|
||||
instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt,
|
||||
archArea: item?.archArea.split('.')[0] !== '-1' ? item?.archArea.split('.')[0] : '확인불가'
|
||||
}
|
||||
})
|
||||
.filter(
|
||||
(item: customRswtBdst) =>
|
||||
item.reuseBuilding[0]?.rswtrObj !== undefined
|
||||
);
|
||||
setRswtrTable(chData);
|
||||
}, [code, rswtSttnRes]);
|
||||
// FIXME: 윤선화 차장님 요구사항으로 엑셀 다운로드 삭제.
|
||||
// const rswtSttnExcelDownload = useCallback(() => {
|
||||
// if (rswtSttn.excelList.length > 0) {
|
||||
// if (rswtSttn.excelList.length === 0) return;
|
||||
// excelDownload(
|
||||
// rswtSttn.excelList,
|
||||
// "중수도시설 현황",
|
||||
// "sample_rswtSttn.xlsx",
|
||||
// "A1:W3",
|
||||
// );
|
||||
// }
|
||||
// }, [rswtSttn.excelList])
|
||||
|
||||
useEffect(() => {
|
||||
rswtSttnSearch(false, rswtrPage, "20", '', '', code.cityCd, code.sggCd);
|
||||
}, [code.cityCd, code.sggCd, rswtSttnSearch, rswtrPage]);
|
||||
///////////////////////////////////////////// 중수도 시설 //////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////// 온배수 시설 //////////////////////////////////////////////////
|
||||
// 온배수시설 cellClick 이벤트 (상세정보)
|
||||
const htdflCellClick = (index: number | undefined) => {
|
||||
if (index !== undefined && htdflTable !== undefined) {
|
||||
onFeatureSelect(htdflTable[index]);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!htdflSttn.isInit) {
|
||||
initHtdfl(false, 1, "20", "0", '', code.cityCd, code.sggCd);
|
||||
}
|
||||
}, [code.cityCd, code.sggCd, htdflSttn.isInit, initHtdfl]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = tableData(htdflSttnRes, { code });
|
||||
const chData = data
|
||||
.map((item: any) => ({
|
||||
...item,
|
||||
...item.reuseBuilding[0],
|
||||
...item.reuseBuilding[0].htdflObj,
|
||||
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
|
||||
}))
|
||||
.filter((item: any) => item.htdflObj !== undefined);
|
||||
setHtdflTable(chData);
|
||||
}, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]);
|
||||
|
||||
useEffect(() => {
|
||||
htdflSttnSearch(true, htdflPage, "20", '', '', code.cityCd, code.sggCd);
|
||||
}, [code.cityCd, code.sggCd, htdflPage, htdflSttnSearch]);
|
||||
///////////////////////////////////////////// 온배수 시설 //////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////// 하수처리 시설 //////////////////////////////////////////////////
|
||||
useEffect(() => {
|
||||
if (code.sggCd.length && pbnstHasuRes.length > 0) {
|
||||
setHasuTable(tableData(pbnstHasuRes, { code }));
|
||||
}
|
||||
}, [code, pbnstHasuRes]);
|
||||
useEffect(() => {
|
||||
if (!pbnst.isInit) {
|
||||
pubHasuInit("1", "0");
|
||||
}
|
||||
}, [pbnst.isInit, pubHasuInit]);
|
||||
useEffect(() => {
|
||||
pubHasuInit(String(hasuPage), "0");
|
||||
}, [hasuPage, pubHasuInit]);
|
||||
|
||||
const hasuCellClick = (index: number | undefined) => {
|
||||
if (index !== undefined && pbnstHasuRes !== undefined) {
|
||||
onFeatureSelect(pbnstHasuRes[index]);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
///////////////////////////////////////////// 하수처리 시설 //////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////// 수요처 관리 //////////////////////////////////////////////////
|
||||
useEffect(() => {
|
||||
if (!coSttn.isInit) {
|
||||
coSttnInit("1", "13", "0");
|
||||
}
|
||||
}, [coSttn.isInit, coSttnInit]);
|
||||
const coMngCellClick = (index: number | undefined) => {
|
||||
if (index !== undefined && mngTable !== undefined) {
|
||||
onFeatureSelect(mngTable[index]);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (coSttnRes) {
|
||||
if (coSttnRes?.length > 0 && code.cityCd.length > 0) {
|
||||
const data = tableData(coSttnRes, { code });
|
||||
const chData = data.map((item: any) => ({
|
||||
...item,
|
||||
telno: formatPhoneNumber(item.telno || undefined),
|
||||
regDt: moment(item.regDt).format("YYYY-MM-DD"),
|
||||
dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5, 10)}-${item.dmdInfoId.slice(10)}`
|
||||
}));
|
||||
// .filter((item: any) => item !== undefined);
|
||||
setMngTable(chData);
|
||||
}
|
||||
}
|
||||
}, [coSttnRes, code]);
|
||||
useEffect(() => {
|
||||
coSttnSearch(String(mngPage), "13", String(0));
|
||||
}, [coSttnSearch, mngPage]);
|
||||
///////////////////////////////////////////// 수요처 관리 //////////////////////////////////////////////////
|
||||
|
||||
// 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수
|
||||
const renderConditionalContent = () => {
|
||||
const actTabData = tabData.find((tab) => tab.id === activeTab);
|
||||
let layerId: string | undefined | null = getSelectedLayers();
|
||||
if (layerId === null || layerId === undefined) {
|
||||
layerId = seletectedLayerNm || '';
|
||||
}
|
||||
//하수처리시설
|
||||
const isPrcsFclt = layerId == 'tb_il_swage_prcs_fclt'
|
||||
// 재이용시설
|
||||
const isPbnst = layerId == 'tb_wr_bdst_ruse_fclt';
|
||||
// 빗물이용시설
|
||||
const isRain = layerId == 'tb_wr_rnwtr_utztn_fclt';
|
||||
// 중수도시설
|
||||
const isRswt = layerId == 'tb_wr_rswtr_fclt';
|
||||
// 온배수시설
|
||||
const isHtdfl = layerId == 'tb_wr_htdfl_fclt';
|
||||
// 수요처관리
|
||||
const isReuseMng = layerId == 'tb_wr_ruse_fclt_dmd_co';
|
||||
if (isPrcsFclt) {
|
||||
if (viewMode == 'list') {
|
||||
return (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수처리시설 현황" />
|
||||
</div>
|
||||
{/* 높이 조정 */}
|
||||
<UiTable
|
||||
tableData={hasuTable || []}
|
||||
headlist={pubFacHeadList}
|
||||
pageSize={10}
|
||||
total={5000}
|
||||
headName={""}
|
||||
cellClick={hasuCellClick}
|
||||
onPageChange={setHasuPage}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div style={{ overflowX: 'hidden' }}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수처리시설 상세정보" />
|
||||
</div>
|
||||
{/* 높이 조정 */}
|
||||
<UiTable
|
||||
tableData={isRuseReg?.data || []}
|
||||
headlist={pubFacHeadList.slice(0, 4)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"위치정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
<UiTable
|
||||
tableData={isRuseReg?.data || []}
|
||||
headlist={pubFacHeadList.slice(4)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"기본정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isPbnst) {
|
||||
if (viewMode == 'detail') {
|
||||
// 재이용시설 상세
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수재이용시설 상세정보" />
|
||||
</div>
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<UITab
|
||||
defaultValue={activeTab}
|
||||
tabList={tabData.map((tab) => ({
|
||||
value: tab.id,
|
||||
label: tab.name,
|
||||
onClick: () => setActiveTab(tab.id),
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
<PbnstSttnModal
|
||||
data={actTabData?.data[0] || ({} as customPbnst)}
|
||||
titleVisible={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
// 재이용시설 목록
|
||||
return (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<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 || []}
|
||||
headlist={pbnstSttn.pbnstSttHeadList}
|
||||
pageSize={20}
|
||||
total={pbnstSttnResCount || 0}
|
||||
cellClick={(index: number | undefined) => {
|
||||
pbnstCellClick(index);
|
||||
}}
|
||||
onPageChange={onPageChangeHandler}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />
|
||||
</UiTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isRain) {
|
||||
if (viewMode == 'detail') {
|
||||
// 빗물이용시설 상세
|
||||
return (
|
||||
<SttnDetailModal type={"RNB"} formObj={rainDetailModal.data as customRainBdst} />
|
||||
);
|
||||
} else {
|
||||
// 빗물이용시설 목록
|
||||
return (
|
||||
<>
|
||||
<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 || []}
|
||||
headlist={rainSttn.rainSttHeadList}
|
||||
pageSize={20}
|
||||
total={rainSttnResCount}
|
||||
cellClick={(index: number | undefined, headId: string) => {
|
||||
rainCellClick(index ?? undefined, headId);
|
||||
}}
|
||||
onPageChange={setRainPage}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />
|
||||
</UiTable>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isRswt) {
|
||||
if (viewMode == 'detail') {
|
||||
// 중수도시설 상세
|
||||
return (
|
||||
<SttnDetailModal type={"RST"} formObj={rswtrDetailModal.data as customRswtBdst} />
|
||||
);
|
||||
} else {
|
||||
// 중수도시설 목록
|
||||
return (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="중수도시설 현황" />
|
||||
{/* <div className="flex gap-2 mt-8">
|
||||
<Button className={btnClass} color="primary" onClick={rswtSttnExcelDownload}>
|
||||
엑셀다운로드
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
<UiTable
|
||||
headName={[]}
|
||||
tableData={rswtrTable || []}
|
||||
headlist={rswtSttn.rswtSttHeadList}
|
||||
pageSize={20}
|
||||
total={rswtSttnResCount}
|
||||
cellClick={(index: number | undefined, headId: string) => {
|
||||
rswtCellClick(index, headId);
|
||||
}}
|
||||
onPageChange={setRswtrPage}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />
|
||||
</UiTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isHtdfl) {
|
||||
if (viewMode == 'detail') {
|
||||
// 온배수시설 상세
|
||||
return (
|
||||
<SttnDetailModal type={"HTB"} formObj={htdflDetailModal.data as customHtdflBdst} />
|
||||
);
|
||||
} else {
|
||||
// 온배수시설 목록
|
||||
return (
|
||||
<div style={{ overflowY: 'auto' }}>
|
||||
<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={htdflTable || []}
|
||||
headlist={htdflSttn.htdflSttHeadList}
|
||||
pageSize={20}
|
||||
total={htdflSttnResCount || 0}
|
||||
cellClick={(index: number | undefined) => {
|
||||
htdflCellClick(index ?? undefined);
|
||||
}}
|
||||
onPageChange={setHtdflPage}
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />
|
||||
</UiTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
if (isReuseMng) {
|
||||
if (viewMode == 'detail') {
|
||||
return (
|
||||
<div style={{ overflowX: 'hidden' }}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="수요처 상세정보" />
|
||||
</div>
|
||||
{/* 높이 조정 */}
|
||||
<UiTable
|
||||
tableData={isDmdModal?.data || []}
|
||||
headlist={coSttHeadList.slice(0, 3)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"위치정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
<UiTable
|
||||
tableData={isDmdModal?.data || []}
|
||||
headlist={coSttHeadList.slice(3)}
|
||||
pageSize={1}
|
||||
total={0}
|
||||
headName={"기본정보"}
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
} else if(viewMode == 'list') {
|
||||
return ( <div>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="수요처 현황" />
|
||||
</div>
|
||||
<UiTable
|
||||
headName={[]}
|
||||
tableData={mngTable || []}
|
||||
headlist={coSttHeadList}
|
||||
pageSize={20}
|
||||
total={coSttnResCount + 10 || 0}
|
||||
cellClick={(index: number | undefined) => {
|
||||
coMngCellClick(index);
|
||||
// setUpdateData(coSttnRes[index as number]);
|
||||
}}
|
||||
onPageChange={(page) => {
|
||||
setMngPage(page);
|
||||
}}
|
||||
></UiTable></div>)
|
||||
}
|
||||
}
|
||||
return null; // 조건에 맞는 콘텐츠가 없을 경우
|
||||
};
|
||||
|
||||
return (
|
||||
viewMode == 'nothing' ? <></> :
|
||||
<NewWindowPortal
|
||||
onClose={memoizedOnClose}
|
||||
title="시설 상세 정보"
|
||||
width={viewMode == 'list' ? window.screen.availWidth : 1100}
|
||||
height={viewMode == 'list' ? window.screen.availHeight : layerId == '하수처리시설' && viewMode == 'detail' ? 450 : 900}
|
||||
scrollbars='yes'
|
||||
resizable='no'>
|
||||
{/* 체크된 레이어에 따른 조건부 렌더링 */}
|
||||
{renderConditionalContent()}
|
||||
</NewWindowPortal>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewWindowPopup;
|
||||