Compare commits

..
Author SHA1 Message Date
leedj312 0c16b9d14d 중수도 지도점검 엑셀등록 오류 메시지 추가 2025-07-31 13:28:21 +09:00
leedj312 3ba11b5508 빗물 엑셀 등록 항목 오류 점검 2025-07-29 20:11:51 +09:00
leedj312 65f74987f8 Merge commit 'aaed8fe971d0ccf31cb0b07f96bb51af4c125a1f' into iipbs/wr/dev 2025-07-29 15:52:08 +09:00
leedj312 aaed8fe971 재이용 시설 실적 등록 목록 테이블 수정 2025-07-29 15:51:18 +09:00
yschoi cba305ab9b Merge branch 'iipbs/wr/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/wr/dev 2025-07-29 15:17:51 +09:00
yschoi 913256cac4 원복 2025-07-29 15:16:45 +09:00
leedj312 75d14782f4 Merge commit 'c30de7f40d6b810cc19d5cd23a581d012116899f' into iipbs/wr/dev 2025-07-29 11:33:05 +09:00
jyha00 c30de7f40d 재이용실적 현황 엑셀 다운로드 수정, 초기화 버튼 수정 2025-07-29 11:10:22 +09:00
leedj312 ca96e55c07 Merge commit '64698fb97e39f5b35b0fd05012f94833dead1522' into iipbs/wr/dev 2025-07-29 10:47:41 +09:00
jyha00 64698fb97e 재이용실적 현황 페이지네이션 오류 수정 2025-07-29 10:33:34 +09:00
leedj312 662a461cfa 하수재이용 현황 페이지 네이션 오류 수정 2025-07-28 18:26:43 +09:00
yschoi 2a15138014 Merge remote-tracking branch 'origin/main' into iipbs/wr/dev 2025-07-28 12:41:08 +09:00
leedj312 97727908fa 수요처 nullish 처리 2025-07-28 10:32:58 +09:00
mind-one005 16ebb8eb3a Merge branch 'iipbs/sn/dev' 2025-07-27 21:59:04 +09:00
mind-one005 f8c902a138 기본계획 component 분리 2025-07-27 21:55:24 +09:00
mind-one005 01c01dedbb Merge branch 'iipbs/common/main' 2025-07-27 17:16:48 +09:00
mind-one005 007a39ebfa Merge branch 'iipbs/common/main' into iipbs/sn/dev 2025-07-27 17:14:21 +09:00
mind-one005 8c2bdc65eb 침입수,RDII 그래프 수정 2025-07-27 17:09:25 +09:00
47 changed files with 1175 additions and 1705 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
NEXT_PUBLIC_SITE_ID = DM
NEXT_PUBLIC_PORT = 11035
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
+2 -2
View File
@@ -3,9 +3,9 @@ NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_PORT = 3005
NEXT_PUBLIC_PORT = 11035
#NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035
NEXT_PUBLIC_PROXY_HOST = http://localhost:3005/
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000/
NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
@@ -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 { DemandCompBizType } from '@/app/store/sdmMng';
import type { DemandCompType } 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<DemandCompBizType>({} as DemandCompBizType);
const [transformedList, setTransformedList] = useState<DemandCompBizType[]>([]);
const [sdmApplyDetail, setSdmApplyDetail] = useState<DemandCompType>({} as DemandCompType);
const [transformedList, setTransformedList] = useState<DemandCompType[]>([]);
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: DemandCompBizType) => ({
const newTransformedList = sdmInfoStatusList.map((item: DemandCompType) => ({
...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,8 +73,6 @@ export default function SdmApplyList({}: SdmApplyProps) {
if (index !== undefined) {
const selectedStatus = transformedList[index];
if (selectedStatus) {
setSdmApplyDetail(selectedStatus);
}
}
@@ -123,33 +121,33 @@ export default function SdmApplyList({}: SdmApplyProps) {
</TableRow>
<TableRow>
<TableCell colSpan={1} className={TABLE_STYLES.header}>총대장균군수(개/100ml)</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.cell}>{<TransValueFormat value={sdmApplyDetail?.grssClfg} format="number"/>}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>결합잔류염소(mg/L)​</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.combResdch ?? ''} format="number"/>} {sdmApplyDetail?.combResdch?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.combResdch} format="number"/>}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>탁도(NTU)</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.trbt ?? ''} format="number"/>} {sdmApplyDetail?.trbt?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.trbt} format="number"/>}</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"/>} {sdmApplyDetail?.blgcOxygDmndQnt?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.blgcOxygDmndQnt} format="number"/>}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>냄새</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.smll} format="number"/>} {sdmApplyDetail?.smll?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.smll} format="number"/>}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>색도(도)</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.crmty ?? ''} format="number"/>} {sdmApplyDetail?.crmty?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.crmty} format="number"/>}</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"/>} {sdmApplyDetail?.tn?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tn} format="number"/>}</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"/>} {sdmApplyDetail?.tph?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tph} format="number"/>}</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"/>} {sdmApplyDetail?.clIonQnt?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.clIonQnt} format="number"/>}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>전기전도도(㎲/cm)​</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.cdtnv ?? ''} format="number"/>} {sdmApplyDetail?.cdtnv?'이하':'-'}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.cdtnv} format="number"/>}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>비고</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.rmrk}</TableCell>
</TableRow>
@@ -158,11 +156,11 @@ export default function SdmApplyList({}: SdmApplyProps) {
</TableRow>
<TableRow>
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지1</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{ sdmApplyDetail?.splyCndtList?.[0]?.ruseUtztnFcltNm ?? ""}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}></TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지2</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.splyCndtList?.[1]?.ruseUtztnFcltNm ?? ""}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}></TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지3</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.splyCndtList?.[2]?.ruseUtztnFcltNm ?? ""}</TableCell>
<TableCell colSpan={1} className={TABLE_STYLES.cell}></TableCell>
</TableRow>
</TableBody>
</Table>
@@ -179,7 +177,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}>{sdmApplyDetail?.ruseUtztnFcltNm??''}</TableCell>
<TableCell colSpan={2} className={TABLE_STYLES.cell}></TableCell>
</TableRow>
</TableBody>
</Table>
@@ -1,7 +1,7 @@
"use client";
import { useDmStore } from "@/app/store";
import { sdmInfoFormSchema, type DemandCompBizType, type SdmInfoFormData } from "@/app/store/sdmMng";
import type { DemandCompType } 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";
@@ -10,206 +10,227 @@ 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 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<SdmInfoFormData>({
sdmNm: "",
const [facilityName, setFacilityName] = useState<string>('');
const [defaultValues, setDefaultValues] = useState<FormData>({
sdmNm: '',
demandYmd: new Date(),
demandQnty: "",
usePurpose: "",
money: "",
demandQnty: '',
usePurpose: '',
money: '',
/**요구수질 */
reqWaterQlty: "",
grssClfg: null,
tn: null,
combResdch: null,
tph: null,
trbt: null,
ph: null,
bod: null,
reqWaterQlty: '',
grssClfg: '',
tn: '',
combResdch: '',
tph: '',
trbt: '',
ph: '',
bod: '',
/**컬럼확인필요 */
clIonQnt: null,
smll: "",
cdtnv: null,
crmty: null,
rmrk: "",
clIonQnt: '',
smll: '',
cdtnv: '',
crmty: '',
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<SdmInfoFormData>({
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues,
mode: "onSubmit",
mode: "onChange",
});
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]);
//후보지 팝업 값 셋팅
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (event.data?.type === "SET_SUBS_INFO") {
setSubsInfo(event.data.payload); // zustand로 값 설정
}
};
},[subsInfo, form])
window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, [setSubsInfo]);
//후보지 팝업 값 셋팅
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]);
// 로그인 성공 시 모달 닫기 및 폼 업데이트
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<SdmInfoFormData> = async (data) => {
const router = useRouter();
const onSubmit: SubmitHandler<FormData> = async (data) => {
try {
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) || "",
// 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) || '',
dmndTmng: data.demandYmd || new Date(),
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 || "",
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 || '',
bzmnNo: "",
dmdAplyPswd: "",
index: 0,
reqDt: "",
dmdInfoSn: 0,
status: "",
splyCndtList: dmSubsInfo || [],
status: '',
splyCndtList: data.candidateTitle || [],
};
await insertSdmInfoData(apiData);
setTimeout(() => {
router.push("/");
}, 1000); // 0.8초 후 이동
} catch (error) {
console.error("Error submitting form:", error);
console.error('Error submitting form:', error);
}
};
@@ -232,10 +253,8 @@ function SdmInfoInsertContent() {
/>
<div className="flex justify-center pt-4">
<Button
color="sky"
size={"sm"}
type="submit"
className="w-40 font-semibold py-2 px-4 rounded-md "
className="w-40 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-md transition-colors duration-200"
>
수요정보 등록
</Button>
@@ -245,7 +264,7 @@ function SdmInfoInsertContent() {
</div>
</div>
{!isLogin ? (
{!isLogin ?
<UiModal
body={<SdmLogin />}
bodyClass="sm:max-w-[90vw] w-[530px] h-[430px]"
@@ -258,7 +277,7 @@ function SdmInfoInsertContent() {
noHeaderYn="Y"
noFooterYn="Y"
/>
) : null}
: null}
<Toaster position="top-center" />
</div>
);
@@ -271,3 +290,4 @@ 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,9 +44,15 @@ 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)
insertSdmData(convertedData).catch(error => {
console.error('Error inserting SDM data:', error);
});
}, [insertSdmData]);
return (
@@ -64,3 +70,4 @@ 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-[#D63F1F] text-5xl font-semibold mb-8">물 재이용, 왜 필요한가요?</h1>
<h1 id="card2-desc" className="font-NotoSansKR text-[#ED5534] 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-[#00818A] text-5xl font-bold mb-6">아직 할 일이 많은 하수처리수,<br />풍족하게 재이용할 수 있어요!</h1>
<h1 id="card5-desc" className="font-NotoSansKR leading-snug text-[#0099A4] 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"
@@ -1,7 +1,7 @@
'use client';
import { CHART_FORM, MainChart, type ChartConfig } from '@common/business_components';
import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react';
import React, { useEffect, useRef, useState } from 'react';
interface RegionData {
name: string;
@@ -213,7 +213,7 @@ const WrStatusPage: React.FC = () => {
{ region: '제주특별자치도', sewage: '8(1,666)', rainwater: '47(5,746)', reclaimed: '64(660)', thermal: '0' },
];
const regionData: RegionData[] = useMemo(() => [
const regionData: RegionData[] = [
{ name: '서울', x: 170, y: 140, values: [0.3, 0.3, 0.2, 0.2] },
{ name: '인천', x: 120, y: 150, values: [0.3, 0.3, 0.2, 0.2] },
{ name: '경기', x: 210, y: 210, values: [0.3, 0.3, 0.2, 0.2] },
@@ -231,7 +231,7 @@ const WrStatusPage: React.FC = () => {
{ name: '울산', x: 450, y: 500, values: [0.3, 0.3, 0.2, 0.2] },
{ name: '부산', x: 420, y: 550, values: [0.3, 0.3, 0.2, 0.2] },
{ name: '제주', x: 60, y: 750, values: [0.3, 0.3, 0.2, 0.2] },
], []);
];
const handleMarkerClick = (regionId: string) => {
if (selectedRegion === regionId) {
@@ -242,7 +242,7 @@ const WrStatusPage: React.FC = () => {
};
const createDonutMarker = useCallback((svg: SVGSVGElement, x: number, y: number, name: string, values: number[]) => {
const createDonutMarker = (svg: SVGSVGElement, x: number, y: number, name: string, values: number[]) => {
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FED16A'];
const radius = 26;
const innerRadius = 18;
@@ -334,7 +334,7 @@ const WrStatusPage: React.FC = () => {
});
svg.appendChild(g);
}, [handleMarkerClick, regionIdMap]);
};
useEffect(() => {
const loadSVG = async () => {
@@ -374,7 +374,7 @@ const WrStatusPage: React.FC = () => {
};
loadSVG();
}, [createDonutMarker, regionData]);
}, []);
useEffect(() => {
const svg = svgContainerRef.current?.querySelector('svg');
-3
View File
@@ -8,9 +8,6 @@ export default function RootLayout({
}>) {
return (
<html lang="ko">
<head>
<title>물재이용 대민</title>
</head>
<body>
{children}
</body>
+23 -262
View File
@@ -2,108 +2,12 @@
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 DemandCompBizType = {
export type DemandCompType = {
index : number | '';
dmdInfoId : string | '';
dmdInfoFk : string | '';
@@ -119,23 +23,22 @@ export type DemandCompBizType = {
wntUntprc : number | '';
dmndTmng : Date | '';
dmndQty : 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 //색도
grssClfg : number | '';
tn : number | '';
combResdch : number | '';
tph : number | '';
trbt : number | '';
ph : number | '';
blgcOxygDmndQnt : number | '';
clIonQnt : number | '';
smll : number | '';
cdtnv : number | '';
crmty : number | '';
rmrk : string | '';
reqDt : string | '';
dmdInfoSn : number | '';
status : string | '';
splyCndtList:SupplyCandidate[]|[],
ruseUtztnFcltNm?:string|''
}
@@ -249,8 +152,7 @@ export type MoneyDetailType = {
clclnBrdnCst : string | '';
clclnEcqrRt : string | '';
}
// 규칙 타입 선언
export type DisableRulesType = Record<string, string[]>;
/**====================================
* Store - 데이터
====================================*/
@@ -258,139 +160,6 @@ export type DisableRulesType = Record<string, string[]>;
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: "청소" },
@@ -398,9 +167,9 @@ const useTypesData : SelectDataType[] = [
{ text: "세척", val: "세척" },
{ text: "살수", val: "살수" },
{ text: "조경", val: "조경" },
{ text: "농업_간접용수", val: "농업_간접용수" },
{ text: "농업_직접용수", val: "농업_직접용수" },
{ text: "농업", val: "농업" },
{ text: "하천", val: "하천" },
{ text: "지하수", val: "지하수" },
{ text: "공업", val: "공업" },
];
@@ -511,7 +280,7 @@ const postSdmData = async (data: DemandCompType) => {
}
// 수요정보 등록
const postSdmInfoData = async (data: DemandCompBizType) => {
const postSdmInfoData = async (data: DemandCompType) => {
try {
await postFetch("/reuseFc/request/demandInfo", data);
toast.success('수요 정보 등록에 성공하였습니다.');
@@ -591,7 +360,7 @@ const fetchGetSimilarCaseList = async (part: string, value: number|string) => {
export type SdmActions = {
initializeSdmData: () => void;
insertSdmData: (data: DemandCompType) => Promise<void>;
insertSdmInfoData: (data: DemandCompBizType) => Promise<void>;
insertSdmInfoData: (data: DemandCompType) => Promise<void>;
getDemandCompList: (searchText: string, pageCnt: number) => Promise<void>;
actionLogin: (data: DemandCompType) => Promise<void>;
getSdmInfoStatusList: (dmdInfoId: string) => Promise<void>;
@@ -609,10 +378,9 @@ export type SdmType ={
moneyDetailList: MoneyDetailType[];
// 수요정보 신청현황
sdmInfoStatusList: DemandCompBizType[];
sdmInfoStatusList: DemandCompType[];
upHeadSdmInfoStatus: TableUpperProps[];
defaultRules: Record<string, object>
disableRules: DisableRulesType;
// 로그인관련
successNextPage : boolean;
successLogin: boolean;
@@ -630,10 +398,7 @@ export const SdmState: SdmType = {
moneyDetailList: moneyDetailList,
// 수요정보 신청현황
sdmInfoStatusList: [],
defaultRules:defaultRules,
disableRules:disableRules,
upHeadSdmInfoStatus: upHeadSdmInfoStatus,
// 로그인관련
successNextPage : false,
successLogin: false,
@@ -663,7 +428,7 @@ export const SdmReducer = (set: any): SdmActions => {
}
},
insertSdmInfoData: async (data: DemandCompBizType) => {
insertSdmInfoData: async (data: DemandCompType) => {
postSdmInfoData(data);
},
@@ -700,13 +465,11 @@ export const SdmReducer = (set: any): SdmActions => {
}))
return;
}
const sdmInfoStatusList : DemandCompBizType[] = [];
const sdmInfoStatusList : DemandCompType[] = [];
res.demandInfo.map((el: DemandCompBizType, index: number) => {
res.demandInfo.map((el: DemandCompType, 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,
@@ -722,9 +485,7 @@ export const SdmReducer = (set: any): SdmActions => {
useUsgCd: el.useUsgCd,
dmndTmng: el.dmndTmng,
dmdInfoSn : el.dmdInfoSn,
status : status,
splyCndtList: el.splyCndtList,
ruseUtztnFcltNm: ruseUtztnFcltNm || ''
status : status
}
sdmInfoStatusList.push(tmpData);
})
+2 -4
View File
@@ -13,10 +13,8 @@ export const StoreZus = (initState: DmState= {
...dmMapState,
}) => {
return createStore<DmStore>()((set) => {
if (typeof window !== "undefined") {
tokenManager.setToken("site-id-key", 'DM');
tokenManager.setToken("site-theme-key", 'green');
}
tokenManager.setToken("site-id-key", 'DM');
tokenManager.setToken("site-theme-key", 'green');
return {
@@ -1,5 +1,5 @@
import { useDmStore } from '@/app/store';
import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form';
import type { DemandCompType } from '@/app/store/sdmMng';
import React, { useCallback, useEffect, useRef, useState } from 'react';
type thisProps = {
@@ -1,18 +0,0 @@
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, "");
};
@@ -118,6 +118,14 @@ export function SearchBar({
/>
</div>
<div className="flex flex-row space-x-2">
<Button
color="red"
className="mx-2 mt-2"
size={"sm"}
onClick={() => {}}
>
후보지선택
</Button>
<Button
color="sky"
className="mt-2"
@@ -1,29 +1,28 @@
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;
isDisabled?: boolean;
inputComponent: React.ReactElement<{ disabled?: boolean }>;
inputComponent: ReactNode;
}
export function AccessibleFormField<T extends FieldValues>({
label,
required = false,
error,
inputComponent,
isDisabled = false,
}: AccessibleFormFieldProps<T>) {
const labelText = required ? `${label} *` : label;
return (
<FormUi label={labelText} isDisabled={isDisabled}>
<FormUi label={labelText}>
<div>
{isValidElement(inputComponent)
? cloneElement(inputComponent, { disabled: isDisabled } as Partial<typeof inputComponent.props>)
: inputComponent}
{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}
File diff suppressed because it is too large Load Diff
@@ -1,12 +1,11 @@
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, useMemo, useState } from "react";
import { useEffect, useState, useMemo } 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() {
@@ -16,10 +15,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;
@@ -29,33 +28,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 SdmLoginForm = z.infer<typeof sdmLoginFormSchema>;
type FieldComponent = (field: ControllerRenderProps<SdmLoginForm, keyof SdmLoginForm>) => JSX.Element;
type FormData = z.infer<typeof sdmLoginFormSchema>;
type FieldComponent = (field: ControllerRenderProps<FormData, keyof FormData>) => JSX.Element;
const formSchema = sdmLoginFormSchema;
useEffect(() => {
initializeCommonData();
}, [initializeCommonData]);
const defaultValues: SdmLoginForm = {
const defaultValues: FormData = {
dmdInfoId: '',
bznmTotNo: '',
password: '',
@@ -63,7 +62,7 @@ export function SdmLoginForm() {
dmdAplyPswd: '',
};
const form = useForm<SdmLoginForm>({
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues,
mode: "onChange",
@@ -73,23 +72,6 @@ export function SdmLoginForm() {
setIsBznmTotNoDisabled(checked);
};
const initPassword = () => {
toast(
() => (
<div className="flex flex-col items-center">
<div className="text-2xl mb-2">
관리자(031-0000-0000)에게 비밀번호 초기화 요청을 유선으로 하시기 바랍니다.
</div>
</div>
),
{
id: "pwd-reset",
duration: 5000,
position: "top-center",
}
);
}
const handleLogin = () => {
const data = form.getValues();
const passwordStr = sha512.hex(data.password)
@@ -100,61 +82,61 @@ export function SdmLoginForm() {
}
actionLogin(param as unknown as DemandCompType);
};
const formFields: Array<{
name: keyof SdmLoginForm;
name: keyof FormData;
label: string;
component: FieldComponent | undefined;
}> = [
{
name: "dmdInfoId",
label: "사업자등록번호",
component: (field) => (
<Input
placeholder="사업자등록번호를 입력하세요"
{
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="여러사업장이 있을 경우 입력해주세요"
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="여러사업장이 있을 경우 입력해주세요"
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"
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}
/>
)
}
];
return (
<>
@@ -166,11 +148,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>
)}
@@ -178,15 +160,12 @@ export function SdmLoginForm() {
))}
</FormProvider>
<div className="flex justify-center pt-4">
<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>
<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>
</div>
</>
+2 -2
View File
@@ -3,9 +3,9 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3005",
"dev": "next dev --turbopack",
"build": "next build",
"start:dm": "next start -p 3005",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 70 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({
@@ -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({
@@ -162,7 +162,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
<span className="text-2xl text-red-600 font-bold">● 침입수량 산정결과</span>
<div className="flex items-center mt-2">
<span className="ml-5 text-black text-xl">침입수량 : </span>
<span className="w-[35px] inline-block text-right text-black text-xl font-bold">{inftwtQty}</span>
<span className="w-[45px] inline-block text-right text-black text-xl font-bold">{inftwtQty}</span>
<span className="ml-2 text-black text-xl">m³/d</span>
</div>
</div>
@@ -170,7 +170,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
{/* 차트 */}
<div className="h-[90%] bg-white text-3xl">
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'침입수량 그래프'}
yFieldText2="시간" yFieldText="유량(m³/h)"/>
yFieldText2="" yFieldText="유량(m³/h)"/>
</div>
</div>
{popupOpen && (
@@ -161,17 +161,17 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
<span className="text-2xl text-red-600 font-bold">● 강우유입수량 분석결과</span>
<div className="flex items-center mt-2">
<span className="w-[95px] inline-block text-right text-black text-xl">평균RDII : </span>
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {rdiiQty}</span>
<span className="w-[45px] inline-block text-right text-black text-xl font-bold"> {rdiiQty}</span>
<span className="ml-2 text-black"> m³/d</span>
</div>
<div className="flex items-center mt-2">
<span className="w-[95px] inline-block text-right text-black text-xl">적용강우량 : </span>
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {rainTotal}</span>
<span className="w-[45px] inline-block text-right text-black text-xl font-bold"> {rainTotal}</span>
<span className="ml-2 text-black"> mm</span>
</div>
<div className="flex items-center mt-2">
<span className="w-[95px] inline-block text-right text-black text-xl">강우량대비RDII : </span>
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {totalVolume}</span>
<span className="w-[45px] inline-block text-right text-black text-xl font-bold"> {totalVolume}</span>
<span className="ml-2 text-black"> m³/mm</span>
</div>
</div>
@@ -1,14 +1,15 @@
'use client';
import React, { useEffect, useState } from 'react';
import { Button } from '@common/components';
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
import { SelectBox } from '@common/business_components/ui';
import { Button, Input } from '@common/components';
import React from 'react';
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
import { SelectBox, UiTable } from '@common/business_components/ui';
import LoadingOverlay from '@/components/ui/LoadingBar';
import { addFileName, flattenFolders, getFileDownload, useCityCd, useDcTree, useSggCd, reportGubuns } from './searchModalReportApi';
import { type SelectDataType } from '../commonStore';
import { useCityCd, useSggCd } from '../components/searchModalReportApi';
// import { useBizByFcltStore } from "../../../store/StoreProvider";
// // 하수도정비 기본계획 보고서
// 하수도정비 기본계획 보고서
export const reportTableHeadlist = [
{ id: "no", title: "NO" },
{ id: "sido", title: "시도" },
@@ -23,79 +24,180 @@ export const reportTableHeadlist = [
interface BasicTabProps {
open?: boolean;
gubun: string;
sidos: SelectDataType[];
ctpvCd: string | number;
sigungu: string;
filteredSiguns: SelectDataType[];
yearFilter: { start: string; end: string; };
handleSearch: () => void;
handleCtpvCdChange: (val: string | number) => void;
// onOpenChange?: (open: boolean) => void;
// onFeatureSelect?: (row: any) => void;
// setTableData?: (data: any[]) => void;
// setTableCount?: (count: number) => void;
setYearFilter: (filter: { start: string; end: string }) => void;
setGubun: (gubun: string) => void;
setSigungu: (sigungu: string) => void;
}
const ReportTab: React.FC<BasicTabProps> = ({
open,
gubun,
sidos,
ctpvCd,
sigungu,
filteredSiguns = [{ val: '0', text: '전체', sido: '0' }],
yearFilter,
handleSearch,
handleCtpvCdChange,
// onOpenChange,
// onFeatureSelect,
// setTableData,
// setTableCount,
setYearFilter,
setGubun,
setSigungu,
}) => {
const ReportTab: React.FC<BasicTabProps> = ({ open }) => {
const [isLoading, setIsLoading] = useState(false);
const [isResetPage, setIsResetPage] = useState<boolean>(false);
const [isResetSelection, setIsResetSelection] = useState<boolean>(false);
const [showTable, setShowTable] = useState(false);
const [tableData, setTableData] = useState<any[]>([]);
const [tableCount, setTableCount] = useState(0);
const [curPage, setCurPage] = useState(1);
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
const [ctpvCd, setCtpvCd] = useState('0'); // 하수정비기본계획 report
const [sigungu, setSigungu] = useState('0'); // 하수정비기본계획 report
const [filteredSiguns, setFilteredSiguns] = useState<SelectDataType[]>([{ val: '0', text: '전체', sido: '0' }]);
const [gubun, setGubun] = useState(""); // 하수정비기본계획 report
const [reportGubun, setReportGubun] = useState("0"); // 하수정비기본계획 report
// 하수정비기본계획 report
const sidos = useCityCd();
const siguns = useSggCd();
const reportOriginData = useDcTree(""); //:Folder[]
const dynamicHeadlist: { id: string, title: string }[] = reportTableHeadlist;
useEffect(() => {
// filterParams 초기화
setCtpvCd('0');
setSigungu('0');
setReportGubun('0');
setYearFilter({ start: "", end: "" });
setShowTable(false);
setTableData([]);
setTableCount(0);
setCurPage(1); // 페이지를 1로 초기화
setIsResetPage(true); // 페이지를 초기화
setIsResetSelection(true); // 선택 초기화
}, [open]);
// 페이지 변경 시마다 데이터 fetch
useEffect(() => {
// showTable이 true일 때만 fetch (최초 렌더 방지)
setIsResetPage(false);
setIsResetSelection(true); // 선택 초기화
if (showTable) fetchFilteredData();
}, [curPage]);
const handleCtpvCdChange = (val: string | number) => { // 하수도정비기본계획.시도
const sidoOption = sidos.find((n) => n.val === val);
setCtpvCd(String(val));
if (val !== '0') {
const filterSiguns = siguns.filter(item => item.sido === sidoOption?.text);
filterSiguns.unshift({ val: '0', text: '전체', sido: '0' });
setFilteredSiguns(filterSiguns);
}
setSigungu("0");
setGubun("");
setYearFilter({ start: "", end: "", });
}
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
const handleSearch = () => {
setCurPage(1); // 페이지를 1로 초기화
fetchFilteredData();
setIsResetPage(true); // 페이지를 초기화
setIsResetSelection(false); // 선택 초기화
setShowTable(true);
};
// 하수도정비기본계획 테이블 cellClick 핸들러
const handleReportCellClick = (index: number | undefined ) => {
console.log(index);
if (typeof index === "number" && tableData[index]) {
const reportData = tableData[index];
const fileName = reportData.fileName;
const filePath = reportData.filePath;
if (reportData.fileSn !== "") {
getFileDownload(reportData.fileSn, fileName, filePath);
}
}
};
// 검색 API 호출 함수
const fetchFilteredData = async () => {
setIsLoading(true); // 로딩 시작
setIsResetSelection(true); // 선택 초기화
try {
//const reportOriginData = await getDcTree('경기도');
const reportData = flattenFolders(reportOriginData);
let filterReportData = reportData;
if (ctpvCd !== "0") { // 시도검색
const sidoOption = sidos.find((n) => n.val === ctpvCd);
filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text);
}
if (sigungu !== "0") { // 시군구검색
const sigunguOption = filteredSiguns.find((n) => n.val === sigungu);
filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text);
}
if (reportGubun !== "0") { // 보고서구분검색
const reportGubunOption = reportGubuns.find((n) => n.val === reportGubun);
filterReportData = filterReportData.filter(item => item.reportGubun === reportGubunOption?.text);
}
if (yearFilter.start != "" && yearFilter.end == "") //승인년도검색
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" : null);
if (yearFilter.start == "" && yearFilter.end != "")
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear <= yearFilter.end + "년" : null);
if (yearFilter.start != "" && yearFilter.end != "") {
filterReportData = filterReportData.filter(item => {
return (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" && item.approvalYear <= yearFilter.end + "년" : null;
});
}
if (gubun !== "") { // 구분검색
filterReportData = filterReportData.filter(item => (item.plantZoneGugun) ? String(item.plantZoneGugun).includes(String(gubun)) : null);
}
console.log('report count:', filterReportData.length);
setTableCount(filterReportData.length || 0);
filterReportData = filterReportData.slice((curPage - 1) * 10, curPage * 10); // paging
filterReportData = filterReportData?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 }));
//const fileList = await getDcFile("CLS20250530000041");
addFileName(filterReportData).then(data => {
filterReportData = data;
setTableData(filterReportData || []);
});
setTableData(filterReportData || []);
setShowTable(true);
} catch (err) {
console.error('API ERROR:', err);
setTableData([]);
} finally {
setIsLoading(false); // 로딩 종료
}
};
return (
<>
<div className="relative flex flex-col border rounded-lg p-6 w-full h-[10vh]">
<div className="flex items-center mb-3">
<SelectBox
className="w-[150px] mr-8"
<SelectBox className="w-[150px] mr-8"
selectClass="rounded-lg bg-white"
label="시도"
selectArray={sidos}
value={ctpvCd}
onSelectValue={handleCtpvCdChange}
label="시도" selectArray={sidos}
value={ctpvCd} onSelectValue={handleCtpvCdChange}
selectContentClass="z-[101]"
/>
<SelectBox
className="w-48"
<SelectBox className="w-48"
selectClass="w-48 rounded-lg bg-white truncate"
label="시군구"
selectArray={filteredSiguns}
value={sigungu}
onSelectValue={val => setSigungu(String(val))}
label="시군구" selectArray={filteredSiguns}
value={sigungu} onSelectValue={val => setSigungu(String(val))}
selectContentClass="z-[101]"
/>
</div>
<div className="flex items-center mb-6">
<label className="inline-block w-auto whitespace-nowrap text-xl text-gray-600 mr-2">구분</label>
{/* <label className="inline-block w-auto whitespace-nowrap text-xl text-gray-600 mr-2">구분</label>
<Input
className='!text-xl w-[100px] placeholder:text-center mr-8'
placeholder="입력"
value={gubun}
onChange={(e) => { setGubun(e.target.value); }}
/> */}
<SelectBox className="w-[150px] mr-8"
selectClass="rounded-lg bg-white"
label="보고서구분" selectArray={[...reportGubuns]}
value={reportGubun} onSelectValue={val => setReportGubun(String(val))}
selectContentClass="z-[101]" labelClass='whitespace-nowrap text-xl text-gray-600 pr-2'
/>
<YearRangeSelectBox
start={yearFilter.start}
end={yearFilter.end}
{/* 승인년도 필터 */}
<YearRangeSelectBox start={yearFilter.start} end={yearFilter.end}
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
startLabel="승인년도"
endLabel=""
startLabel="승인년도" endLabel=""
/>
{/* 조회 버튼 */}
<div className="flex-1 flex justify-end">
@@ -103,6 +205,28 @@ const ReportTab: React.FC<BasicTabProps> = ({
</div>
</div>
</div>
<div className="h-[79vh] w-full overflow-hidden rounded-lg border">
{/* 테이블2 영역 (가로/세로 스크롤 가능) */}
<div className="flex-1 px-2 pb-2">
<LoadingOverlay isLoading={isLoading} />
<UiTable
headName={(() => {
return (<>조회 결과 ({tableCount.toLocaleString('ko-KR')})건</>);
})()
}
tableData={tableData}
headlist={dynamicHeadlist}
pageSize={10}
total={tableCount}
cellClick={handleReportCellClick}
onPageChange={setCurPage}
resetPageNo={isResetPage}
resetSelection={isResetSelection}
/>
</div>
</div>
</>
);
};
@@ -3,11 +3,8 @@ 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 { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
import { type SelectDataType } from '../commonStore';
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES } from '../components/common/table';
import { addFileName, flattenFolders, getFileDownload, useCityCd, useDcTree, useSggCd } from '../components/searchModalReportApi';
// import { useBizByFcltStore } from "../../../store/StoreProvider";
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';
@@ -68,9 +65,10 @@ const SearchModal: React.FC<SearchModalProps> = ({
const [detailBizId, setDetailBizId] = useState(0);
const [city, setCity] = useState(0);
const [sgg, setSgg] = useState(0);
const [ctpvCd, setCtpvCd] = useState('0');
const [sigungu, setSigungu] = useState('0');
const [gubun, setGubun] = useState(""); // 하수정비기본계획 report
const [ctpvCd, setCtpvCd] = useState('0'); // 하수정비기본계획 report
const [sigungu, setSigungu] = useState('0'); // 하수정비기본계획 report
const [gubun, setGubun] = useState(""); // 하수정비기본계획 report
const [reportGubun, setReportGubun] = useState("0"); // 하수정비기본계획 report
const [start1, setStart1] = useState("");
const [start2, setStart2] = useState("");
const [end1, setEnd1] = useState("");
@@ -83,10 +81,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
const [filteredSiguns, setFilteredSiguns] = useState<SelectDataType[]>([{ val: '0', text: '전체', sido: '0' }]);
const sidos = useCityCd();
const siguns = useSggCd();
const reportOriginData = useDcTree(""); //:Folder[]
// 오늘 날짜 구하기
const today = new Date();
const todayYear = today.getFullYear().toString();
@@ -105,7 +100,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
return [];
}
if (activeTab === 'operation') return operationTableHeadlist;
if (activeTab === 'report') return reportTableHeadlist;
return [];
}, [activeTab, basicType, workType]);
@@ -134,8 +128,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
setVar4("");
setCity(0);
setSgg(0);
setCtpvCd('0');
setSigungu('0');
setYearFilter({
start: "",
end: "",
@@ -281,41 +273,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
setDetailTableData([]);
setDetailTableCount(0);
} else if (activeTab === 'report') {
//const reportOriginData = await getDcTree('경기도');
const reportData = flattenFolders(reportOriginData);
let filterReportData = reportData;
if (ctpvCd !== "0") { // 시도검색
const sidoOption = sidos.find((n) => n.val === ctpvCd);
filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text);
}
if (sigungu !== "0") { // 시군구검색
const sigunguOption = filteredSiguns.find((n) => n.val === sigungu);
filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text);
}
if (yearFilter.start != "" && yearFilter.end == "") //승인년도검색
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" : null);
if (yearFilter.start == "" && yearFilter.end != "")
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear <= yearFilter.end + "년" : null);
if (yearFilter.start != "" && yearFilter.end != "") {
filterReportData = filterReportData.filter(item => {
return (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" && item.approvalYear <= yearFilter.end + "년" : null;
});
}
if (gubun !== "") { // 구분검색
filterReportData = filterReportData.filter(item => (item.plantZoneGugun) ? String(item.plantZoneGugun).includes(String(gubun)) : null);
}
console.log('report count:', filterReportData.length);
setTableCount(filterReportData.length || 0);
filterReportData = filterReportData.slice((curPage - 1) * 10, curPage * 10); // paging
filterReportData = filterReportData?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 }));
//const fileList = await getDcFile("CLS20250530000041");
addFileName(filterReportData).then(data => {
filterReportData = data;
setTableData(filterReportData || []);
});
setTableData(filterReportData || []);
}
}
setShowTable(true);
} catch (err) {
@@ -588,31 +546,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
}
}
};
// 하수도정비기본계획 테이블 cellClick 핸들러
const handleReportCellClick = (index: number | undefined, headId?: String) => {
console.log(index);
if (typeof index === "number" && tableData[index]) {
const reportData = tableData[index];
const fileName = reportData.fileName;
const filePath = reportData.filePath;
if (reportData.fileSn !== "") {
getFileDownload(reportData.fileSn, fileName, filePath);
}
}
};
const handleCtpvCdChange = (val: string | number) => { // 하수도정비기본계획.시도
const sidoOption = sidos.find((n) => n.val === val);
setCtpvCd(String(val));
if (val !== '0') {
const filterSiguns = siguns.filter(item => item.sido === sidoOption?.text);
filterSiguns.unshift({ val: '0', text: '전체', sido: '0' });
setFilteredSiguns(filterSiguns);
}
setSigungu("0");
setGubun("");
setYearFilter({ start: "", end: "", });
}
const renderBody = () => (
<>
@@ -673,17 +607,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
{activeTab === 'report' && (
<ReportTab
open={open}
gubun={gubun}
sidos={sidos}
ctpvCd={ctpvCd}
sigungu={sigungu}
filteredSiguns={filteredSiguns}
yearFilter={yearFilter}
handleSearch={handleSearch}
handleCtpvCdChange={handleCtpvCdChange}
setYearFilter={setYearFilter}
setGubun={setGubun}
setSigungu={setSigungu}
/>
)}
{/* </UITab2> */}
@@ -691,7 +614,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
{/* 📋 결과 리스트 */}
{/* 기본정보 탭, 하수도정비기본계획보고서 탭 */}
{(activeTab === 'basic' || activeTab === 'report') && (
{(activeTab === 'basic' ) && (
<div className="h-[79vh] w-full overflow-hidden rounded-lg border">
{/* 테이블2 영역 (가로/세로 스크롤 가능) */}
<div className="flex-1 px-2 pb-2">
@@ -706,7 +629,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
headlist={dynamicHeadlist}
pageSize={10}
total={tableCount}
cellClick={activeTab === 'report' ? handleReportCellClick : handleBasicCellClick}
cellClick={handleBasicCellClick}
onPageChange={setCurPage}
resetPageNo={isResetPage}
resetSelection={isResetSelection}
@@ -31,6 +31,14 @@ const isYear = (value: string): boolean => {
return /^[0-9]{4}년$/.test(value);
};
export const reportGubuns = [{val: '0', text: '전체'}, {val: '1', text: '보고서'}
, {val: '2', text: '요약보고서'} , {val: '3', text: '부록'}, {val: '4', text: '기타'}];
/**
* Flatten a hierarchical folder structure into a flat array.
* @param data - The hierarchical folder data to flatten.
* @returns An array of flattened folders with their paths and levels.
*/
export function flattenFolders(data: Folder[]): FlattenedFolder[] {
const result: FlattenedFolder[] = [];
const exceptNodeNm = ["부분","통합","전체"];
@@ -72,15 +80,10 @@ export function flattenFolders(data: Folder[]): FlattenedFolder[] {
}
data.forEach((root) => traverse(root, {}, 0));
console.log(result.slice(0, 10));
return result;
}
export const getDcTree = async (clsfSystmNm:string) => {
//const cityName = encodeURIComponent(clsfSystmNm);
const queryString = 'clsfSystmNm='+clsfSystmNm;
console.log("getDcTree queryString:", queryString);
//const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`);
const result = await getDcData("/api/v1/dc/clsf?clsfSystmNm=" + clsfSystmNm);
return result;
};
@@ -110,13 +113,8 @@ export const addFileName = async (data:FlattenedFolder[]) => {
data.map(async (item) => {
const filenameArr:ReportDataFile[] = await getDcFile(String(item.clsfSystmSn));
const f = filenameArr[0];
//console.log("clsfSystmSn:", item.clsfSystmSn, "f:", f);
const result = {...item, fileName:f?.fileTitle, regDate:f?.regDt, fileSn:f?.fileSn, filePath:f?.filePath};
// if (f !== undefined && f.fileSn !== "") {
// const fprcs = await getDcFileprcs(f?.fileSn);
// if (fprcs.length>0)
// result.filePath = fprcs[0].filePath;
// }
return result;
})
);
@@ -124,7 +122,6 @@ export const addFileName = async (data:FlattenedFolder[]) => {
}
export const getDcFileprcs = async (fileSn:string) => {
const queryString = 'fileSn='+fileSn;
console.log("getDcTree queryString:", queryString);
const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`);
return result;
};
@@ -151,9 +148,6 @@ export const fileDownload = (data:Blob, fileName:string) => { // Blob데이터
window.URL.revokeObjectURL(url);
}
export const getFileDownload = async (fileSn:string, fileName:string, filePath:string) => {
//const fileprcsData = await getDcFileprcs(fileSn); // filePath 조회
//if (fileprcsData?.length === 0 ) { alert("filePath 조회 오류"); }
//if (fileprcsData[0].filePath === "") alert("filePath 정보 없으므로 다운로드 불가");
if (filePath === "") alert("filePath 정보 없으므로 다운로드 불가:filesn"+ fileSn);
const blobData = await getDcFileDownload(filePath); // 실제 다운로드 API
fileDownload(blobData, fileName);
@@ -165,12 +159,13 @@ export const getCityCd = async (): Promise<SelectDataType[]> => {
if (!response.ok) throw new Error(`시도 데이터 오류: ${response.status}`);
const res = await response.json();
const city = res.data;
const cityCd = city.map((item: any) => ({
let citys:SelectDataType[] = city.map((item: {adstDstrCd:string, sido:string}) => ({
val: item.adstDstrCd,
text: item.sido,
}));
cityCd.unshift({ val: '0', text: '전체' });
return cityCd;
citys = citys.sort((a:SelectDataType, b:SelectDataType) => a.text.localeCompare(b.text));
citys.unshift({ val: '0', text: '전체' });
return citys;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
return [{ val: '0', text: '전체', sido: '0' }];
@@ -186,9 +181,9 @@ export const useSggCd = () => {
if (result) {
setData(result);
}
console.log("useSggCd호출");
};
fetch();
console.log("useSggCd호출");
}, []);
return data;
};
@@ -200,7 +195,7 @@ export const getSggCd = async (): Promise<SelectDataType[]> => {
if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`);
const res = await response.json();
const sgg = res.data;
const sggCd = sgg.map((item: any) => ({
const sggCd = sgg.map((item: {adstDstrCd:string, sido:string, gugun:string}) => ({
val: item.adstDstrCd,
sido: item.sido,
text: item.gugun,
@@ -222,9 +217,9 @@ export const useCityCd = () => {
if (result) {
setData(result);
}
console.log("useCityCd호출");
};
fetch();
console.log("useCityCd호출");
}, []);
return data;
};
-1
View File
@@ -87,7 +87,6 @@ export const deleteData = async (url: string, data?: object): Promise<any> => {
// RSONE DC API 호출
export const getDcData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/dc-service' + url, {
method: "GET",
});
+5 -12
View File
@@ -61,13 +61,16 @@ const Map = () => {
// 2. data가 OpenLayers Feature 인스턴스인지 확인
let foundFeature: any = null;
console.log(data)
//console.log(data.getId())
let featureId = data.fid;
const layerName = data.layerNm;
if (layerName === "tb_wr_bdst_ruse_fclt") {
if (layerName === "재이용시설") {
// 재이용시설
featureId = `${layerName}.${data.searchPk}`;
} else if ( //빗물이용시설, 중수도시설
layerName === "tb_wr_rnwtr_utztn_fclt" ||
layerName === "빗물이용시설" ||
layerName == "tb_wr_rswtr_fclt"
) {
featureId = `${layerName}.${data.bdstInfoSn}`;
@@ -136,16 +139,6 @@ 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} // 메모이제이션된 함수 사용
+12 -9
View File
@@ -116,6 +116,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
const [mngList, setMngList] = useState<DemandCompType[]>(coSttnRes || []);
///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 ///////////////////////////////////////////////
const getSelectedLayers = () => {
console.log(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 };
@@ -127,6 +128,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
}
});
result = featureLayer.get('lyrNm');
console.log(result)
return result;
};
@@ -156,15 +158,15 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
}
};
const detainSttn = async (featureCd: string, layerNm: string) => {
if (layerNm == 'tb_wr_rnwtr_utztn_fclt') { //빗물이용시설
if (layerNm == '빗물이용시설') { //빗물이용시설
const res = await getFetch(fetchType.WR, `/reuseFc/rainyInfoList/1/20/0/${featureCd}`)
setRainList(res)
}
else if (layerNm == 'tb_wr_rswtr_fclt') { //중수도 시설
else if (layerNm == '중수도시설') { //중수도 시설
const res = await getFetch(fetchType.WR, `/reuseFc/waterreuseFc/1/20/0/${featureCd}`)
setRswtrList(res)
}
else if(layerNm =='tb_wr_ruse_fclt_dmd_co'){ //수요처
else if(layerNm =='수요처'){ //수요처
const res = await getFetch(fetchType.WR, `/reuseFc/request/1/20/0/${featureCd}`)
setMngList(res)
}
@@ -192,6 +194,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
},[mngList])
const layerId = getSelectedLayers();
console.log(layerId)
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;
@@ -505,17 +508,17 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
layerId = seletectedLayerNm || '';
}
//하수처리시설
const isPrcsFclt = layerId == 'tb_il_swage_prcs_fclt'
const isPrcsFclt = layerId == '하수처리시설'
// 재이용시설
const isPbnst = layerId == 'tb_wr_bdst_ruse_fclt';
const isPbnst = layerId == '재이용시설';
// 빗물이용시설
const isRain = layerId == 'tb_wr_rnwtr_utztn_fclt';
const isRain = layerId == '빗물이용시설';
// 중수도시설
const isRswt = layerId == 'tb_wr_rswtr_fclt';
const isRswt = layerId == '중수도시설';
// 온배수시설
const isHtdfl = layerId == 'tb_wr_htdfl_fclt';
const isHtdfl = layerId == '온배수시설';
// 수요처관리
const isReuseMng = layerId == 'tb_wr_ruse_fclt_dmd_co';
const isReuseMng = layerId == '수요처관리';
if (isPrcsFclt) {
if (viewMode == 'list') {
return (
@@ -71,7 +71,7 @@ export default function PublicReuseFaciltyInsert() {
// defaultValues: { items: pbnstRegArrayZod.parse([]) },
mode: "onChange",
});
const { isDirty } = form.formState;
// const { isDirty } = form.formState;
const {
fields: items,
@@ -90,10 +90,10 @@ export default function PublicReuseFaciltyInsert() {
}, [pbnst.isInit, pubHasuInit]);
const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => {
if (!isDirty) {
toast.error("변경사항이 없습니다."); // 변경사항이 없으면 메시지 표시
return;
}
// if (!isDirty) {
// toast.error("변경사항이 없습니다."); // 변경사항이 없으면 메시지 표시
// return;
// }
const data = writeRgtrAndMdfr(valuse.items);
let bizs = [] as fcBizType[];
data.forEach((fclt) => {
@@ -1,6 +1,7 @@
"use client";
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import { prfRegPageSize } from "@/app/store/prfmncReg/pbnstPrfmncReg";
import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type";
import { excelDownload, yearOptions } from "@/components/lib/func/commom";
@@ -33,6 +34,8 @@ export default function PbnstSttn() {
} = useWrStore((state) => state);
const { pbnstSttnRes, fclts, pbnstSttnResCount } = pbnstSttn;
const [isReset, setIsReset] = useState(false);
const ref = useRef<ReturnType>({} as ReturnType);
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
@@ -67,6 +70,24 @@ export default function PbnstSttn() {
},
[getPbnStnDetail, pbnstSttnRes]
);
useEffect(() => {
if (!pbnstSttn.isInit) {
getFlcts();
}
}, [pbnstSttn.isInit]);
useEffect(() => {
pbnSttnInit("1", "15", ref.current, searchYear, fclts);
}, [fclts]);
useEffect(() => {
if (pbnstSttnRes && pbnstSttnRes.length > 0) {
setBdstTable(pbnstSttnRes);
} else {
setBdstTable([]);
}
}, [pbnstSttnRes]);
useEffect(() => {
if (pbnstSttn.excelList.length >= 0) {
@@ -79,23 +100,7 @@ export default function PbnstSttn() {
);
}
}, [pbnstSttn.excelList]);
useEffect(() => {
if (!pbnstSttn.isInit) {
getFlcts();
}
}, [pbnstSttn.isInit, pbnSttnInit, ref.current]);
useEffect(() => {
if (pbnstSttnRes && pbnstSttnRes.length > 0) {
setBdstTable(pbnstSttnRes);
} else {
setBdstTable([]);
}
}, [pbnstSttnRes]);
useEffect(() => {
pbnSttnInit("1", "15", ref.current, searchYear, fclts);
}, [fclts]);
useEffect(() => {
if (pbnstSttn.pbnstDetail.pbnstRuseFcltId)
@@ -157,16 +162,11 @@ export default function PbnstSttn() {
/>
</div>
}
queryClick={function (returnType: ReturnType): void {
pbnstSttnSearch(
false,
"1",
"15",
returnType,
pbnRuseNm,
searchYear,
fclts
);
queryClick={ ()=> {
// pbnSearch(returnType);
//setTableIndex(0)
setIsReset(true);
}}
excelClick={excelClick}
initClick={function (): void {
@@ -194,13 +194,16 @@ export default function PbnstSttn() {
pbnstSttnSearch(
false,
String(page),
String(15),
String(prfRegPageSize),
ref.current,
pbnRuseNm,
searchYear,
fclts
);
setIsReset(false);
}}
resetPageNo={isReset}
>
<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />
</UiTable>
@@ -38,11 +38,12 @@ import { useTranslation } from "react-i18next";
import { headMakeprfmnc } from "../chkgdnReg/TableHead";
export default function PrfmncPrfmncReg() {
const { auth, prfmnc, prfmncHasuInit, prfmnRegData, prfmncHasuNmSearch } = useWrStore(
const { auth, prfmnc, prfmncHasuInit, prfmnRegData, prfmncHasuNmSearch,getPrfFmcRes } = useWrStore(
(state) => state
);
const { pbnstPrfmncHeadList, monthHeadList, prfmncRes, prfmncResCount } = prfmnc;
const { prfPbnFclts, pbnstPrfmncHeadList, monthHeadList, prfmncRes, prfmncResCount } = prfmnc;
const { regAuthrtYn, mdfcnAuthrtYn } = auth;
const [isReset, setIsReset] = useState(false);
const [bdstTable, setbdstTable] = useState<FcPrfType[]>();
const [searchTxt, setSearchTxt] = useState<string>();
const [isExcelOpen, setIsExcelOPen] = useState<{
@@ -51,7 +52,6 @@ export default function PrfmncPrfmncReg() {
}>({ isModal: false, data: {} as FcPrfType });
const [inputData, setInputData] = useState<any[]>();
const [pbnstRuseFcltId, setpbnstRuseFcltId] = useState<string>();
const [pageNation, setPageNation] = useState<number>(1);
const [cellNum, setCellNum] = useState<number>(1);
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
@@ -65,20 +65,6 @@ export default function PrfmncPrfmncReg() {
"A1:O1"
);
}, [prfmnc.excelList]);
/*
const onSave = useCallback(
async() => {
if (pbnstRuseFcltId) {
await prfmnRegData(pageNation,'41',
excelToPbnPrfData(
inputData as unknown as JSON[],
searchYear,
pbnstRuseFcltId
) as unknown as PrfType
);
// cellClick(cellNum)
}
}, [inputData, searchYear, pbnstRuseFcltId, pageNation,cellNum]);*/
const { t } = useTranslation('base');
const onSave = useCallback(() => {
@@ -86,19 +72,20 @@ export default function PrfmncPrfmncReg() {
if (!pbnstRuseFcltId) return;
prfmnRegData(
pageNation,
1,
'41',
excelToPbnPrfData(
inputData as unknown as JSON[],
searchYear,
pbnstRuseFcltId
) as unknown as PrfType
, searchYear
, searchYear,
prfPbnFclts
);
};
confirmToast(t('isUpdateMsg'), handleConfirm);
}, [inputData, searchYear, pbnstRuseFcltId, pageNation, cellNum]);
}, [inputData, searchYear, pbnstRuseFcltId, cellNum,prfPbnFclts]);
const cellClick = useCallback(
(index: number | undefined) => {
@@ -194,10 +181,15 @@ export default function PrfmncPrfmncReg() {
useEffect(() => {
if (!prfmnc.isInit) {
const year = new Date().getFullYear()
prfmncHasuInit(1, '41', String(year));
getPrfFmcRes()
}
}, [prfmnc.isInit, prfmncHasuInit]);
}, [prfmnc.isInit, getPrfFmcRes]);
useEffect(() =>{
const year = new Date().getFullYear()
prfmncHasuInit(1, '41',String(year) ,prfPbnFclts);
},[prfPbnFclts])
useEffect(() => {
@@ -254,14 +246,8 @@ export default function PrfmncPrfmncReg() {
<div className="flex flex-row space-x-2">
<Button size={"sm"} onClick={() => {
if (searchTxt && searchTxt?.length > 0) {
prfmncHasuNmSearch(1, '41', searchYear, searchTxt)
setPageNation(1)
} else {
prfmncHasuInit(1, '41', searchYear)
setPageNation(1)
}
setIsReset(true)
}}>
조회
</Button>
@@ -314,10 +300,15 @@ export default function PrfmncPrfmncReg() {
setCellNum(index ?? 0);
}}
onPageChange={
(page) => {
prfmncHasuInit(page, '41', searchYear);
setPageNation(page)
(page:number) => {
if (searchTxt && searchTxt?.length > 0) {
prfmncHasuNmSearch(page, '41', searchYear, searchTxt,prfPbnFclts)
} else {
prfmncHasuInit(page, '41', searchYear,prfPbnFclts)
}
setIsReset(false);
}}
resetPageNo={isReset}
/>
</ScrollArea>
</ListDiv>
@@ -350,9 +341,10 @@ export default function PrfmncPrfmncReg() {
setIsExcelOPen({ ...isExcelOpen, isModal: open })
}}
setModalExcelData={async (excelData) => {
await prfmnRegData(pageNation, '41',
await prfmnRegData(1, '41',
excelData,
searchYear
,prfPbnFclts
);
// 데이터 저장 후 UI 업데이트는 useEffect가 감지하여 처리하므로 여기서는 호출하지 않습니다.
}
@@ -150,9 +150,9 @@ export default function PbnstPrfmncSttn() {
}
const tableData = [...inUsageData, ...outUsageData, usageTotalObj];
setPrfData(tableData);
setExcelData(excelPrfmncSttnParsing(prfData ?? []))
// });
setExcelData(excelPrfmncSttnParsing(tableData ?? []))
} else {
setExcelData(excelPrfmncSttnParsing([]))
setPrfData([]);
}
}
@@ -605,7 +605,11 @@ export default function PbnstPrfmncSttn() {
isResetPage(true)
}}
excelClick={excelClick}
initClick={function (): void { }}
initClick={function (): void {
setPbnRuseNm("");
setFromDate(String(new Date().getFullYear()));
setToDate(String(new Date().getFullYear()))
}}
btnClass={btnClass}
ref={ref} isAdmin={false} />
</div>
+1 -1
View File
@@ -98,7 +98,7 @@ export const DmdCoReqSchema = z.object({
z.object({
ruseUtztnFcltNm: z.string(),
ruseUtztnFcltCd: z.string(),
})
}).nullish()
).nullish(),
totalConstCost: z.number().nullish(),
});
@@ -52,8 +52,6 @@ export const coSttnActionsExport: (set: any) => coSttnActions = (
`/reuseFc/request/${pageNo}/${pageCnt}/${con}`
);
console.log('coSttnRes ', coSttnRes)
set((state: coSttnState) => {
return {
...state,
@@ -6,6 +6,7 @@ import {
isInvalidNumber,
parseRawValue,
} from "@/components/lib/func/excelFunc";
import toast from "react-hot-toast";
export const useTypes = [
"조경용수",
"청소화장실용수",
@@ -16,6 +17,18 @@ export const useTypes = [
"기타",
];
export const chkMngType =[
'위탁',
'자체'
]
export const chkSpwtPositnType = [
'유량계',
'수위계',
'펌프적산전력계',
'기타'
]
export type Buildingtype = {
bdstInfoSn: number;
ctpvCd: 1;
@@ -143,43 +156,56 @@ export const excelToRainChkData = (excelData: any, year: string) => {
})
.filter((el: any) => el !== undefined);
console.log("rainChkgdData", rainChkgdData);
if (rainChkgdData.length > 0) {
rainChkgdData.forEach((rain: any) => {
const defaultMessage = `[시설명: ${rain.bdstNm}, 점검일: ${rain.chckYmd}, 이용용도: ${rain.guideDetails[0]?.utztnUsgCd}] 항목에서 `;
if (rain.chckYmd && isInvalidDate(rain.chckYmd)) {
issues.push(
defaultMessage + `점검일: 날짜 형식 아님 (${rain.chckYmd})`
defaultMessage + `점검일: 날짜 형식 아님 입력값 : (${rain.chckYmd})`
);
}
if (rain.anlOperCst.length > 2 && isInvalidNumber(rain.anlOperCst)) {
issues.push(
defaultMessage +
` 연간운영비(원/년): 숫자 형식 아님 (${rain.anlOperCst})`
` 연간운영비(원/년): 숫자 형식 아님 /입력값:${rain.anlOperCst}`
);
}
if (rain.oilqMeasMthd.length > 1 && !chkMngType.includes(rain.oilqMeasMthd)) {
issues.push(
defaultMessage + `유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rain.oilqMeasMthd}`
);
}
if (rain.spwtPositn.length > 1 && !chkSpwtPositnType.includes(rain.spwtPositn)) {
issues.push(
defaultMessage + `측정위치: 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rain.spwtPositn}`
);
}
rain.rainyDetails?.forEach((key) => {
if (key.ph?.length > 1 && isInvalidNumber(key.ph)) {
issues.push(defaultMessage + `PH: 숫자 형식 아님 (${key.ph})`);
issues.push(defaultMessage + `PH: 숫자 형식 아님 /입력값:${key.ph}`);
}
if (key.trbt?.length > 1 && isInvalidNumber(key.trbt)) {
issues.push(defaultMessage + `탁도: 숫자 형식 아님 (${key.trbt})`);
issues.push(defaultMessage + `탁도: 숫자 형식 아님 /입력값:${key.trbt}`);
}
if (key.grssClfg?.length > 1 && isInvalidNumber(key.grssClfg)) {
issues.push(
defaultMessage + `총대장균군수: 숫자 형식 아님 (${key.grssClfg})`
defaultMessage + `총대장균군수: 숫자 형식 아님 /입력값:${key.grssClfg}`
);
}
if (key.levyAmt?.length > 1 && isInvalidNumber(key.levyAmt)) {
issues.push(
defaultMessage +
`과태료부과금액(천원): 숫자 형식 아님 (${key.levyAmt})`
`과태료부과금액(천원): 숫자 형식 아님 /입력값:${key.levyAmt}`
);
}
if (key.vltnDt?.length > 2 && isInvalidDate(key.vltnDt)) {
issues.push(
defaultMessage +
`과태료 부과일(위반일): 날짜 형식 아님 (${key.vltnDt})`
`과태료 부과일(위반일): 날짜 형식 아님 /입력값:${key.vltnDt}`
);
}
});
@@ -188,17 +214,19 @@ export const excelToRainChkData = (excelData: any, year: string) => {
if (isInvalidNumber(guide.splyUswtrUtztnQnt)) {
issues.push(
defaultMessage +
`대체_${mm}: 숫자 형식 아님 (${guide.splyUswtrUtztnQnt})`
`대체_${mm}: 숫자 형식 아님 /입력값:${guide.splyUswtrUtztnQnt}`
);
}
if (isInvalidNumber(guide.ruseFcltUtztnQnt)) {
issues.push(
defaultMessage +
`빗물_${mm}: 숫자 형식 아님 (${guide.ruseFcltUtztnQnt})`
`빗물_${mm}: 숫자 형식 아님 /입력값:${guide.ruseFcltUtztnQnt}`
);
}
});
});
}else{
toast.error("데이터가 없습니다. 점검 데이터를 확인해주세요.");
}
if (issues.length > 0) {
@@ -1,5 +1,10 @@
import type { SelectDataType } from "@common/business_components/ui";
import { useTypes, type Buildingtype } from "../rainChkgdn/excel";
import {
chkMngType,
chkSpwtPositnType,
useTypes,
type Buildingtype,
} from "../rainChkgdn/excel";
import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import type { rswtChkZodType } from ".";
import {
@@ -171,14 +176,14 @@ export const excelToRswtGuideData = (
.filter((el: any) => el !== undefined);
if (rswtChkgdData.length > 0) {
rswtExcelDataCheck(rswtChkgdData,issues)
rswtExcelDataCheck(rswtChkgdData, issues);
}
if (issues.length > 0) {
return { data: [], issues };
}
return { data:rswtChkgdData, issues: [] };
return { data: rswtChkgdData, issues: [] };
};
export const excelToRswtPolution = (excelData: any, issues: string[]) => {
@@ -248,25 +253,25 @@ export const excelToRswtPolution = (excelData: any, issues: string[]) => {
})
.filter((el: any) => el !== undefined);
if (rswtChkgdData.length > 0) {
rswtExcelDataCheck(rswtChkgdData,issues)
rswtExcelDataCheck(rswtChkgdData, issues);
}
if (issues.length > 0) {
return { data: [], issues };
}
return { data:rswtChkgdData, issues: [] };
return { data: rswtChkgdData, issues: [] };
};
const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
const rswtExcelDataCheck = (
rswtChkgdData: rswtChkZodType[],
issues: string[]
) => {
rswtChkgdData.forEach((rswt: any) => {
const defaultMessage = `*중수도 수질 [NO: ${rswt.bdstRuseFcltSn}, 점검일: ${rswt.chckYmd}, 이용용도: ${rswt.utztnUsgCd}] 항목에서 `;
if (rswt.chckYmd && isInvalidDate(rswt.chckYmd)) {
issues.push(
defaultMessage + `점검일: 날짜 형식 아님 (${rswt.chckYmd})`
);
issues.push(defaultMessage + `점검일: 날짜 형식 아님 (${rswt.chckYmd})`);
}
if (rswt.anlOperCst.length > 2 && isInvalidNumber(rswt.anlOperCst)) {
issues.push(
@@ -279,6 +284,25 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
defaultMessage + ` 처리용량(m³): 숫자 형식 아님 (${rswt.prcsCpct})`
);
}
if (
rswt.oilqMeasMthd.length > 1 &&
!chkMngType.includes(rswt.oilqMeasMthd)
) {
issues.push(
defaultMessage +
`유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rswt.oilqMeasMthd}`
);
}
if (
rswt.spwtPositn.length > 1 &&
!chkSpwtPositnType.includes(rswt.spwtPositn)
) {
issues.push(
defaultMessage +
`측정위치: 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rswt.spwtPositn}`
);
}
if (rswt?.rswtrPollutions && rswt?.rswtrPollutions.length > 0) {
rswt.rswtrPollutions.forEach((pol) => {
if (isInvalidNumber(pol.grssClfgUswtr)) {
@@ -301,8 +325,7 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
}
if (isInvalidNumber(pol.bod)) {
issues.push(
defaultMessage +
`${pol.node}분기_BOD: 숫자 형식 아님 (${pol.bod})`
defaultMessage + `${pol.node}분기_BOD: 숫자 형식 아님 (${pol.bod})`
);
}
if (isInvalidNumber(pol.smll)) {
@@ -319,8 +342,7 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
}
if (isInvalidNumber(pol.tph)) {
issues.push(
defaultMessage +
`${pol.node}분기_T-P: 숫자 형식 아님 (${pol.tph})`
defaultMessage + `${pol.node}분기_T-P: 숫자 형식 아님 (${pol.tph})`
);
}
if (isInvalidNumber(pol.tn)) {
@@ -358,7 +380,5 @@ const rswtExcelDataCheck=(rswtChkgdData:rswtChkZodType[], issues:string[]) =>{
}
});
}
})
}
});
};
@@ -7,16 +7,17 @@ import toast from "react-hot-toast";
const pbnstPrfmncHeadList = pbnstPrfmncHeadListExport;
const monthHeadList = monthHeadListExport;
export const prfRegPageSize = 10;
export type pbnstPrfmncState = {
prfmnc: {
isInit: boolean;
pbnstPrfmncHeadList: TableUpperProps[];
monthHeadList: TableUpperProps[];
prfmncRes: FcPrfType[];
prfmncResCount:number;
prfmncResCount: number;
excelList: number[];
pageKey:number
pageKey: number;
prfPbnFclts: string[];
};
};
@@ -26,68 +27,126 @@ export const pbnstPrfmncStateExport: pbnstPrfmncState = {
pbnstPrfmncHeadList: pbnstPrfmncHeadList,
monthHeadList: monthHeadList,
prfmncRes: [],
prfmncResCount:1,
prfPbnFclts: [],
prfmncResCount: 1,
excelList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
pageKey:1
pageKey: 1,
},
};
export type pbnstPrfmncType = pbnstPrfmncState & pbnstPrfmncActions;
type pbnstPrfmncActions = {
prfmncHasuInit: (page: number, sggOrCity: string ,year:string) => void;
prfmncHasuNmSearch: (page: number, sggOrCity: string , txt:string ,year:string) => void;
prfmnRegData: (page:number,sggOrCity:string, data: PrfType,year:string) => void;
prfmncHasuInit: (
page: number,
sggOrCity: string,
year: string,
prfPbnFclts: string[]
) => void;
prfmncHasuNmSearch: (
page: number,
sggOrCity: string,
year: string,
searchTxt: string,
prfPbnFclts: string[]
) => void;
prfmnRegData: (
page: number,
sggOrCity: string,
data: PrfType,
year: string,
prfPbnFclts: string[]
) => void;
getPrfFmcRes: () => void;
};
const checkWrRegList = async (year:string) => {
const url =`/reuseFc/pubDetailList`
if(year&& year.length>0){
// url+=`/${year}`
const hasuListFetch = async (
page: number,
sggOrCity: string,
year: string,
prfPbnFclts: string[],
searchTxt?: string
) => {
let filteredFclt = prfPbnFclts;
if (sggOrCity !== "0") {
filteredFclt = prfPbnFclts?.filter((fclt: string) => {
return sggOrCity.length == 2
? fclt.slice(0, 2) === sggOrCity
: fclt.slice(0, 5) === sggOrCity;
});
}
const wrRegList = await getFetch("wr", url);
return wrRegList;
};
const hasuListFetch = async (page: number, sggOrCity: string , year:string,searchNm?:string ) => {
const checkWrRegListRes = await checkWrRegList(year);
const rainRes = await postFectch(
`/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity ? sggOrCity : "11"}/0/1/SW`,
checkWrRegListRes?.fcltCds,
fetchType.COMMON
let url = `/prcsFclt/ruseInfo/1/${filteredFclt.length + 1}/A/${sggOrCity}/0/1/SW`;
if (searchTxt && searchTxt?.length > 0) {
url += `/${searchTxt}`;
}
const rainRes = await postFectch(url, filteredFclt, fetchType.COMMON);
const checkRuseFcltList =
searchTxt && searchTxt?.length > 0
? rainRes.data.map((item) => item.bscFctCd)
: filteredFclt;
const pubRuseList = await postFectch(
`/reuseFc/pubDetailList/${year}/${sggOrCity}/${page}/${prfRegPageSize}`,
checkRuseFcltList,
fetchType.WR
);
let findData = rainRes.data.map((item: pubHasuType) => {
const data = checkWrRegListRes?.fcPrfList.find(
(fclt: FcPrfType) =>
fclt.pbnstRuseFcltId.substring(0, 11) === item?.bscFctCd
);
if(data !==undefined){
return( {
const rusePrfList = pubRuseList.data
.map((item: FcPrfType) => {
const findData = rainRes.data?.find(
(fclt: pubHasuType) =>
item.pbnstRuseFcltId.substring(0, 11) === fclt?.bscFctCd
);
if (findData !== undefined) {
return {
sido: findData.sido,
gugun: findData.gugun,
bscFctNm: findData.bscFctNm,
bscFctCd: findData.bscFctCd,
ruseUtztnFcltNm: item.ruseUtztnFcltNm,
pbnstRuseFcltId: item.pbnstRuseFcltId,
prfList: item.prfList,
};
}
})
.filter(Boolean); // undefined 값을 제거합니다.
sido: item.sido,
gugun: item.gugun,
bscFctNm: item.bscFctNm,
bscFctCd: item.bscFctCd,
ruseUtztnFcltNm: data.ruseUtztnFcltNm,
pbnstRuseFcltId: data.pbnstRuseFcltId,
prfList: data.prfList,
});
}
}).filter(Boolean); // undefined 값을 제거합니다.
if(searchNm){
findData= findData.filter((ruse:any) => ruse.ruseUtztnFcltNm.includes(searchNm))
}
return {data:findData, count:findData.length};
return { data: rusePrfList, count: pubRuseList.count };
};
export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
set: any
) => {
return {
prfmncHasuInit: async (page: number, sggOrCity: string,year:string) => {
getPrfFmcRes: async () => {
try {
const prfmncRes = await hasuListFetch(page, sggOrCity, year);
const pbnPrfRes = await getFetch(fetchType.WR, "/reuseFc/pubFc");
console.log("pbnPrfRes ", pbnPrfRes);
set((state: pbnstPrfmncState) => ({
...state,
prfmnc: {
...state.prfmnc,
prfPbnFclts: pbnPrfRes,
},
}));
} catch (error) {
console.log(error);
}
},
prfmncHasuInit: async (
page: number,
sggOrCity: string,
year: string,
prfPbnFclts: string[]
) => {
try {
console.log("prfPbnFclts ", prfPbnFclts);
const prfmncRes = await hasuListFetch(
page,
sggOrCity,
year,
prfPbnFclts
);
return set((state: pbnstPrfmncState) => ({
...state,
prfmnc: {
@@ -101,9 +160,21 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
console.log(errorHasu);
}
},
prfmncHasuNmSearch: async (page: number, sggOrCity: string,fcltNm:string ,year:string)=> {
prfmncHasuNmSearch: async (
page: number,
sggOrCity: string,
year: string,
searchTxt: string,
prfPbnFclts: string[]
) => {
try {
const prfmncRes = await hasuListFetch(page, sggOrCity,fcltNm ,year);
const prfmncRes = await hasuListFetch(
page,
sggOrCity,
year,
prfPbnFclts,
searchTxt
);
return set((state: pbnstPrfmncState) => ({
...state,
prfmnc: {
@@ -117,7 +188,13 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
console.log(errorHasu);
}
},
prfmnRegData: async (page:number,sggOrCity:string,data: PrfType,year:string) => {
prfmnRegData: async (
page: number,
sggOrCity: string,
data: PrfType,
year: string,
prfPbnFclts: string[]
) => {
try {
const ressponse = await postFectch(
"/reuseFc/pubFcPrf",
@@ -125,11 +202,14 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
fetchType.WR
);
if (ressponse.status === 200) {
toast.success("하수재이용 시설 실적 등록이 완료되었습니다.");
const prfmncRes = await hasuListFetch(page, sggOrCity,year) ;
const prfmncRes = await hasuListFetch(
page,
sggOrCity,
year,
prfPbnFclts
);
return set((state: pbnstPrfmncState) => ({
...state,
@@ -138,10 +218,9 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
prfmncRes: prfmncRes.data,
prfmncResCount: prfmncRes.count,
isInit: false,
pageKey: state.prfmnc.pageKey + 1
pageKey: state.prfmnc.pageKey + 1,
},
}));
}
} catch (e) {
console.log(e);
@@ -50,42 +50,56 @@ const hasuListFetch = async (
page: number,
returnType: ReturnType,
searchNm: string | undefined,
year: string,
fromDate: string,
toDate: string,
flcts: string[]
) => {
let url = `/prcsFclt/ruseInfo/`;
let url = '';
let filteredFclt = flcts;
const filterData = returnType.sigunValue !== '0' ? returnType.sigunValue : returnType.sidoValue
if (filterData !== '0')
filteredFclt = flcts.filter((fclt: string) => {
return filterData.length == 2 ? fclt.slice(0, 2) === filterData : fclt.slice(0, 5) === filterData
})
if (returnType.partValue === "A") {
url += `${page}/5/${returnType.partValue}/${
url += `${returnType.partValue}/${
// returnType.sidoValue ? returnType.sidoValue : returnType.sigunValue
returnType.sigunValue !== '0' ? returnType.sigunValue : returnType.sidoValue
}/0/1/SW`
if (filterData !== '0')
filteredFclt = flcts.filter((fclt: string) => {
return filterData.length == 2 ? fclt.slice(0, 2) === filterData : fclt.slice(0, 5) === filterData
})
} else if (returnType.partValue === "R") {
url += `${page}/5/${returnType.partValue}/${returnType.regionValue
url += `${returnType.partValue}/${returnType.regionValue
? returnType.regionValue
: returnType.unitWaterShedValue
? returnType.unitWaterShedValue
: returnType.middleRegionValue
}/0/1/SW`
} else if (returnType.partValue === 'G') {
url += `${page}/5/${returnType.envPartValue}/${returnType.sigunValue !== '0' ? returnType.sigunValue : returnType.sidoValue}/0/1/SW`
url += `${returnType.envPartValue}/${returnType.sigunValue !== '0' ? returnType.sigunValue : returnType.sidoValue}/0/1/SW`
if (filterData !== '0')
filteredFclt = flcts.filter((fclt: string) => {
return filterData.length == 2 ? fclt.slice(0, 2) === filterData : fclt.slice(0, 5) === filterData
})
} else {
url += `1/2/A/0/0/1/SW`
url += `A/0/0/1/SW`
}
if (searchNm && searchNm?.length > 0) {
url += "/" + searchNm
}
const pbnList = await postFectch(url, flcts, fetchType.COMMON);
const pbnList = await postFectch(`/prcsFclt/ruseInfo/1/${filteredFclt.length}/` + url, filteredFclt, fetchType.COMMON);
const checkRuseFcltList = searchNm && searchNm?.length > 0 ? pbnList.data.map((item) => item.bscFctCd) : filteredFclt
const getRuseList = await postFectch(
`/reuseFc/pubDetailList/${year}`,
flcts,
`/reuseFc/pubDetailPrfList/${fromDate}/${toDate}/${filterData}/${page}/5`,
checkRuseFcltList,
fetchType.WR
);
let count = 0;
const checkWrRegListRes = getRuseList.data;
const data: WaterReuseFacility[] = checkWrRegListRes.map(
(item: FcPrfType) => {
if (!item.prfList || item.prfList.length === 0) {
@@ -96,7 +110,6 @@ const hasuListFetch = async (
fclt.bscFctCd === item.pbnstRuseFcltId.substring(0, 11)
);
if (findData) {
count++;
return {
sido: findData?.sido || '-',
gugun: findData?.gugun || '-',
@@ -104,12 +117,12 @@ const hasuListFetch = async (
ruseUtztnFcltNm: item?.ruseUtztnFcltNm || '-',
pbnstRuseFcltId: item?.pbnstRuseFcltId || '-',
bscFctCd: findData?.bscFctCd || '-',
count
prfList: item.prfList,
}
}
}
).filter(Boolean);
return data;
return {data: data, count: getRuseList.count};
}
export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions = (
@@ -175,29 +188,17 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
page,
returnType,
fcltNm,
fromDate,
toDate,
fclts);
// const bscFctCdList = prfmncRes.map((item: any) => {
// return item.bscFctCd;
// })
const bscFctCdList = prfmncRes
.map((item: any) => item.bscFctCd)
.filter((value, index, self) => self.indexOf(value) === index);
// const bscFctCdList = [...new Set(prfmncRes.map((item: any) => item.bscFctCd))];
const res = await postFectch(
`/reuseFc/pubDetailList/${fromDate}/${toDate}`,
bscFctCdList,
`wr`,
)
let inUsageSum = 0;
let outUsageSum = 0;
let year = "";
const yearsData = [] as any
res.data.forEach((item: any) => {
if (item.prfList.length === 0) {
prfmncRes.data.forEach((item: any) => {
if (!item.prfList || item.prfList.length === 0) {
const year: string = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출.
const yearVal: { [key: string]: any } = { year: year, pbnstRuseFcltId: item.pbnstRuseFcltId ?? "", ruseUtztnFcltNm: item.ruseUtztnFcltNm ?? "" };
yearVal[`${year}inUsage`] = 0;
@@ -205,7 +206,7 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
yearVal[`${year}total`] = 0;
yearsData.push(yearVal);
}
item.prfList.forEach((acc) => {
item.prfList?.forEach((acc: any) => {
year = acc.ym?.substring(0, 4) ?? "";
inUsageSum =
(acc.wshwtr ?? 0) + //조경용수
@@ -273,7 +274,7 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
// prfmncRes 정보 merge
const resData = Object.values(mergedMap).map((item: any) => {
const match = prfmncRes.find((res) => res.pbnstRuseFcltId === item.pbnstRuseFcltId);
const match = prfmncRes.data.find((res) => res.pbnstRuseFcltId === item.pbnstRuseFcltId);
return match ? { ...item, ...match } : item;
});
@@ -282,10 +283,10 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
pbnstPrfmncSttn: {
...state.pbnstPrfmncSttn,
// prfmncRes: prfmncRes,
prfmncResCount: fclts.length - 5,
prfmncResCount: prfmncRes.count,
isInit: false,
prfmncRes: resData,
rawData: res.data
rawData: prfmncRes.data
},
}));
} catch (errorHasu) {
@@ -95,43 +95,59 @@ const hasuListFetch = async (
year: string,
flcts: string[]
) => {
let url = `/prcsFclt/ruseInfo/`;
let url = ``;
let filteredFclt = flcts;
const filterData = reurnValue.sigunValue !== '0' ? reurnValue.sigunValue : reurnValue.sidoValue
if(filterData !== '0')
filteredFclt= flcts.filter((fclt: string) => {
return filterData.length==2 ?fclt.slice(0, 2) === filterData:fclt.slice(0, 5) === filterData
})
if (reurnValue.partValue === "A") {
url += `${page}/${total}/${reurnValue.partValue}/${
url += `${reurnValue.partValue}/${
// reurnValue.sidoValue ? reurnValue.sidoValue : reurnValue.sigunValue
reurnValue.sigunValue !== '0' ? reurnValue.sigunValue : reurnValue.sidoValue
}/0/1/SW`
if(filterData !== '0')
filteredFclt= flcts.filter((fclt: string) => {
return filterData.length==2 ?fclt.slice(0, 2) === filterData:fclt.slice(0, 5) === filterData
})
} else if (reurnValue.partValue === "R") {
url += `${page}/${total}/${reurnValue.partValue}/${reurnValue.regionValue
url += `${reurnValue.partValue}/${reurnValue.regionValue
? reurnValue.regionValue
: reurnValue.unitWaterShedValue
? reurnValue.unitWaterShedValue
: reurnValue.middleRegionValue
}/0/1/SW`
} else if (reurnValue.partValue === 'G') {
url += `${page}/${total}/${reurnValue.envPartValue}/${reurnValue.sigunValue !== '0' ? reurnValue.sigunValue : reurnValue.sidoValue}/0/1/SW`
url += `${reurnValue.envPartValue}/${reurnValue.sigunValue !== '0' ? reurnValue.sigunValue : reurnValue.sidoValue}/0/1/SW`
if(filterData !== '0')
filteredFclt= flcts.filter((fclt: string) => {
return filterData.length==2 ?fclt.slice(0, 2) === filterData:fclt.slice(0, 5) === filterData
})
} else {
url += `1/2/A/0/0/1/SW`
url += `A/0/0/1/SW`
}
if (searchNm && searchNm?.length > 0) {
url += "/" + searchNm
}
const pbnList = await postFectch(url, flcts, fetchType.COMMON);
const pbnList = await postFectch( `/prcsFclt/ruseInfo/1/${filteredFclt.length}/`+url, filteredFclt, fetchType.COMMON);
const checkRuseFcltList = searchNm && searchNm?.length > 0?pbnList.data.map((item)=> item.bscFctCd ):filteredFclt
const getRuseList = await postFectch(
`/reuseFc/pubDetailList/${year}`,
flcts,
`/reuseFc/pubDetailList/${year}/${filterData}/${page}/${total}`,
checkRuseFcltList,
fetchType.WR
);
const checkWrRegListRes = getRuseList.data;
const data: WaterReuseFacility[] = checkWrRegListRes.map(
(item: FcPrfType) => {
const findData = pbnList.data.find(
@@ -140,7 +156,6 @@ const hasuListFetch = async (
);
if (findData) {
const usage = item.prfList.reduce(
(acc, cur) => {
acc.inUsage +=
@@ -197,7 +212,11 @@ const hasuListFetch = async (
}
).filter(Boolean);
return data;
return {data:data, count:getRuseList.count};
};
export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
@@ -237,8 +256,7 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
fclts: string[]
) => {
try {
const pbnstSttnRes = await hasuListFetch(
page,
total,
@@ -250,7 +268,7 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
// if (Number(total) > 9000) {
if (isExcel) {
const pbnstExcel = excelPbnstSttnParsing(pbnstSttnRes);
const pbnstExcel = excelPbnstSttnParsing(pbnstSttnRes.data);
set((state: pbnstSttnState) => {
return {
...state,
@@ -267,8 +285,8 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
...state,
pbnstSttn: {
...state.pbnstSttn,
pbnstSttnRes: pbnstSttnRes,
pbnstSttnResCount: pbnstSttnRes.length
pbnstSttnRes: pbnstSttnRes.data,
pbnstSttnResCount: pbnstSttnRes.count
// isInit: false,
},
};
@@ -295,13 +313,14 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
fclts
);
set((state: pbnstSttnState) => {
return {
...state,
pbnstSttn: {
...state.pbnstSttn,
pbnstSttnRes: pbnstSttnRes,
pbnstSttnResCount: pbnstSttnRes.length,
pbnstSttnRes: pbnstSttnRes.data,
pbnstSttnResCount: pbnstSttnRes.count,
isInit: false,
pbnstDetail: {} as customPbnst
},
@@ -365,8 +384,8 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
...state,
pbnstSttn: {
...state.pbnstSttn,
pbnstSearchData: pbnstSttnRes,
pbnstSttnResCount: pbnstSttnRes.length,
pbnstSearchData: pbnstSttnRes.data,
pbnstSttnResCount: pbnstSttnRes.count,
},
};
});
@@ -488,16 +488,6 @@ export function PbnstBuseForm({
/>
</div>
{/* <MngRegFormDetail
formList={ruseWaterQulityList}
h1Title={`하수처리수 재이용시설 보증수질 `}
subTitle="※재처리시설이 있는 경우만 작성"
form={form}
moClass="grid grid-cols-12 gap-4"
sonClass="col-span-4"
indexMng={indexMng}
indexBiz={indexBiz}
/> */}
<div>
<BuseWaterQulity
@@ -17,7 +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)
const response = await fetch( PROXY_HOST+ ENDPOINT.USER_SERVICE.LOGIN, {
method: "POST",
body: JSON.stringify({
@@ -19,7 +19,6 @@ type MonthPickerProps = {
readOnly?: boolean;
startYear?: number; // 시작 연도 props 추가
yearReadOnly?: boolean;
enableMonth?: string[];
};
// yyyyMM → Date 객체
@@ -42,7 +41,6 @@ export const DateMonthPicker = ({
readOnly = false,
startYear,
yearReadOnly = false,
enableMonth,
}: MonthPickerProps) => {
const [open, setOpen] = useState(false);
@@ -83,7 +81,6 @@ export const DateMonthPicker = ({
onMonthChange={handleSelect}
startYear={startYear}
yearReadOnly={yearReadOnly}
enableMonth={enableMonth}
/>
</PopoverContent>
</Popover>
@@ -30,10 +30,9 @@ export type MonthPickerProps = {
) => void;
startYear?: number;
yearReadOnly?: boolean;
enableMonth?: string[];
};
export function MonthPicker({ currentMonth, onMonthChange, startYear, yearReadOnly, enableMonth }: MonthPickerProps) {
export function MonthPicker({ currentMonth, onMonthChange, startYear, yearReadOnly }: MonthPickerProps) {
const today = startOfToday();
const currentYear = today.getFullYear();
@@ -62,9 +61,9 @@ const selectedYear = parseInt(yearFromProp);
"inline-flex h-9 w-16 items-center justify-center rounded-md p-0 text-sm font-normal ring-offset-white transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 aria-selected:opacity-100 dark:ring-offset-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:focus-visible:ring-slate-800",
isEqual(month, currentMonth) &&
"bg-slate-900 text-slate-50 hover:bg-slate-900 hover:text-slate-50 focus:bg-slate-900 focus:text-slate-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50 dark:hover:text-slate-900 dark:focus:bg-slate-50 dark:focus:text-slate-900",
// !isEqual(month, currentMonth) &&
// isEqual(month, getStartOfCurrentMonth()) &&
// "bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
!isEqual(month, currentMonth) &&
isEqual(month, getStartOfCurrentMonth()) &&
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
);
return (
@@ -114,7 +113,7 @@ const selectedYear = parseInt(yearFromProp);
<button
name="month"
className={getMonthButtonClass(month)}
disabled={isFuture(month) || (enableMonth && !enableMonth?.includes(format(month, 'MMM', { locale: ko })))}
disabled={isFuture(month)}
role="gridcell"
tabIndex={-1}
type="button"
@@ -128,101 +128,7 @@ function Gis({
// Feature Click Event
const handleFeatureSelect = useCallback((data) => {
console.log(data);
// gisMap = mapInstanceRef.current;
// gisMap = mapInstanceRef.current;
// if (!gisMap) {
// console.warn('지도가 초기화되지 않았습니다.');
// return;
// }
// if (!gisMap) {
// console.warn('지도가 초기화되지 않았습니다.');
// return;
// }
// // 항상 같은 select 객체 사용
// const select = GisApp.Module.select;
// if (!select) return;
// // 항상 같은 select 객체 사용
// const select = GisApp.Module.select;
// if (!select) return;
// // 1. 기존 선택 초기화
// select.resetSelectedFeature();
// // 2. data가 OpenLayers Feature 인스턴스인지 확인
// let foundFeature = null;
// if (data && typeof data.getGeometry === 'function' && typeof data.setStyle === 'function') {
// // 이미 Feature 인스턴스라면 그대로 사용
// foundFeature = data;
// } else {
// // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용
// let featureId = data.fid;
// const layerName = layerNmRef.current
// if (layerName === '하수재이용시설') {
// featureId = data.searchPk;
// } else if (layerName === '빗물이용시설' || layerName == '중수도시설') {
// featureId = data.bdstInfoSn;
// } else if (layerName === '하수처리시설') {
// featureId = data.bscFctCd;
// } else if (layerName === '온배수시설') {
// featureId = data.bdstRuseFcltSn;
// } else if(layerName == '수요처'){
// featureId = data.dmdInfoId.replaceAll('-','')
// }
// else {
// console.warn('해당 레이어에 대한 featureId 매핑 없음', layerNm);
// featureId = data.fid || data.id || data.ftrIdn;
// }
// // gisMap.getLayers().getArray().forEach(layer => {
// // const source = layer.getSource?.();
// // if (source?.getFeatureById) {
// // const feature = source.getFeatureById(String(featureId).trim());
// // if (feature) foundFeature = feature;
// // }
// // });
// // const [layerType, id] = String(featureId).split('.');
// gisMap.getLayers().getArray().forEach(layer => {
// const source = layer.getSource?.();
// if (source?.getFeatures) {
// const allFeatures = source.getFeatures();
// for (const f of allFeatures) {
// const id = String(f.getId());
// // console.log('id', id, featureId)
// if (id && id.split('.')[1] === String(featureId)) {
// foundFeature = f;
// break;
// }
// }
// }
// });
// }
// // 3. 피처 선택
// if (foundFeature) {
// let geometry = foundFeature.getGeometry();
// if (geometry) {
// let extent = geometry.getExtent();
// gisMap.getView().fit(extent, {
// duration: 300,
// padding: [50, 50, 50, 50],
// maxZoom: 16,
// constrainResolution: true,
// nearest: true,
// });
// }
// select.selectFeature(foundFeature, true);
// } else {
// console.warn('OpenLayers Feature를 찾을 수 없습니다:', data);
// }
// setFeatureData(data);
// setIsOpen(true);
// setIsSearchModalOpen(false);
onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출
console.log(data)
}, [mapInstanceRef]);
// 지도 생성 및 초기화
@@ -467,6 +373,7 @@ function Gis({
setVisible={setTabVisible}
onCheckedChange={handleCheckedLayersChange}
onLayerIconClick={(layerNm) => {
console.log(layerNm)
layerNmRef.current = layerNm
setLayerNm(layerNm); setIsOpen(true); const dummyFeature = {
getProperties: () => ({}), // 최소 구조
@@ -189,7 +189,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
};
const handleLayerImgClick = (nodeNm, nodeId, layerNm) => {
if(!checked.includes(nodeId)) alert('시설을 선택해주세요')
else onLayerIconClick(layerNm)
else onLayerIconClick(nodeNm)
}
if (!treeData || treeData.length === 0) return null;
@@ -130,8 +130,6 @@ GisOlLayer.prototype = {
strategy: all,
});
return source;
return source;
},
/**
* [사용]WFS 레이어 생성
@@ -149,7 +147,6 @@ GisOlLayer.prototype = {
|| lyrTyNm == 'tb_ms_sbmrg_dngr_map'
|| lyrTyNm == 'tb_ms_swage_mhl'
|| lyrTyNm == 'tb_ms_swage_ppl'
|| lyrTyNm == 'tb_ms_swage_pmstn'
) {
source = this.getVectorSourceBbox(option);
}else {
@@ -78,10 +78,6 @@ export function UiTable({
}
}, [tableData, headlist]);
useEffect(() => {
setIdx(index);
}, [index])
const handlePageChange = (page: number) => {
setCur(page);
onPageChange(page);
@@ -124,10 +120,10 @@ export function UiTable({
(boldArray.includes(index) ? (
<TableCell
className={`${head.align === "right" ? "text-right" : head.align === "center" ? "text-center" : "text-center"} ${cellClick && idx === index
? "cursor-pointer !font-[1000]"
: cellClick
? "cursor-pointer"
: ""
? "cursor-pointer !font-[1000]"
: cellClick
? "cursor-pointer"
: ""
} sticky top-20 z-40 bg-secondary !font-bold`}
style={{ top: `${headerHeight + index * 32 - 4}px` }}
onClick={() => handleCellClick(index, head.id)}
@@ -155,10 +151,10 @@ export function UiTable({
) : (
<TableCell
className={`${head.align === "right" ? "text-right" : head.align === "center" ? "text-center" : "text-center"} ${cellClick && idx === index
? "cursor-pointer !font-[1000]"
: cellClick
? "cursor-pointer"
: ""
? "cursor-pointer !font-[1000]"
: cellClick
? "cursor-pointer"
: ""
} mt-[${boldArray.length * 40}px]`}
onClick={() => handleCellClick(index, head.id)}
key={`cell-${head.id}-${index}`}