Merge commit '58ec7ed0028615d14df864deed820a63267ddf26' into iipbs/wr/gisPopup

This commit is contained in:
JIeunhuh
2025-07-14 13:18:46 +09:00
66 changed files with 11948 additions and 1352 deletions
+18 -3
View File
@@ -3,23 +3,38 @@ import { BaseLayout } from "@common/components";
import "./globals.css";
import { StoreProvider } from "./store";
import { MainDiv } from "@common/business_components";
import koBase from '../public/locales/ko/message.json';
import i18next from "i18next";
import { I18nextProvider } from "react-i18next";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
i18next.init({
lng: 'ko',
fallbackLng: 'ko',
resources: {
ko: {
base: koBase,
},
}
});
return (
<html lang="en">
<body>
<I18nextProvider i18n={i18next}>
<StoreProvider>
<BaseLayout>
<MainDiv>
<MainDiv>
{children}
</MainDiv>
</MainDiv>
</BaseLayout>
</StoreProvider>
</I18nextProvider>
</body>
</html>
</html >
);
}
+10 -21
View File
@@ -4,23 +4,21 @@ import { useWrStore } from "@/app/store";
import {
rainZod,
selectClass,
TEST_UUID,
type customRainBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { DatePicker, FormUi } from "@common/business_components";
import { Form, FormField, SelectBox } from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
import { useForm, useWatch, type UseFormReturn } from "react-hook-form";
import { z } from "zod";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import { Label } from "@common/business_components/ui/form/lib/label";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import { DatePicker, FormUi } from "@common/business_components";
import { Form, FormField, SelectBox } from "@common/business_components/ui";
import { Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
export default function RainPrfmncSttnPopup({
rainForm,
@@ -29,7 +27,7 @@ export default function RainPrfmncSttnPopup({
rainForm: customRainBdst;
isModal: boolean;
}) {
const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore(
const { code, rainBdstReg, rainBdstUpdate } = useWrStore(
(state) => state
);
const { rainFcltCd } = code;
@@ -81,15 +79,6 @@ export default function RainPrfmncSttnPopup({
}
}, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]);
// form 값이 변경될 때마다 로그 출력
useEffect(() => {
const subscription = form.watch((value) => {
});
return () => subscription.unsubscribe();
}, [form]);
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
+15 -16
View File
@@ -1,5 +1,17 @@
"use client";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
rswtZod,
type customRswtBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
DatePicker,
Form,
@@ -7,24 +19,11 @@ import {
FormUi,
SelectBox,
} from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
rswtZod,
type customRswtBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import { useEffect } from "react";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import { Label } from "@common/business_components/ui/form/lib/label";
export default function RswtrPrfmncsSttnPopup({
rswtForm,
@@ -33,7 +32,7 @@ export default function RswtrPrfmncsSttnPopup({
rswtForm: customRswtBdst;
isModal: boolean;
}) {
const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore(
const { code, rswtBdstUpdate, rswtBdstReg } = useWrStore(
(state) => state
);
+10 -13
View File
@@ -1,13 +1,13 @@
"use client";
import { useWrStore } from "@/app/store";
import { usageProcData } from "@/app/store/matDmd/coMatRvw/headList";
import {
DmdCoReqSchema,
type DedMatUpdateType,
type DmdCoReqType,
type DmdMatTableType,
} from "@/app/store/matDmd/coMatRvw/type";
import { formH1Class, formTableName } from "@/components/lib/class/comClass";
import { formH1Class } from "@/components/lib/class/comClass";
import {
formatPhoneNumber,
getCity,
@@ -15,33 +15,30 @@ import {
tableData,
yearOptions,
} from "@/components/lib/func/commom";
import ConCostForm from "@/components/ui/form/coMat/conCostForm/conCostForm";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import {
ContentsDiv,
Form,
FormField,
FormUi,
ScrollArea,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
UiTable,
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Input, Label } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import moment from "moment";
import { useEffect, useRef, useState } from "react";
import { useForm, useWatch } from "react-hook-form";
import toast, { Toaster } from "react-hot-toast";
import { z } from "zod";
import moment from "moment";
import { usageProcData } from "@/app/store/matDmd/coMatRvw/headList";
import { zodResolver } from "@hookform/resolvers/zod";
import ConCostForm from "@/components/ui/form/coMat/conCostForm/conCostForm";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
export default function CoMatRvw() {
const { code, coRvw, coMatRvwInit, coMatRvwSearch, coMatRvwInqua } =
@@ -119,7 +116,7 @@ export default function CoMatRvw() {
useEffect(() => {
if (coMatRvwRes&&coMatRvwRes.length > 0) {
const data = coMatRvwRes?.map((item, index) => {
const data = coMatRvwRes?.map((item) => {
let flag = "";
flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ||undefined);
@@ -238,7 +235,7 @@ export default function CoMatRvw() {
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
onClick={(e) => {
onClick={() => {
if (fromDate && toDate) {
const fromNum = Number(fromDate);
const toNum = Number(toDate);
@@ -277,12 +274,12 @@ export default function CoMatRvw() {
headlist={coRvwHeadList}
pageSize={20}
total={coMatRvwCount}
cellClick={(index: number | undefined, headId: string) => {
cellClick={(index: number | undefined) => {
if (index !== undefined && index >= 0) {
TableCellClick(index);
}
}}
onPageChange={() => {}}
onPageChange={(val) => {setPageNatiion(val)}}
></UiTable>
</ListDiv>
<hr />
@@ -5,7 +5,6 @@ import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList";
import { excelDownload, getCity } from "@/components/lib/func/commom";
import RuseCaseMngExcel from "@/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import {
ContentsDiv,
filePickedBlobArray,
@@ -22,7 +21,7 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { useEffect, useState } from "react";
import { toast, Toaster } from "react-hot-toast";
import { Toaster } from "react-hot-toast";
export default function RuseCaseMng() {
const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state);
@@ -37,7 +36,7 @@ export default function RuseCaseMng() {
// 물재이용사례 조회
const searchRuseCaseMng = async (pageNo: number, pageSize: number) => {
const res = await selectRuseCaseMng(
await selectRuseCaseMng(
pageNo,
pageSize,
sidoCd ?? "",
@@ -237,11 +236,10 @@ export default function RuseCaseMng() {
<UiTable
headName={"물재이용사례 현황"}
tableData={ruseCaseMngList}
children={undefined}
headlist={ruseCaseMng.ruseHeadList}
pageSize={20}
total={count}
cellClick={(index: number | undefined, headId: string) => {}}
cellClick={() => {}}
onPageChange={(pageNo: number) => {
searchRuseCaseMng(pageNo, 10);
}}
+33 -51
View File
@@ -10,9 +10,9 @@ import {
formatPhoneNumber,
getCity,
getdmdMatFlag,
tableData,
yearOptions,
} from "@/components/lib/func/commom";
import ConCostForm from "@/components/ui/form/coMat/conCostForm/conCostForm";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import {
ContentsDiv,
@@ -26,15 +26,12 @@ import {
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, DatePicker, Input, Label } from "@common/components/ui";
import { Button, Label } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import moment from "moment";
import { useCallback, useEffect, useState } from "react";
import { set, useFieldArray, useForm } from "react-hook-form";
import { z } from "zod";
import { tableData } from "@/components/lib/func/commom";
import { useForm } from "react-hook-form";
import toast, { Toaster } from "react-hot-toast";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
export default function RuseRvwHstry() {
const {
code,
@@ -44,7 +41,7 @@ export default function RuseRvwHstry() {
ruseRvwInqua,
ruseRvwComplete,
} = useWrStore((state) => state);
const { rvwHistRes, ruseHistHeadList,rvwHistCount } = ruseHist;
const { rvwHistRes, ruseHistHeadList, rvwHistCount } = ruseHist;
const [pageNation, setPageNatiion] = useState<number>(1);
const [dmdCoNm, setDmdCoNm] = useState<string>("");
@@ -70,18 +67,16 @@ export default function RuseRvwHstry() {
setObjSearch({ city: cityValue, sgg: "0" });
};
useEffect(() =>{
getButtonSearch()
},[pageNation])
useEffect(() => {
getButtonSearch();
}, [pageNation]);
const getButtonSearch=()=>{
const getButtonSearch = () => {
if (fromDate && toDate) {
const fromNum = Number(fromDate);
const toNum = Number(toDate);
if (fromNum > toNum) {
toast.error(
"시작년도가 종료년도 이후로 설정할 수 없습니다."
);
toast.error("시작년도가 종료년도 이후로 설정할 수 없습니다.");
} else {
let con: string | number = "";
if (String(objSearch.city).length > 0) {
@@ -90,16 +85,10 @@ export default function RuseRvwHstry() {
if (String(objSearch.sgg).length > 0 && objSearch.sgg !== "0") {
con = String(objSearch.sgg);
}
getSearch(
fromNum,
toNum,
con,
dmdCoNm,
ruseUtztnFcltNm
);
getSearch(fromNum, toNum, con, dmdCoNm, ruseUtztnFcltNm);
}
}
}
};
const getSgg = (val: string | number) => {
console.log(val);
setObjSearch({ ...objSearch, sgg: String(val) });
@@ -123,8 +112,6 @@ export default function RuseRvwHstry() {
ruseUtztnFcltNm
);
} else {
ruseRvwInqua(pageNation, 13, String(startYear), String(endYear), sggCd);
}
};
@@ -191,33 +178,33 @@ export default function RuseRvwHstry() {
useEffect(() => {
if (rvwHistRes) {
const data = rvwHistRes?.map((item, index) => {
const data = rvwHistRes?.map((item) => {
let flag = "";
flag = getdmdMatFlag(item.demandMatching?.splyFcltFlg ?? "");
const formatDate = (dateStr: string | null | undefined) => {
// null, undefined, 빈 문자열인 경우 '없음'을 반환합니다.
if (!dateStr) return "없음";
// "YYYYMMDD" 형식으로 날짜를 파싱합니다.
const d = moment(dateStr, "YYYYMMDD");
// 파싱된 날짜가 유효한 경우에만 포맷하고, 그렇지 않으면 '없음'을 반환합니다.
return d.isValid() ? d.format("YYYY-MM-DD") : "없음";
};
flag = getdmdMatFlag(item.demandMatching?.splyFcltFlg ?? "");
const formatDate = (dateStr: string | null | undefined) => {
// null, undefined, 빈 문자열인 경우 '없음'을 반환합니다.
if (!dateStr) return "없음";
// "YYYYMMDD" 형식으로 날짜를 파싱합니다.
const d = moment(dateStr, "YYYYMMDD");
// 파싱된 날짜가 유효한 경우에만 포맷하고, 그렇지 않으면 '없음'을 반환합니다.
return d.isValid() ? d.format("YYYY-MM-DD") : "없음";
};
return {
...item,
ctpvCd: item.demandCo?.ctpvCd,
sggCd: item.demandCo?.sggCd,
dmdCoNm: item.demandCo?.dmdCoNm,
daddr: item.demandCo?.daddr,
telno: formatPhoneNumber(item.demandCo?.telno ||''),
telno: formatPhoneNumber(item.demandCo?.telno || ""),
dmndTmng: item?.dmndTmng
? moment(item?.dmndTmng).format("YYYY-MM-DD")
: "없음",
rvwCmptnYmd: formatDate(item.demandMatching?.rvwCmptnYmd),
mtchnCmptnYmd: formatDate(item.demandMatching?.mtchnCmptnYmd),
ruseUtztnFcltNm: item.ruseUtztnFcltNm,
rvwCmptnYmd: formatDate(item.demandMatching?.rvwCmptnYmd),
mtchnCmptnYmd: formatDate(item.demandMatching?.mtchnCmptnYmd),
ruseUtztnFcltNm: item.ruseUtztnFcltNm,
dmndQty: item.dmndQty,
splyFcltFlg: flag,
completeYn: false,
@@ -228,7 +215,6 @@ export default function RuseRvwHstry() {
}
}, [rvwHistRes]);
useEffect(() => {
console.log("변경여부 ", form.formState.isDirty);
}, [form.formState]);
@@ -308,8 +294,8 @@ export default function RuseRvwHstry() {
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
onClick={(e) => {
getButtonSearch()
onClick={() => {
getButtonSearch();
}}
>
조회
@@ -333,16 +319,12 @@ export default function RuseRvwHstry() {
headlist={ruseHistHeadList}
pageSize={20}
total={rvwHistCount}
cellClick={(index: number | undefined, headId: string) => {
cellClick={(index: number | undefined) => {
TableCellClick(index);
}}
onPageChange={
(page)=>{
setPageNatiion(page)
}
}
onPageChange={(page) => {
setPageNatiion(page);
}}
></UiTable>
{/* <hr />
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">
+81 -61
View File
@@ -8,10 +8,9 @@ import {
excelDownload,
formatPhoneNumber,
getCity,
tableData
tableData,
} from "@/components/lib/func/commom";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import {
ContentsDiv,
SearchDiv,
@@ -60,18 +59,21 @@ export default function CoSttn() {
const [seartchTxt, setSearchTxt] = useState<string>();
const [upadateData, setUpdateData] = useState<DemandCompType>();
const getSelCity = useCallback((val: string | number) => {
const getSelCity = useCallback(
(val: string | number) => {
const cityValue = String(val);
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
// 시/도를 변경하면 시/군/구는 '전체'("0")로 초기화합니다.
setObjSearch({ city: cityValue, sgg: "0" });
}, [code.sggCd]);
},
[code.sggCd]
);
const getSgg = useCallback((val: string | number) => {
setObjSearch((prev) => ({ ...prev, sgg: String(val) }));
setPageNation(1)
setPageNation(1);
}, []);
useEffect(() => {
@@ -84,7 +86,7 @@ export default function CoSttn() {
}
}
}, [code.cityCd, objSearch.city, getSelCity]);
useEffect(() => {
if (!coSttn.isInit) {
coSttnInit("1", "13", "0");
@@ -92,15 +94,13 @@ export default function CoSttn() {
}, [coSttn.isInit, coSttnInit]);
useEffect(() => {
if(coSttnRes){
if (coSttnRes) {
if (coSttnRes?.length > 0 && code.cityCd.length > 0) {
const data = tableData(coSttnRes, { code });
const chData = data.map((item: any) => ({
...item,
telno:formatPhoneNumber(item.telno||undefined),
regDt: moment(item.regDt).format("YYYY-MM-DD")
telno: formatPhoneNumber(item.telno || undefined),
regDt: moment(item.regDt).format("YYYY-MM-DD"),
}));
// .filter((item: any) => item !== undefined);
setBdstTable(chData);
@@ -111,11 +111,14 @@ export default function CoSttn() {
}, [coSttnRes, code]);
useEffect(() => {
if (coSttn.excelList.length > 0) {
if (coSttn.excelList.length === 0) return;
const coExcel = excelCoSttnParsing(coSttn.excelList, code.cityCd, code.sggCd);
const coExcel = excelCoSttnParsing(
coSttn.excelList,
code.cityCd,
code.sggCd
);
excelDownload(
coExcel as [],
"수요처 관리",
@@ -127,25 +130,29 @@ export default function CoSttn() {
}, [coSttn.excelList, code]);
const excelClick = useCallback(() => {
const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city;
const con =
objSearch.sgg && objSearch.sgg !== "0" ? objSearch.sgg : objSearch.city;
if (con) coSttnSearch("1", "10000", String(con));
}, [coSttnSearch, objSearch.city, objSearch.sgg]);
// }, [excelDownload, bdstTable]);
}, [coSttnSearch, objSearch.city, objSearch.sgg]);
// }, [excelDownload, bdstTable]);
const searchClick = () => {
const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city;
const con =
objSearch.sgg && objSearch.sgg !== "0" ? objSearch.sgg : objSearch.city;
if (con) {
if (seartchTxt && seartchTxt?.length > 0) {
coSttnTxtSearch(String(pageNation), "13", String(con), seartchTxt ?? "");
coSttnTxtSearch(
String(pageNation),
"13",
String(con),
seartchTxt ?? ""
);
} else {
coSttnSearch(String(pageNation), "13", String(con));
}
}
};
return (
<>
<ContentsDiv>
@@ -160,10 +167,10 @@ export default function CoSttn() {
label="시도"
selectClass={""}
selectArray={code.cityCd || []}
onSelectValue={(val)=>{
getSelCity(val)
setPageNation(1)
} }
onSelectValue={(val) => {
getSelCity(val);
setPageNation(1);
}}
value={objSearch.city}
/>
<SelectBox
@@ -205,11 +212,12 @@ export default function CoSttn() {
onClick={() => {
/* 저장 로직 */
if (!upadateData) {
toast.error("수요처 변경을 원하는 행을 선택해주세요")
}else{
toast.error("수요처 변경을 원하는 행을 선택해주세요");
} else {
setIsDmdModal({
...isDmdModal,
isMoal: true})
isMoal: true,
});
}
}}
>
@@ -235,29 +243,29 @@ export default function CoSttn() {
tableData={bdstTable || []}
headlist={coSttn.coSttHeadList}
pageSize={20}
total={ coSttnResCount+10 || 0 }
cellClick={(index: number | undefined, headId: string) => {
total={coSttnResCount + 10 || 0}
cellClick={(index: number | undefined) => {
setUpdateData(coSttnRes[index as number]);
}}
onPageChange={(page) =>{
const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city;
setPageNation(page)
coSttnSearch( String(page), "13", String(con));
} }
onPageChange={(page) => {
const con =
objSearch.sgg && objSearch.sgg !== "0"
? objSearch.sgg
: objSearch.city;
setPageNation(page);
coSttnSearch(String(page), "13", String(con));
}}
></UiTable>
</ListDiv>
</div>
</div>
<UiModal
bodyClass={"w-[40vw] max-w-none h-[80vh] "// +upadateData?.dmdInfoPk?'h-[60vh]':'h-[90vh]'
bodyClass={
"w-[40vw] max-w-none h-[80vh] " // +upadateData?.dmdInfoPk?'h-[60vh]':'h-[90vh]'
}
open={isDmdModal.isMoal}
onOpenChange={() => {
setIsDmdModal({ isMoal: false, data: {} as DemandCompType });
setIsDmdModal({ isMoal: false, data: {} as DemandCompType });
}}
body={
<SdmInsertForm
@@ -267,26 +275,38 @@ export default function CoSttn() {
rtnValue={(searchText: string, pageCnt: number) => {
getDemandCompList(searchText, pageCnt);
}}
rtnInputData={ async(data) => {
const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city;
console.log('data ', data)
if(upadateData?.dmdInfoPk){
console.log('data ', data)
await updateDemandCompany(data as DemandCompType)
rtnInputData={async (data) => {
const con =
objSearch.sgg && objSearch.sgg !== "0"
? objSearch.sgg
: objSearch.city;
console.log("data ", data);
if (upadateData?.dmdInfoPk) {
console.log("data ", data);
await updateDemandCompany(data as DemandCompType);
coSttnInit("1", "13", String(con));
setIsDmdModal({ isMoal: false, data: data as DemandCompType });
}else{
const dmd= await getFetch(fetchType.WR,`/reuseFc/request/${data.dmdInfoPk}`)
if(dmd) {
toast.error(`사업자번호 ${dmd.dmdInfoPk}는 이미 등록되었습니다. 다시등록해주세요`)
return
}
await regDemandCompany(data as DemandCompType);
coSttnInit("1", "13", String(con));
setIsDmdModal({ isMoal: false, data: data as DemandCompType });
setIsDmdModal({
isMoal: false,
data: data as DemandCompType,
});
} else {
const dmd = await getFetch(
fetchType.WR,
`/reuseFc/request/${data.dmdInfoPk}`
);
if (dmd) {
toast.error(
`사업자번호 ${dmd.dmdInfoPk}는 이미 등록되었습니다. 다시등록해주세요`
);
return;
}
await regDemandCompany(data as DemandCompType);
coSttnInit("1", "13", String(con));
setIsDmdModal({
isMoal: false,
data: data as DemandCompType,
});
}
}}
bodyClassNm={"w-full mx-auto bg-white rounded-lg shadow-md p-6"}
updateList={upadateData}
+2 -2
View File
@@ -27,8 +27,8 @@ export default function DrgtSttn() {
data: customDrgt | undefined;
}>({ data: undefined, isOpen: false });
const cellClick = (index: number | undefined, headId: string) => {
if (fromDate && toDate) {
const cellClick = (index: number) => {
if (fromDate && toDate && drgtRes[index]) {
drgtModalSearch(fromDate, toDate, drgtRes[index].sggNm);
} else {
toast.error("조회기간을 선택해주세요.");
+24 -9
View File
@@ -5,6 +5,7 @@ import { btnClass } from "@/app/store/common/code";
import { type customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn";
import { ldrmntHeadListExport } from "@/app/store/matDmd/ldrmntCoSttn/headList";
import {
confirmToast,
excelDownload,
getCity,
tableData
@@ -29,9 +30,10 @@ import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function LdrmntCoSttn() {
const { code, ldrmnt, ldrmntInit, ldrmntSearch, } = useWrStore(
const { code, ldrmnt, ldrmntInit, ldrmntSearch, ldrmntUpdate } = useWrStore(
(state) => state
);
const { ldrmntRes, excelList, ldrmntResCount } = ldrmnt;
@@ -42,6 +44,8 @@ export default function LdrmntCoSttn() {
const [bdstTable, setBdstTable] = useState<customLdrmnt[]>();
const [searchTxt, setSearchTxt] = useState<string>();
const [excelData, setExcelData] = useState<any[]>();
const [resetPage, isResetPage] = useState(false);
const { t } = useTranslation('base');
// const [isExcelDownload, setIsExcelDownload] = useState(false);
const excelClick = useCallback(() => {
@@ -76,9 +80,9 @@ export default function LdrmntCoSttn() {
const excelDownloadClick = useCallback(() => {
const cityNm = computeCityNm();
ldrmntSearch(true, pageNation, String(ldrmntResCount), cityNm, searchTxt || "");
ldrmntSearch(true, 1, String(ldrmntResCount), cityNm, searchTxt || "");
// setIsExcelDownload(true);
}, [objSearch, pageNation, searchTxt, code, ldrmntSearch, computeCityNm, ldrmntResCount]);
}, [objSearch, searchTxt, code, ldrmntSearch, computeCityNm, ldrmntResCount]);
useEffect(() => {
if (/*isExcelDownload &&*/ excelData && excelData.length > 0) {
@@ -111,7 +115,6 @@ export default function LdrmntCoSttn() {
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
@@ -160,8 +163,8 @@ export default function LdrmntCoSttn() {
}, [excelList])
useEffect(() => {
const cityNm = computeCityNm();
ldrmntSearch(false, pageNation, '20', cityNm, searchTxt || "");
ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
isResetPage(false)
}, [pageNation]);
return (
@@ -204,8 +207,8 @@ export default function LdrmntCoSttn() {
<Button
size={"sm"}
onClick={(e) => {
const cityNm = computeCityNm();
ldrmntSearch(false, pageNation, '20', cityNm, searchTxt || "");
ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
isResetPage(true)
}}
>
조회
@@ -250,6 +253,7 @@ export default function LdrmntCoSttn() {
total={ldrmntResCount || 0}
cellClick={(index: number | undefined, headId: string) => { cellClick(index ?? undefined, headId) }}
onPageChange={setPageNation}
resetPageNo={resetPage}
/>
</ListDiv>
</div>
@@ -260,7 +264,18 @@ export default function LdrmntCoSttn() {
setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen })
}}
bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"}
body={<CoDetailModal formObj={detailModal.data} />}
body={<CoDetailModal formObj={detailModal.data} onFormSubmit={async (value) => {
// confirmToast(t('isUpdateMsg'), await ldrmntUpdate(value))
// // await ldrmntUpdate(value)
// setDetailModal({ ...detailModal, isOpen: false })
// ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
const handleConfirm = async () => {
await ldrmntUpdate(value);
setDetailModal({ ...detailModal, isOpen: false });
ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
};
confirmToast(t('isUpdateMsg'), handleConfirm);
}} />}
/>
<UiModal
@@ -24,7 +24,6 @@ import type {
import {
pbnstRegZod,
pbnstRegArrayZod,
fcBizSchema,
} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm";
import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm";
@@ -36,8 +35,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createDefaultPbnstItem } from "./utilsZod";
import { flushSync } from "react-dom";
export default function PublicReuseFaciltyInsert() {
const {
@@ -55,7 +53,7 @@ export default function PublicReuseFaciltyInsert() {
const [isRuseReg, setIsRuseReg] = useState<{
isModal: boolean;
}>({ isModal: false });
const [resetPage, isResetPage] = useState(false);
const [indexMng, setIndexMng] = useState<number>(0);
const [indexBiz, setIndexBiz] = useState<number>(0);
const [searchTxt, setSearchTxt] = useState<string>();
@@ -89,15 +87,16 @@ export default function PublicReuseFaciltyInsert() {
}, [pbnst.isInit, pubHasuInit]);
const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => {
if (!isDirty) {
toast.error("변경사항이 없습니다."); // 변경사항이 없으면 메시지 표시
return;
} const data = writeRgtrAndMdfr(valuse.items);
}
const data = writeRgtrAndMdfr(valuse.items);
let bizs = [] as fcBizType[];
data.forEach((fclt) => {
bizs = [...bizs, ...fclt.fcBizs];
});
const pbnst = data.map((fclt) => {
return {
pbnstRuseFcltPk: fclt.pbnstRuseFcltPk,
@@ -111,42 +110,34 @@ export default function PublicReuseFaciltyInsert() {
};
});
Promise.allSettled([
pbnstRuseFcltUpdate(pbnst as customPbnst[]),
pbnstRuseBizUpdate(bizs)
pbnstRuseBizUpdate(bizs),
]).then(([resFclt, resBiz]) => {
const isFcltSuccess = resFclt.status === "fulfilled";
const isBizSuccess = resBiz.status === "fulfilled";
if (isFcltSuccess && isBizSuccess) {
toast.success('하수 재이용 시설 및 사업이 수정되었습니다. ')
toast.success("하수 재이용 시설 및 사업이 수정되었습니다. ");
// 메시지 표시, 페이지 이동 등
} else {
let message = ''
let message = "";
// ✅ 각각 어떤 항목이 실패했는지도 로그/알림 처리
if (!isFcltSuccess) {
message = '하수재이용 시설 '
message = "하수재이용 시설 ";
}
if (!isBizSuccess) {
message += '하수재이용 시설 사업 '
message += "하수재이용 시설 사업 ";
}
toast.error(message + '오류 입니다 ')
toast.error(message + "오류 입니다 ");
pubHasuInit("1", "41");
}
});
};
// 폼 유효성 검사 실패 시 호출될 함수
const onInvalid = (errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>) => {
console.log("errors:", errors);
const onInvalid = (
errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>
) => {
const findAllErrors = (
errorObject: any,
path: (string | number)[] = []
@@ -180,7 +171,9 @@ export default function PublicReuseFaciltyInsert() {
if (itemsIndex > -1 && path.length > itemsIndex + 1) {
const itemIdx = path[itemsIndex + 1] as number;
const facilityName = form.getValues(`items.${itemIdx}.ruseUtztnFcltNm`);
const facilityName = form.getValues(
`items.${itemIdx}.ruseUtztnFcltNm`
);
if (facilityName) {
prefix += `[${facilityName}] `;
}
@@ -188,13 +181,14 @@ export default function PublicReuseFaciltyInsert() {
const fcBizsIndex = path.indexOf("fcBizs");
if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) {
const bizIdx = path[fcBizsIndex + 1] as number;
const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`);
const cycle = form.getValues(
`items.${itemIdx}.fcBizs.${bizIdx}.cycl`
);
if (cycle) {
prefix += `[${cycle}차수] `;
}
}
}
// 최종 오류 메시지 출력
toast.error(`${prefix}${message}`);
});
@@ -204,6 +198,10 @@ export default function PublicReuseFaciltyInsert() {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
console.log("formState", items);
}, [items]);
useEffect(() => {
if (code.sggCd.length && pbnstHasuRes.length > 0) {
setbdstTable(tableData(pbnstHasuRes, { code }));
@@ -225,6 +223,12 @@ export default function PublicReuseFaciltyInsert() {
setCurrentTab(value);
};
useEffect(() => {
if (indexMng) {
setIndexBiz(0);
}
}, [indexMng]);
useEffect(() => {
if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) {
form.reset({ items: pbnst.pbnstRes });
@@ -233,66 +237,25 @@ export default function PublicReuseFaciltyInsert() {
form.reset({ items: [] });
}
}, [pbnst.pbnstRes]);
useEffect(() => {
if (indexMng) {
setIndexBiz(0);
}
}, [indexMng]);
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
const appendPbnstRuseFclt = useCallback(() => {
const selecTableRow = selectRow;
if (pbnstRes && pbnstRes.length > 0) {
const lastPbnst = pbnstRes[pbnstRes.length - 1];
if (lastPbnst && selecTableRow) {
// append(
// pbnstRegZod.parse({
// pbnstRuseFcltPk:
// String(selecTableRow.bscFctCd) + String(pbnstRes.length + 1),
// ruseUtztnFcltNm: null,
// fcBizs: [
// fcBizSchema.parse({
// pbnstRuseFcltFk:
// String(lastPbnst.pbnstRuseFcltPk) +
// String(pbnstRes.length + 1),
// cycl: 1,
// }),
// ] as fcBizType[],
// })
// );
append(createDefaultPbnstItem(selecTableRow, pbnstRes.length));
if (selectRow) { // pbnstRes.length를 사용하여 새로운 항목의 인덱스를 결정
append(createDefaultPbnstItem(selectRow, pbnstRes.length));
setIsRuseReg({ isModal: true });
}
} else {
if (selecTableRow) {
append(
pbnstRegZod.parse({
pbnstRuseFcltPk: String(selecTableRow.bscFctCd) + "1",
ruseUtztnFcltNm: "",
regDt: null,
mdfcnDt: null,
fcBizs: [
fcBizSchema.parse({
pbnstRuseFcltFk: String(selecTableRow.bscFctCd) + "1",
cycl: 1,
}),
] as fcBizType[],
})
);
if (selectRow) { // pbnstRes가 비어있으면 첫 항목이므로 인덱스 0
append(createDefaultPbnstItem(selectRow, 0));
setIsRuseReg({ isModal: true });
}
}
}, [pbnstRegZod, pbnstRes, append, selectRow, form]);
}, [pbnstRes, append, selectRow]);
useEffect(() => {
pubHasuInit(String(pageNation), "41");
isResetPage(false);
}, [pageNation, pubHasuInit]);
return (
@@ -319,6 +282,7 @@ export default function PublicReuseFaciltyInsert() {
searchTxt && searchTxt?.length > 0
? pubHasuSearch(String(pageNation), "41", searchTxt)
: pubHasuInit(String(pageNation), "41");
isResetPage(true);
}}
>
조회
@@ -361,6 +325,7 @@ export default function PublicReuseFaciltyInsert() {
cellClick={cellClick}
onPageChange={setPageNation}
children={undefined}
resetPageNo={resetPage}
/>
</ScrollArea>
</ListDiv>
@@ -389,7 +354,6 @@ export default function PublicReuseFaciltyInsert() {
formType="update"
form={form}
setIndexMng={(index: number) => {
console.log("setIndexMng", index);
setIndexMng(index);
}}
indexMng={indexMng}
@@ -406,7 +370,6 @@ export default function PublicReuseFaciltyInsert() {
indexBiz={indexBiz}
/>
</TabsContent>
</ScrollArea>
</Tabs>
) : (
@@ -437,7 +400,6 @@ export default function PublicReuseFaciltyInsert() {
</Button>
</div>
</>
)}
</ListDiv>
<UiModal
@@ -450,7 +412,6 @@ export default function PublicReuseFaciltyInsert() {
setIsRuseReg({ isModal: false });
}
}}
onInvalid={onInvalid}
></PbnstRegModal>
}
@@ -1,28 +1,37 @@
import type { fcBizType, pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import type { fcBizType, operType, pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
export function createDefaultPbnstItem(
selecTableRow: pubHasuType,
index: number
) {
const pk = String(selecTableRow.bscFctCd) + String(index + 1);
export function createDefaultPbnstItem(row: pubHasuType, index: number) {
const pk = row.bscFctCd + String(index + 1);
return JSON.parse(JSON.stringify({
pbnstRuseFcltPk: pk,
swtrmtFcltFk: "",
ruseUtztnFcltNm: "",
fcltCpct: 0,
ruseCpct: 0,
splyExtraCpct: 0,
regDt: null,
rgtr: "",
mdfcnDt: null,
mdfr: "",
fcFiles: [],
fcBizs: [createDefaultBiz(pk, 1)],
fcOpers: [], // 빈 배열 강제
}));
}
export function createDefaultOper(pbnstRuseFcltFk: string): operType {
return {
pbnstRuseFcltPk: pk,
swtrmtFcltFk: "",
ruseUtztnFcltNm: "", // 필수 입력이므로 빈 문자열로
fcltCpct: 0,
ruseCpct: 0,
splyExtraCpct: 0,
regDt: null,
rgtr: "test",
mdfcnDt: null,
mdfr: "test",
fcFiles: [],
fcBizs: [
createDefaultBiz(pk, 1), // 첫 차수
],
fcOpers: [],
};
pbnstRuseFcltFk,
operMthd: "",
rprtvBzenty: "",
bgngDt: new Date(),
endDt: new Date(),
choose: false,
rgtr: "",
mdfr: "",
} as operType;
}
@@ -45,7 +45,7 @@ export default function Home() {
useEffect(() => {
if (code.sggCd.length > 0) {
if( rswtRes?.length > 0){
if (rswtRes?.length > 0) {
const rswtData = tableData(rswtRes, { code })?.map(
(item: customRswtBdst) => {
let archArea =
@@ -62,12 +62,20 @@ export default function Home() {
);
setbdstTable(rswtData);
setRswtForm(rswtRes[selectVal ?? 0]);
}else{
} else {
setbdstTable([]);
setRswtForm({} as customRswtBdst);
}
}
}, [code, rswtRes, selectVal]);
// }, [code, rswtRes, selectVal]);
}, [code, rswtRes]);
useEffect(() => {
if (rswtRes && rswtRes.length > 0) {
setRswtForm(rswtRes[selectVal ?? 0]);
}
}, [rswtRes, selectVal]);
const cellClick: (val: number | undefined, headId: string) => void = (
val,
headId
@@ -75,7 +83,7 @@ export default function Home() {
if ((bdstTable ?? []).length > 0 && val !== undefined) {
{
setSelect(val);
setRswtForm(rswtRes[val]);
// setRswtForm(rswtRes[val]);
}
}
@@ -34,6 +34,8 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { excelHtdflSttnParsing } from "@/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel";
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function HtdflSttn() {
const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore(
@@ -42,7 +44,8 @@ export default function HtdflSttn() {
const { htdflSttnRes, htdflSttnResCount } = htdflSttn;
const ref = useRef<ReturnType>({} as ReturnType);
const isDownloadTriggered = useRef(false);
const { t } = useTranslation('base');
const [pageNation, setPageNation] = useState(1);
const [isChHead, setIsChHead] = useState(false);
const [resetPage, isResetPage] = useState(false);
@@ -59,12 +62,6 @@ export default function HtdflSttn() {
}>({ isOpen: false, chBuilding: [] });
const [fcltNm, setFcltNm] = useState<string>("");
const excelClick = useCallback(() => {
// 최신 검색 조건 반영
const con = getConValue();
htdflSttnSearch(true, 1, String(htdflSttnResCount), String(con), fcltNm, code.cityCd, code.sggCd);
}, [htdflSttnSearch, objSearch, code.cityCd, code.sggCd, fcltNm, htdflSttnResCount]);
useEffect(() => {
htdflSttnSearch(false, pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd);
isResetPage(false)
@@ -108,16 +105,27 @@ export default function HtdflSttn() {
setDetailModal({ isOpen: true, data: bdstTable[index] });
}
};
const excelClick = useCallback(() => {
isDownloadTriggered.current = true;
htdflSttnSearch(true, 1, String(htdflSttnResCount), String(getConValue()), fcltNm, code.cityCd, code.sggCd);
}, [htdflSttnSearch, objSearch, code.cityCd, code.sggCd, fcltNm, htdflSttnResCount]);
useEffect(() => {
if (!isDownloadTriggered.current) {
return;
}
if (htdflSttn.excelList.length > 0) {
if (htdflSttn.excelList.length === 0) return;
excelDownload(
htdflSttn.excelList,
"온배수시설 현황",
"sample_htdflSttn.xlsx",
"A1:R3"
);
} else {
toast.error(t('excelDownErrMsg'));
}
isDownloadTriggered.current = false;
}, [htdflSttn.excelList, code]);
useEffect(() => {
@@ -256,6 +264,7 @@ export default function HtdflSttn() {
body={<SttnDetailModal type={"HTB"} formObj={detailModal.data} />}
/>
</ContentsDiv>
<Toaster position="top-center" />
</>
);
}
@@ -34,6 +34,8 @@ import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeCo
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RainSttn() {
const { code, rainSttn, rainSttnSearch, rainSttnInit } = useWrStore(
@@ -47,6 +49,7 @@ export default function RainSttn() {
const [bdstTable, setBdstTable] = useState<customRainBdst[]>();
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const [resetPage, isResetPage] = useState(false);
const isDownloadTriggered = useRef(false);
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false })
const [bdstChgProps, setBdstChgProps] = useState<{
@@ -54,9 +57,8 @@ export default function RainSttn() {
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const [fcltNm, setFcltNm] = useState<string>("");
const { t } = useTranslation('base');
const cellClick = (index: number | undefined, headId: string) => {
if (headId !== 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
setDetailModal({ isOpen: true, data: bdstTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
@@ -68,14 +70,12 @@ export default function RainSttn() {
const getSelCity = (val: string | number) => {
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
@@ -130,7 +130,7 @@ export default function RainSttn() {
})
.filter((item: any) => item.rainyObj !== undefined);
setBdstTable(chData);
}, [code, rainSttnRes, rainSttn.rainSttHeadList]);
}, [code, rainSttnRes]);
useEffect(() => {
rainSttnSearch(false, pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd);
@@ -138,21 +138,25 @@ export default function RainSttn() {
}, [pageNation]);
useEffect(() => {
if (!isDownloadTriggered.current) {
return;
}
if (rainSttn.excelList.length > 0) {
if (rainSttn.excelList.length === 0) return;
excelDownload(
rainSttn.excelList,
"빗물재이용시설 현황",
"sample_rainSttn.xlsx",
"A1:W3"
);
} else {
toast.error(t('excelDownErrMsg'));
}
isDownloadTriggered.current = false;
}, [rainSttn.excelList]);
const excelClick = useCallback(() => {
// 최신 검색 조건 반영
const con = getConValue();
rainSttnSearch(true, 1, String(rainSttnResCount), String(con), fcltNm, code.cityCd, code.sggCd);
isDownloadTriggered.current = true;
rainSttnSearch(true, 1, String(rainSttnResCount), String(getConValue()), fcltNm, code.cityCd, code.sggCd);
}, [rainSttnSearch, objSearch, code.cityCd, code.sggCd, fcltNm, rainSttnResCount]);
return (
@@ -279,6 +283,7 @@ export default function RainSttn() {
}}
/>
</ContentsDiv>
<Toaster position="top-center" />
</>
);
}
@@ -31,6 +31,8 @@ import ListDiv from "@common/business_components/ui/list_div";
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RswtSttn() {
const { code, rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore(
@@ -43,6 +45,7 @@ export default function RswtSttn() {
const [resetPage, isResetPage] = useState(false);
const ref = useRef<ReturnType>({} as ReturnType);
const [bdstTable, setBdstTable] = useState<customRswtBdst[]>();
const isDownloadTriggered = useRef(false);
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false })
@@ -51,7 +54,7 @@ export default function RswtSttn() {
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const [fcltNm, setFcltNm] = useState<string>("");
const { t } = useTranslation('base');
const cellClick = (index: number | undefined, headId: string) => {
if (headId !== 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
@@ -124,22 +127,25 @@ export default function RswtSttn() {
}, [code, rswtSttnRes]);
useEffect(() => {
if (!isDownloadTriggered.current) {
return;
}
if (rswtSttn.excelList.length > 0) {
if (rswtSttn.excelList.length === 0) return;
excelDownload(
rswtSttn.excelList,
"중수도시설 현황",
"sample_rswtSttn.xlsx",
"A1:W3",
);
} else {
toast.error(t('excelDownErrMsg'));
}
isDownloadTriggered.current = false;
}, [rswtSttn.excelList]);
const excelClick = useCallback(() => {
// 최신 검색 조건 반영
const con = getConValue();
rswtSttnSearch(true, 1, String(rswtSttnResCount), String(con), fcltNm, code.cityCd, code.sggCd);
isDownloadTriggered.current = true;
rswtSttnSearch(true, 1, String(rswtSttnResCount), String(getConValue()), fcltNm, code.cityCd, code.sggCd);
}, [rswtSttnSearch, objSearch, code.cityCd, code.sggCd, fcltNm, rswtSttnResCount]);
useEffect(() => {
@@ -276,6 +282,7 @@ export default function RswtSttn() {
}}
/>
</ContentsDiv>
<Toaster position="top-center" />
</>
);
}
@@ -11,6 +11,7 @@ import {
type rswtrChkDetailsZodType,
} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn";
import {
confirmToast,
excelArrayDownload,
excelDownload,
yearOptions,
@@ -39,6 +40,7 @@ import {
} from "@common/components/ui/tooltip";
import { useCallback, useEffect, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function Page() {
const {
@@ -201,12 +203,19 @@ export default function Page() {
}
}, [rainChkgdn.isSttn, rswtChkgdn.isSttn]);
const { t } = useTranslation('base');
const clickDelete = (data: number[], selFcltDiv: string) => {
if (data.length > 0) {
if (selFcltDiv === "RNB") {
rainChkDelete(data, searchYear);
const handleConfirm = async () => {
rainChkDelete(data, searchYear);
};
confirmToast(t('isDeleteMsg'), handleConfirm);
} else {
rswtChkDelete(data, searchYear);
const handleConfirm = async () => {
rswtChkDelete(data, searchYear);
};
confirmToast(t('isDeleteMsg'), handleConfirm);
}
}
};
@@ -1,7 +1,7 @@
"use client";
import { useWrStore } from "@/app/store";
import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import { excelDownload, yearOptions } from "@/components/lib/func/commom";
import { confirmToast, excelDownload, yearOptions } from "@/components/lib/func/commom";
import PbnPrfExcelModal from "@/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal";
import {
ContentsDiv,
@@ -37,12 +37,13 @@ import {
type PrfType,
} from "@/app/store/prfmncReg/pbnstPrfmncReg/type";
import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel";
import { useTranslation } from "react-i18next";
export default function prfmncPrfmncReg() {
const { code, prfmnc, prfmncHasuInit, prfmnRegData,prfmncHasuNmSearch } = useWrStore(
const { code, prfmnc, prfmncHasuInit, prfmnRegData, prfmncHasuNmSearch } = useWrStore(
(state) => state
);
const { pbnstPrfmncHeadList, monthHeadList, prfmncRes,prfmncResCount } = prfmnc;
const { pbnstPrfmncHeadList, monthHeadList, prfmncRes, prfmncResCount } = prfmnc;
const [bdstTable, setbdstTable] = useState<FcPrfType[]>();
const [searchTxt, setSearchTxt] = useState<string>();
const [isExcelOpen, setIsExcelOPen] = useState<{
@@ -65,20 +66,40 @@ export default function prfmncPrfmncReg() {
"A1:O1"
);
}, [prfmnc.excelList]);
/*
const onSave = useCallback(
async() => {
if (pbnstRuseFcltPk) {
await prfmnRegData(pageNation,'41',
excelToPbnPrfData(
inputData as unknown as JSON[],
searchYear,
pbnstRuseFcltPk
) as unknown as PrfType
);
// cellClick(cellNum)
}
}, [inputData, searchYear, pbnstRuseFcltPk, pageNation,cellNum]);*/
const onSave = useCallback(
async() => {
if (pbnstRuseFcltPk) {
await prfmnRegData(pageNation,'41',
const { t } = useTranslation('base');
const onSave = useCallback(() => {
const handleConfirm = async () => {
if (!pbnstRuseFcltPk) return;
prfmnRegData(
pageNation,
'41',
excelToPbnPrfData(
inputData as unknown as JSON[],
searchYear,
pbnstRuseFcltPk
) as unknown as PrfType
,searchYear
);
// cellClick(cellNum)
}
}, [inputData, searchYear, pbnstRuseFcltPk, pageNation,cellNum]);
};
confirmToast(t('isUpdateMsg'), handleConfirm);
}, [inputData, searchYear, pbnstRuseFcltPk, pageNation, cellNum]);
const cellClick = useCallback(
(index: number | undefined) => {
@@ -165,13 +186,13 @@ export default function prfmncPrfmncReg() {
useEffect(() => {
if (!prfmnc.isInit) {
const year = new Date().getFullYear()
prfmncHasuInit(1, "41",String(year));
prfmncHasuInit(1, "41", String(year));
}
}, [prfmnc.isInit, prfmncHasuInit]);
useEffect(() => {
if(prfmncRes&&prfmncRes.length>0){
if (prfmncRes && prfmncRes.length > 0) {
setbdstTable(prfmncRes);
}
@@ -224,14 +245,14 @@ export default function prfmncPrfmncReg() {
<div className="flex flex-row space-x-2">
<Button size={"sm"} onClick={(e) => {
if(searchTxt&&searchTxt?.length>0){
prfmncHasuNmSearch(1,'41',searchYear,searchTxt)
if (searchTxt && searchTxt?.length > 0) {
prfmncHasuNmSearch(1, '41', searchYear, searchTxt)
setPageNation(1)
}else{
prfmncHasuInit(1,'41',searchYear)
} else {
prfmncHasuInit(1, '41', searchYear)
setPageNation(1)
}
}}>
조회
</Button>
@@ -282,8 +303,8 @@ export default function prfmncPrfmncReg() {
setCellNum(index ?? 0);
}}
onPageChange={
(page) =>{
prfmncHasuInit(page, "41",searchYear);
(page) => {
prfmncHasuInit(page, "41", searchYear);
setPageNation(page)
}}
children={undefined}
@@ -298,12 +319,11 @@ export default function prfmncPrfmncReg() {
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
<Table variant="secondary">
<TableHeader className="font-bold">
{headMakeprfmnc(monthHeadList, [])}
</TableHeader>
<TableBody key={prfmnc.pageKey+'pageKey'}>
<TableBody key={prfmnc.pageKey + 'pageKey'}>
{InputTableBody(inputData ?? [], monthHeadList, setInputData)}
</TableBody>
</Table>
@@ -319,14 +339,14 @@ export default function prfmncPrfmncReg() {
setIsExcelOPen={(open) => {
setIsExcelOPen({ ...isExcelOpen, isModal: open })
}}
setModalExcelData={ async(excelData ) =>{
await prfmnRegData(pageNation,'41',
setModalExcelData={async (excelData) => {
await prfmnRegData(pageNation, '41',
excelData,
searchYear
);
// 데이터 저장 후 UI 업데이트는 useEffect가 감지하여 처리하므로 여기서는 호출하지 않습니다.
}
}
}
></PbnPrfExcelModal>
}
open={isExcelOpen.isModal}
@@ -23,7 +23,8 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function Page() {
const {
@@ -97,7 +98,6 @@ export default function Page() {
useEffect(() => {
if (!rainChkSttn.isInit) {
isRainSttn(true)
console.log("여기", rainPageSize)
rainChkSttnInit(1, rainPageSize, new Date().getFullYear().toString(), commonCd);
setIsModal({ data: {}, open: false });
}
@@ -119,8 +119,13 @@ export default function Page() {
},
[isModal]
);
const { t } = useTranslation('base');
const excelDown = () => {
if (rainChkSttn.sttnExcelList && selFcltDiv === "RNB") {
if (!rainChkSttn.sttnExcelList || rainChkSttn.sttnExcelList.length === 0) {
toast.error(t('excelDownErrMsg'));
return;
}
excelDownload(
rainChkSttn.sttnExcelList,
"빗물이용시설 지도점검 현황",
@@ -129,6 +134,10 @@ export default function Page() {
3
);
} else if (rswtChkSttn.sttnExcelList && selFcltDiv === "RST") {
if (!rswtChkSttn.sttnExcelList || rswtChkSttn.sttnExcelList.length === 0) {
toast.error(t('excelDownErrMsg'));
return;
}
excelDownload(
rswtChkSttn.sttnExcelList,
"중수도시설 지도점검 현황",
@@ -11,6 +11,7 @@ import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function PbnstPrfmncSttn() {
const {
@@ -33,12 +34,16 @@ export default function PbnstPrfmncSttn() {
const ref = useRef<ReturnType>({} as ReturnType);
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>();
const [pbnRuseNm, setPbnRuseNm] = useState<string>("");
const { t } = useTranslation('base');
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [excelData, setExcelData] = useState<any[]>([]);
const excelClick = useCallback(() => {
if (!excelData || excelData.length === 0) {
toast.error(t('excelDownErrMsg'));
return;
}
excelDownload(
excelData,
"하수재이용실적 현황",
@@ -84,12 +89,12 @@ export default function PbnstPrfmncSttn() {
const prfTableData = (prfList: PrfType[], year: string) => {
const inUsageData = structuredClone(initValInUsage).map(row => ({
...row,
구분: "장내용수",
...row
// 구분: "장내용수",
}));
const outUsageData = structuredClone(initValOutUsage).map(row => ({
...row,
구분: "장외용수",
...row
// 구분: "장외용수",
}));
const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" };
const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" };
@@ -524,14 +529,7 @@ export default function PbnstPrfmncSttn() {
selectUnitWaterShedData={code.lgbsnCd}
selectMiddleRegionData={code.mdmbsnCd}
selectEnvPartData={code.lclgvrnmntCd}
rightChildren={<Button
className={btnClass}
onClick={() => {
setIsChHead(true);
}}
>
조회항목설정
</Button>}
rightChildren={undefined}
leftChildren={<div className="col-span-5">
<div className="flex flex-row">
<div className="flex flex-row items-center">
+14 -14
View File
@@ -3,21 +3,21 @@ import { useWrStore } from "@/app/store";
import { yearOptions } from "@/components/lib/func/commom";
import { ContentsDiv, Title, SearchDiv, SearchFormLeft, SelectBox, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { Button } from "@common/components/ui";
import { useState } from "react";
export default function MatStat(){
const { code, matStat } = useWrStore((state) => {
export default function MatStat() {
const { matStat } = useWrStore((state) => {
return state;
});
const [pageNation, setPageNation] = useState(1);
// const [pageNation, setPageNation] = useState(1);
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
return(
<>
<ContentsDiv>
return (
<>
<ContentsDiv>
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<Title title={"재이용 통계"} />
@@ -34,13 +34,13 @@ const { code, matStat } = useWrStore((state) => {
}
/>
<div className="flex flex-row space-x-2 items-center">
<Button className="" size={"sm"} onClick={(e) => {}}>
<Button className="" size={"sm"} onClick={() => { }}>
조회
</Button>
<Button className="" size={"sm"} onClick={(e) => {}}>
<Button className="" size={"sm"} onClick={() => { }}>
다운로드
</Button>
<Button className="" size={"sm"} onClick={(e) => {}}>
<Button className="" size={"sm"} onClick={() => { }}>
전체통계다운로드
</Button>
</div>
@@ -61,14 +61,14 @@ const { code, matStat } = useWrStore((state) => {
pageSize={20}
total={10000}
headName={"건축물 목록"}
cellClick={(val, headId) => {}}
onPageChange={setPageNation}
cellClick={() => { }}
onPageChange={() => { }}
></UiTable>
{/* </ScrollArea> */}
</ListDiv>
</div>
</div>
</ContentsDiv>
</>
)
</>
)
}
+2 -2
View File
@@ -29,7 +29,7 @@ export default function RainStat() {
useEffect(() => {
searchRainStatList(searchYear);
}, [code.cityCd, code.sggCd, searchRainStatList, searchYear])
}, [code.cityCd, code.sggCd, searchRainStatList])
const excelDetailDownLoad=() =>{
const sidoList = new Set<string>();
@@ -237,7 +237,7 @@ export default function RainStat() {
/>
</div>
<div className="flex flex-row space-x-2 items-center">
<Button className="" size={"sm"} onClick={() => { }}>
<Button className="" size={"sm"} onClick={()=>{searchRainStatList(searchYear);}}>
조회
</Button>
<Button className="" size={"sm"} onClick={() => excelClick()}>
+1 -1
View File
@@ -26,7 +26,7 @@ export default function RswtStat() {
useEffect(() => {
searchRswtStatList(searchYear)
}, [searchRswtStatList, searchYear])
}, [searchRswtStatList])
const excelClick = () => {
downloadExcel({
+1
View File
@@ -22,6 +22,7 @@ export type TableUpperProps = {
selectData ?:string; // type 이 seelct 일경우 selectarray 정하기 위해
row?:number;
mergeCell?:boolean // 셀을 합칠경우
maxLength?:number
readonly?:boolean
};
+10 -12
View File
@@ -4,6 +4,7 @@ import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import { z } from "zod";
import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg";
import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList";
import toast from "react-hot-toast";
// 테이블 헤드 리스트
const ldrmntHeadList = ldrmntHeadListExport;
@@ -34,7 +35,7 @@ const excelDownList: ExcelType[] = [];
type ldrmntActions = {
ldrmntRegData: (data: any[]) => void,
ldrmntInit: (con: string) => void
ldrmntSearch: (isExcel:boolean, page: number, sggCd: string, total: string, coNm: string) => void;
ldrmntSearch: (isExcel: boolean, page: number, sggCd: string, total: string, coNm: string) => void;
ldrmntUpdate: (ldrmntRes: customLdrmnt) => void;
ldrmntDelete: () => void;
loadingOpen: (isOpen: boolean) => void
@@ -106,7 +107,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
console.log('response ', response)
},
ldrmntSearch: async (isExcel:boolean, page: number, total: string, sggCd: string, coNm: string) => {
ldrmntSearch: async (isExcel: boolean, page: number, total: string, sggCd: string, coNm: string) => {
let searchUrl = "";
if (sggCd !== "") {
@@ -121,7 +122,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
fetchType.WR,
searchUrl
);
console.log("알이에스", ldrmntRes)
console.log("알이에스", ldrmntRes)
if (isExcel) {
let excelDownList: ExcelType[] = [];
ldrmntRes.data.forEach((item: customLdrmnt) => {
@@ -163,23 +164,20 @@ console.log("알이에스", ldrmntRes)
}
},
ldrmntUpdate: async (ldrmntRes: customLdrmnt) => {
if(ldrmntRes)
try {
ldrmntRes.rgtr = TEST_UUID;
const response = await putFectch(
const res = await putFectch(
`/reuseFc/request/temp`,
ldrmntRes
);
return set((state: ldrmntState) => ({
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes,
},
}));
if (res.ok) {
toast.success("잠재수요처 상세정보가 수정되었습니다.",)
}
} catch (error) {
console.error("Error ldrmntUpdate:", error);
toast.error("잠재수요처 상세정보 수정에 실패하였습니다.")
}
},
ldrmntDelete: async () => {
@@ -42,9 +42,7 @@ type pbnstPrfmncActions = {
};
const checkWrRegList = async (year:string) => {
let url =`/reuseFc/pubDetailList`
if(year&& year.length>0){
// url+=`/${year}`
}
@@ -58,8 +56,6 @@ const hasuListFetch = async (page: number, sggOrCity: string , year:string,searc
checkWrRegListRes?.fcltCds,
fetchType.COMMON
);
let findData = rainRes.data.map((item: pubHasuType) => {
const data = checkWrRegListRes?.fcPrfList.find(
(fclt: FcPrfType) =>
@@ -2,6 +2,20 @@ import { z } from "zod";
import type { TableUpperProps } from "../../common/type/header";
import { fcOperSchema } from "../../ruseFcltMng/fcltReg/pbnstReg/type";
type Month = `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12}월`;
export type PrfUsageSummary = {
구분: string;
usage: string;
용수명?: string;
total: number;
edit?:boolean
} & Record<Month, number>;
export const PrfSchema = z.object({
pbnstRuseFcltFk: z.string(),
ym: z.string(), // 예: "202310"
@@ -44,8 +58,9 @@ export const FcPrfSchema = z.object({
export type FcPrfType = z.infer<typeof FcPrfSchema>;
export type PrfType = z.infer<typeof PrfSchema>;
export const initValInUsage = [
export const initValInUsage:PrfUsageSummary[] = [
{
구분: "장내용수",
usage: "wshwtr",
용수명: "세척수",
total: 0,
@@ -63,6 +78,7 @@ export const initValInUsage = [
"12월": 0,
},
{
구분: "장내용수",
usage: "coolUswtr",
용수명: "냉각수",
total: 0,
@@ -80,6 +96,7 @@ export const initValInUsage = [
"12월": 0,
},
{
구분: "장내용수",
usage: "cleanUswtr",
용수명: "청소수",
total: 0,
@@ -97,6 +114,7 @@ export const initValInUsage = [
"12월": 0,
},
{
구분: "장내용수",
usage: "drwtUswtr",
용수명: "식수대",
total: 0,
@@ -114,6 +132,7 @@ export const initValInUsage = [
"12월": 0,
},
{
구분: "장내용수",
usage: "dltnUswtr",
용수명: "희석용수",
total: 0,
@@ -131,6 +150,7 @@ export const initValInUsage = [
"12월": 0,
},
{
구분: "장내용수",
usage: "ithlRswtr",
용수명: "중수도",
total: 0,
@@ -148,6 +168,7 @@ export const initValInUsage = [
"12월": 0,
},
{
구분: "장내용수",
usage: "ithlEtcUswtr",
용수명: "기타",
total: 0,
@@ -166,8 +187,9 @@ export const initValInUsage = [
},
];
export const initValOutUsage = [
export const initValOutUsage: PrfUsageSummary[] = [
{
구분: "장외용수",
usage: "resn",
용수명: "세척살수용수",
total: 0,
@@ -185,6 +207,7 @@ export const initValOutUsage = [
"12월": 0,
},
{
구분: "장외용수",
usage: "cleanToilUswtr",
용수명: "청소화장실용수",
total: 0,
@@ -202,6 +225,7 @@ export const initValOutUsage = [
"12월": 0,
},
{
구분: "장외용수",
usage: "lndwtr",
용수명: "조경용수",
total: 0,
@@ -219,6 +243,7 @@ export const initValOutUsage = [
"12월": 0,
},
{
구분: "장외용수",
usage: "hydUswtr",
용수명: "친수용수",
total: 0,
@@ -236,6 +261,7 @@ export const initValOutUsage = [
"12월": 0,
},
{
구분: "장외용수",
usage: "ugwtChr",
용수명: "지하수충전",
total: 0,
@@ -253,6 +279,7 @@ export const initValOutUsage = [
"12월": 0,
},
{
구분: "장외용수",
usage: "farmUswtr",
용수명: "농업용수",
total: 0,
@@ -271,6 +298,7 @@ export const initValOutUsage = [
},
{
구분: "장외용수",
usage: "rvrKeepUswtr",
용수명: "하천등유지용수",
total: 0,
@@ -289,6 +317,7 @@ export const initValOutUsage = [
},
{
구분: "장외용수",
usage: "indstUswtr",
용수명: "공업용수",
total: 0,
@@ -307,7 +336,7 @@ export const initValOutUsage = [
},
];
export const subTotalObj = {
export const subTotalObj:PrfUsageSummary = {
usage: "subtotal",
용수명: "소계",
edit: false,
@@ -326,7 +355,7 @@ export const subTotalObj = {
"12월": 0,
};
export const totalObj = {
export const totalObj:PrfUsageSummary = {
usage: "total",
용수명: "총합계",
edit: false,
@@ -1,5 +1,4 @@
import { z } from "zod";
import type { TableUpperProps } from "../../ruseFcltMng/fcltReg/rainReg";
import type { TableUpperProps } from "../../common/type/header";
export const pbnstPrfmncSttnHeadListExport: TableUpperProps[] = [
{
@@ -37,6 +36,7 @@ export const pbnstPrfmncSttnHeadListExport: TableUpperProps[] = [
export const pbnstYearHeadListExport: TableUpperProps[] = [
{
id: "year",
title: "년도",
colSpan: 3,
row: 0
@@ -57,3 +57,25 @@ export const pbnstYearHeadListExport: TableUpperProps[] = [
row: 1
},
]
export const monthSttnHeadListExport: TableUpperProps[] = [
{ id: "구분", title: "구분", mergeCell: true },
{ id: "용수명", title: "용수명" },
{ id: "total", title: "계" },
{ id: "1월", title: "1월" },
{ id: "2월", title: "2월" },
{ id: "3월", title: "3월" },
{ id: "4월", title: "4월" },
{ id: "5월", title: "5월" },
{ id: "6월", title: "6월" },
{ id: "7월", title: "7월" },
{ id: "8월", title: "8월" },
{ id: "9월", title: "9월" },
{ id: "10월", title: "10" },
{ id: "11월", title: "11월" },
{ id: "12월", title: "12월", },
];
@@ -1,6 +1,6 @@
import { fetchType, getFetch, postFectch } from "@/components/lib/api";
import type { TableUpperProps } from "../../common/type/header";
import { pbnstPrfmncSttnHeadListExport, pbnstYearHeadListExport } from "./headList";
import { monthSttnHeadListExport, pbnstPrfmncSttnHeadListExport, pbnstYearHeadListExport } from "./headList";
import type { FcPrfType, PrfType } from "../../prfmncReg/pbnstPrfmncReg/type";
import type { pubHasuType, selectLabel } from "../../ruseFcltMng/fcltReg/pbnstReg/type";
import type { ReturnType } from "@common/business_components";
@@ -22,6 +22,7 @@ export type pbnstPrfmncSttnState = {
isInit: boolean;
rawData: any[];
selectYearList: selectLabel[]
monthSttnHeadList:TableUpperProps[];
}
}
@@ -34,7 +35,8 @@ export const pbnstPrfmncSttnExport: pbnstPrfmncSttnState = {
prfmncRes: prfmncRes,
isInit: false,
rawData: [],
selectYearList: []
selectYearList: [],
monthSttnHeadList:monthSttnHeadListExport
}
}
@@ -147,15 +149,15 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
let outUsageSum = 0;
let usageSum = 0;
let year = "";
const yearsData = []
const yearsData = [] as any
res.data.forEach((item) => {
res.data.forEach((item : any) => {
if (item.prfList.length === 0) {
const year = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출
const yearVal = { year: year, pbnstRuseFcltPk: item.pbnstRuseFcltPk ?? "", ruseUtztnFcltNm : item.ruseUtztnFcltNm ?? "" };
yearVal[year + "inUsage"] = 0;
yearVal[year + "outUsage"] = 0;
yearVal[year + "total"] = 0;
const year: string = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출.
const yearVal: { [key: string]: any } = { year: year, pbnstRuseFcltPk: item.pbnstRuseFcltPk ?? "", ruseUtztnFcltNm : item.ruseUtztnFcltNm ?? "" };
yearVal[`${year}inUsage`] = 0;
yearVal[`${year}outUsage`] = 0;
yearVal[`${year}total`] = 0;
yearsData.push(yearVal);
}
item.prfList.forEach((acc) => {
@@ -184,14 +186,14 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
);
if (yearData) {
yearData[year + "inUsage"] += inUsageSum;
yearData[year + "outUsage"] += outUsageSum;
yearData[year + "total"] += (inUsageSum + outUsageSum);
yearData[`${year}inUsage`] += inUsageSum;
yearData[`${year}outUsage`] += outUsageSum;
yearData[`${year}total`] += (inUsageSum + outUsageSum);
} else {
const yearVal = { year: year, pbnstRuseFcltPk: acc.pbnstRuseFcltFk, ruseUtztnFcltNm : item.ruseUtztnFcltNm ?? "" };
yearVal[year + "inUsage"] = inUsageSum;
yearVal[year + "outUsage"] = outUsageSum;
yearVal[year + "total"] = inUsageSum + outUsageSum;
const yearVal: { [key: string]: any } = { year: year, pbnstRuseFcltPk: acc.pbnstRuseFcltFk, ruseUtztnFcltNm : item.ruseUtztnFcltNm ?? "" };
yearVal[`${year}inUsage`] = inUsageSum;
yearVal[`${year}outUsage`] = outUsageSum;
yearVal[`${year}total`] = inUsageSum + outUsageSum;
yearsData.push(yearVal);
}
});
@@ -175,7 +175,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
const chData = await makeMdfr(data);
const res = await putFectch("/drain/facility", chData);
if (res.status) {
toast.success("온배수 건축물 수정이 완료되었습니다.");
toast.success("온배수 시설물 수정이 완료되었습니다.");
}
const htdflRes = await getFetchWithCount(
"wr",
@@ -191,7 +191,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
},
}));
} catch (errorUpdate) {
toast.error("온배수 건축물 수정 오류입니다..");
toast.error("온배수 시설물 수정 오류입니다..");
}
},
htdflBdstReg: async (data: customHtdflBdst, code: string | number) => {
@@ -202,7 +202,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
const chData = makeRstr(data);
const res = await postFectch("/drain/facility", chData);
if (res.status ===200) {
toast.success("온배수 건축물 등록이 완료되었습니다.");
toast.success("온배수 시설 등록이 완료되었습니다.");
}
const htdflRes = await initFetch(String(code));
set((state: htdflFacState) => ({
@@ -215,7 +215,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
},
}));
} catch (errorReg) {
toast.error("온배수 건축물 등록 오류입니다..");
toast.error("온배수 시설물 등록 오류입니다..");
}
},htdflNmSearch :async(code:string | number, fcltNm:string) =>{
@@ -14,8 +14,8 @@ export const pubFacHeadListExport: TableUpperProps[] = [
export const pbnstMngUpHeadListExport: TableUpperProps[] = [
{ id: "choose", title: "선택", type: "checkbox" },
{ id: "operMthd", title: "운영방식",type: "select", selectData: "ruseMngMtd" },
{ id: "rprtvBzenty", title: "대행업체", edit: true },
{ id: "operMthd", title: "운영방식",type: "select", selectData: "ruseMngMtd" },
{ id: "rprtvBzenty", title: "대행업체", edit: true ,maxLength:10 },
{
id: "bgngDt",
title: "시작일",
@@ -65,7 +65,6 @@ type pubFacActions = {
appendRuseFclt(array: customPbnst[]): void;
fileDelete: (atflCd: string) => void;
fileDownLoad: (atflCd: string) => void;
getpbnstInsertIndex: (data: pbnstInsertIndex) => void;
pbnstRuseFcltUpdate: (ruseRes: customPbnst[]) => void;
pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void;
pubHasuSearch :(page: string, sggOrCity: string , searchTxt:string) => void
@@ -177,9 +176,6 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
pubHasuInit: async (page: string, sggOrCity: string) => {
try {
const pbnstHasuRes = await hasuListFetch(page, sggOrCity);
return set((state: pubFacState) => ({
...state,
pbnst: {
@@ -222,22 +218,6 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
console.log(errorHasu);
}
},
// pbnstReg: async (txt: string | undefined) => {
// try {
// const pbnstRes = await hasuListFetch("11", "11");
// return set((state: pubFacState) => ({
// ...state,
// pbnst: {
// ...state.pbnst,
// pbnstRes: pbnstRes,
// isInit: false,
// },
// }));
// } catch (errorHasu) {
// console.log(errorHasu);
// }
// },
pbnstInsert: async (pbnstRes: customPbnst) => {
try {
@@ -249,8 +229,6 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
toast.success(`${pbnstRes.ruseUtztnFcltNm} 하수재이용 시설이 등록되었습니다.`);
}
const pbnstHasuRes = await hasuListFetch('1', '0');
return set((state: pubFacState) => ({
...state,
pbnst: {
@@ -276,25 +254,13 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
} ,pbnstRuseBizUpdate : async(ruseRes:fcBizType[])=>{
try {
console.log('ruseRes ', ruseRes)
const response = await putFectch(
`/reuseFc/pubFcBiz`,
ruseRes
);
// const pbnstRes = await hasuListFetch("11", "11");
// return set((state: pubFacState) => ({
// ...state,
// pbnst: {
// ...state.pbnst,
// pbnstRes: pbnstRes,
// isInit: false,
// },
// }));
} catch (error) {
console.error("Error pbnstRuseFcltUpdate:", error);
// toast.error(` 하수재이용 시설 사업이 수정이 실패되엇습니다..`);
}
} ,
getBizCycle: async (pbnstObj: customPbnst) => {
@@ -341,56 +307,13 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
fetchType.CS,
`/attach-service/api/v1/atflcd/download/${atflCd}`
);
if (!response.ok) {
throw new Error(`Error downloading file: ${response.statusText}`);
}
} catch (error) {
console.error("Error downloading file:", error);
throw new Error(`Error downloading file: ${error}`);
}
},
// pbnstMngData: (data: customPbnst[]) => {
// if (data.length > 0) {
// }
// return set((state: pubFacState) => {
// state.pbnst.pbnstMngRegData = data;
// state.pbnst.pbnstBizCycle =
// data.length <= 0
// ? [{ text: "없음", val: "1" }]
// : state.pbnst.pbnstBizCycle;
// return state;
// });
// },
// pbnstBizData: (data: fcBizType) => {
// return set((state: pubFacState) => {
// if (state.pbnst.pbnstMngRegData) {
// const regData = state.pbnst.pbnstMngRegData.find(
// (item) => data.pbnstRuseFcltFk === item.pbnstRuseFcltPk
// );
// if (regData) {
// const bizIdx = regData.fcBizs.findIndex(
// (item) => item.pbnstRuseFcltBizPk == data.pbnstRuseFcltBizPk
// );
// if (bizIdx >= 0) {
// regData.fcBizs[bizIdx] = data;
// const pbnstData = state.pbnst.pbnstMngRegData
// state.pbnst.pbnstMngRegData= [...pbnstData]
// }
// }
// }
// return state;
// });
// },
getpbnstInsertIndex: async (data: pbnstInsertIndex) => {
return set((state: pubFacState) => {
state.pbnst.pbnsUpdateIndex = data;
return state;
});
},
appendRuseFclt: async (array: customPbnst[]) => {
try {
return set((state: pubFacState) => ({
@@ -420,9 +343,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
fcOrgwtsNmList:fcOrgwtsNmList.data??[]
},
}));
}
};
};
@@ -82,6 +82,10 @@ const zNum = z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number()
);
const zNumGte = z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number().gt(0, { message: "0보다 큰 숫자만 가능합니다" })
);
const zNumNullish = zNum.nullish();
const zNumDefaultZero = zNum.default(0).nullish();
@@ -151,7 +155,6 @@ const dmdColist = z.object({
export const fcBizSchema = z.object({
pbnstRuseFcltBizPk: zNumNullish,
pbnstRuseFcltFk: z.string().nullish(),
superkey: z.string().default("").nullish(),
cycl: zNum.default(1),
seCd: z.string().max(10, "구분은 10자를 초과할 수 없습니다.").default(""),
bizMthdCd: z.string().default(""),
@@ -236,25 +239,31 @@ export const fcBizSchema = z.object({
export const pbnstRegZod = z.object({
pbnstRuseFcltPk: z.string().default(""),
swtrmtFcltFk: z.string().default(""),
ruseUtztnFcltNm: z
.preprocess(
ruseUtztnFcltNm: //z.string().default(''),
z.preprocess(
(v) => (v !== null && v !== undefined ? String(v).trim() : ""), // null/undefined → "", 공백 제거
z.string().min(1, "시설 이름을 입력하세요")),
fcltCpct: zNumDefaultZero,
ruseCpct: zNumDefaultZero,
splyExtraCpct: zNumDefaultZero,
fcltCpct: z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number().gte(0, { message: "재이용시설 용량은 0보다 크거나 같아야 합니다" })
),
ruseCpct: z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number().gte(0, { message: "재이용시설 재이용수 공급량은 0보다 크거나 같아야 합니다" })
),
splyExtraCpct: z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number().gte(0, { message: "재이용시설 공급가능량은 0보다 크거나 같아야 합니다" })
),
regDt: zDateDefaultNow,
rgtr: z.string().default("test"),
mdfcnDt: zDateDefaultNow,
mdfr: z.string().default("test"),
fcFiles: z.array(fcFileSchema).default([]).nullish(),
fcBizs: z.array(fcBizSchema).default([]),
fcOpers: z
.array(fcOperSchema)
.refine((val) => val.length >= 1, {
message: "재이용시설의 운영정보를 최소 하나를 등록해주세요",
})
.default([]), // 기본값 설정
fcOpers://z.array(fcOperSchema).default([]),
z .array(fcOperSchema)
.min(1, { message: "재이용시설의 운영정보를 최소 하나를 등록해주세요" })
});
export const pbnstRegArrayZod = z.array(pbnstRegZod);
@@ -196,15 +196,15 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = (
if(chch.status ===200) {
console.log('chch.status' , chch.status)
toast.success("건축물 및 증축/개축/중축 등록이 완료되었습니다.");
toast.success("건축물(중수도) 및 증축/개축/중축 등록이 완료되었습니다.");
}else{
toast.error("건축물 증축/개축/중축 오류입니다.");
toast.error("건축물(중수도 증축/개축/중축 오류입니다.");
}
} else {
toast.success("건축물 등록이 완료되었습니다.");
toast.success("건축물(중수도 등록이 완료되었습니다.");
}
}else{
toast.error("건축물 등록이 완료되었습니다.");
toast.error("건축물(중수도) 등록이 완료하였습니다.");
}
const rswtRes = await initFetch(code);
set((state: rswtFacState) => ({
@@ -59,7 +59,8 @@ type pbnstSttnActions = {
total: string,
reurnValue: ReturnType,
searchNm: string | undefined,
year: string
year: string,
flcts:string[]
) => void;
};
@@ -109,9 +110,7 @@ const hasuListFetch = async (
}/0/1/SW`
} else if( reurnValue.partValue === 'G'){
console.log('reurnValue.sigunValue ', reurnValue.sidoValue)
console.log('reurnValue.sigunValue ', reurnValue.sigunValue)
url += `${page}/${total}/${reurnValue.envPartValue}/${reurnValue.sigunValue !== '0' ? reurnValue.sigunValue : reurnValue.sidoValue}/0/1/SW`
}else{
@@ -121,6 +120,8 @@ const hasuListFetch = async (
if (searchNm && searchNm?.length > 0) {
url += "/" + searchNm
}
console.log(' flcts ' ,flcts)
const pbnList = await postFectch(url, flcts,fetchType.COMMON);
const getRuseList = await postFectch(
`/reuseFc/pubDetailList/${year}`,
@@ -130,12 +131,16 @@ const hasuListFetch = async (
const checkWrRegListRes = getRuseList.data;
const count = getRuseList.count;
const data: WaterReuseFacility[] = checkWrRegListRes.map(
(item: FcPrfType) => {
const findData = pbnList.data.find(
(fclt: pubHasuType) =>
fclt.bscFctCd === item.pbnstRuseFcltPk.substring(0, 11)
);
console.log(' pbnList ', pbnList)
if(findData){
const usage = item.prfList.reduce(
@@ -163,7 +168,7 @@ const hasuListFetch = async (
},
{ total: 0, inUsage: 0, outUsage: 0 }
);
console.log('findData ', findData)
return {
rvbsn: findData?.rvbsn ||'-',
lgbsn: findData?.lgbsn||'-',
@@ -196,6 +201,10 @@ const hasuListFetch = async (
}
).filter(Boolean);
console.log( ' checkWrRegListRes ', getRuseList.data)
console.log( ' data ', data)
return data;
};
@@ -292,7 +301,7 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
year,
fclts
);
console.log('pbnstSttnRes ' , pbnstSttnRes)
set( (state: pbnstSttnState) => {
return {
...state,
@@ -332,7 +341,8 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
total: string,
reurnValue: ReturnType,
searchNm: string | undefined,
year: string
year: string,
flcts:string[]
) => {
try {
@@ -341,7 +351,8 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = (
total,
reurnValue,
searchNm,
year
year,
flcts
);
set((state: pbnstSttnState) => {
@@ -4,6 +4,7 @@ import { excelRainStat } from "./excel";
import { rainStatHeadListExport } from "./headList";
export type RainStatProps = {
cityText: string
ctpvCd: string //시도
sggCd: string //시군구
areaNm: string //행정구역명
+1 -1
View File
@@ -4,6 +4,7 @@ import { rswtStatHeadListExport } from "./headList";
import { excelRswtStat } from "./excel";
export type RswtStatProps = {
cityText: string //시도명
ctpvCd: string //시도
sggCd : string //시군구
areaNm: string //행정구역명
@@ -72,7 +73,6 @@ export const rswtStatActionsExport: (set: any) => rswtStatActions = (
return {
searchRswtStatList: async (year: string) => {
const res = await getFetch(fetchType.WR, `/reuseChk/reuseWater/stat/${year}`)
const rswtStatList: RswtStatProps[] = [];
const cityArr: any[] = [];
res.forEach((item: RswtStatProps) => {
+12 -3
View File
@@ -108,8 +108,18 @@ export const postFectch = async (url: string, params: any, type?: string) => {
return await response.json();
};
export const putFectch = async (url: string, params: any) => {
const response = await fetch(DEV_URL_WR + url, {
export const putFectch = async (url: string, params: any,type?:string) => {
const div =
type === fetchType.COMMON
? DEV_URL_FO
: type === fetchType.LOCAL
? LOCAL_URL
: type === fetchType.CS
? ''
: DEV_URL_WR;
const response = await fetch(div + url, {
method: "PUT",
headers: {
"Content-Type": "application/json",
@@ -132,7 +142,6 @@ export const deleteFectch = async (url: string, params?: any, type?: string) =>
? ''
: DEV_URL_WR;
console.log("deleteFectch url", div + url);
const response = await fetch(div + url, {
method: "DELETE",
headers: {
+37 -30
View File
@@ -57,7 +57,7 @@ export const rswtChkTableData = (chkData: rswtChkZodType[]) => {
const makeRswtData: any[] = [];
if(chkData&&chkData.length>0){
if (chkData && chkData.length > 0) {
chkData?.forEach((root) => {
root.rswtrDetails.forEach((detail) => {
@@ -99,10 +99,11 @@ export const chRowspanTable = (
let order = 0
// rowspan된 셀 렌더링 여부 추적용
const renderedMap = new Map<string, Set<number>>();
return (
<>
{makeTableData.map((row, rowIndex) => (
{/* {makeTableData.map((row, rowIndex) => ( */}
{makeTableData && makeTableData.length > 0 ? (makeTableData.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{headList
.map((head) => {
@@ -177,7 +178,13 @@ export const chRowspanTable = (
})}
</TableRow>
))}
))) : (
<TableRow>
<TableCell colSpan={headList.length} className="h-24 text-center text-gray-500">
조회된 결과가 없습니다.
</TableCell>
</TableRow>
)}
</>
);
};
@@ -235,44 +242,44 @@ export const InputTableBody = (
}
return span;
};
return (
return (
<>
{chgRes.map((data, dataIndex) => {
// 소계/총합계 행 판별
const isSubTotal = data["용수명"] === "소계";
const isTotal = data["구분"] === 0;
const bgClass = isTotal
? "bg-[#d5e1eb] font-bold"
: isSubTotal
? "bg-[#e6eef5] font-semibold"
: "";
? "bg-[#e6eef5] font-semibold"
: "";
return (
<TableRow key={dataIndex + "row"}>
{headList.map((head, headIndex) => {
// 총합계 행이면 "구분"에서 colSpan=2로 병합
if (
data["구분"] === 0 &&
head.id === "구분"
) {
return (
<TableCell
key={head.id + headIndex}
colSpan={2}
className="font-bold text-center bg-[#d5e1eb]"
>
총합계
</TableCell>
);
}
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
if (
data["구분"] === 0 &&
head.id === "용수명"
) {
return null;
}
// 총합계 행이면 "구분"에서 colSpan=2로 병합
if (
data["구분"] === 0 &&
head.id === "구분"
) {
return (
<TableCell
key={head.id + headIndex}
colSpan={2}
className="font-bold text-center bg-[#d5e1eb]"
>
총합계
</TableCell>
);
}
// 총합계(합계) 행이면 "용수명" 컬럼은 렌더링하지 않음
if (
data["구분"] === 0 &&
head.id === "용수명"
) {
return null;
}
// mergeCell 처리
if (head.mergeCell) {
const rowSpan = getRowSpan(chgRes, head.id, dataIndex);
+47 -17
View File
@@ -6,24 +6,26 @@ import {
filePickedBlobArray,
type SheetParameter,
} from "@common/business_components/ui";
import { Button } from "@common/components/ui";
import type { RefObject } from "react";
import toast from "react-hot-toast";
export const getCity = (val: string, sggCd: SelectDataType[]) => {
let data:any
if(val !=='0'){
let data: any
if (val !== '0') {
data = sggCd
.filter((item) => String(item.val).substring(0, 2) === val)
.map((item) => ({
val: item.val,
text: item.text, // 한국어 텍스트
}));
}else{
.filter((item) => String(item.val).substring(0, 2) === val)
.map((item) => ({
val: item.val,
text: item.text, // 한국어 텍스트
}));
} else {
data = sggCd
.map((item) => ({
val: item.val,
text: item.text, // 한국어 텍스트
}));
.map((item) => ({
val: item.val,
text: item.text, // 한국어 텍스트
}));
}
data.unshift({ val: "0", text: "전체" });
return data;
@@ -92,7 +94,7 @@ export const excelDownload = (
export const excelArrayDownload = (
fileNm: string,
sampleFile: string,
sheetArray:SheetParameter []
sheetArray: SheetParameter[]
) => {
if (!sheetArray) return;
fetch(`/${sampleFile}`, {}).then(async (response) => {
@@ -113,7 +115,7 @@ export const handleCommaNumberChange = (
) => {
const raw = e.target.value.replace(/,/g, "");
if (!/^\d*$/.test(raw)) return;
onChange(raw === "" ? "" : Number(raw));
onChange(raw === "" ? "" : Number(raw));
};
@@ -124,7 +126,7 @@ export const handleMinusNumberChange = (
) => {
const raw = e.target.value.replace(/,/g, "");
if (!/^\d*$/.test(raw)) return;
onChange(raw === "" ? -1 : Number(raw));
onChange(raw === "" ? -1 : Number(raw));
};
// 숫자입력시 , 삽입
@@ -175,14 +177,14 @@ export const getdmdMatFlag = (flag: string | undefined | null) => {
case "Matched":
return "매칭완료";
case "Checked":
return '검토완료';
return '검토완료';
default:
return "신청";
}
};
export const formatPhoneNumber=(tel:string) => {
export const formatPhoneNumber = (tel: string) => {
const onlyNums = tel.replace(/[^0-9]/g, '');
if (onlyNums.length === 10) {
return onlyNums.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
@@ -191,4 +193,32 @@ export const formatPhoneNumber=(tel:string) => {
} else {
return tel; // 유효하지 않으면 빈 문자열
}
}
export const confirmToast = (content: string, saveFunc: () => void) => {
toast((t) => (
<span className="flex flex-col items-center">
<div className="mb-6">
{content}
</div>
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
onClick={() => {
saveFunc();
toast.dismiss(t.id);
}}
>
확인
</Button>
<Button
size={"sm"}
onClick={() => toast.dismiss(t.id)}>취소</Button>
</div>
</span>
),
{
id: "global-confirm-toast", // 중복 토스트 방지
duration: Infinity, // 자동 사라짐 방지
});
}
+29 -2
View File
@@ -7,6 +7,9 @@ import {
} from "tus-js-client";
import { CS_TUS_UPLOAD, CS_URL } from "../config";
import type { FilesType } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm";
import { fetchType, putFectch } from "../api";
import { error } from "console";
import toast from "react-hot-toast";
type UploadOptions = {
handleUploadStart?: (upload: Upload) => void;
@@ -60,8 +63,30 @@ export const TusClientUpload = (oFile: FilesType, options?: UploadOptions) =>
onSuccess(payload: OnSuccessPayload) {
options?.handleSuccess?.(payload);
if (fileInfo) {
console.log('fileInfo', fileInfo);
resolve(JSON.parse(fileInfo) as ReturnFileType);
const WrTableInfo = {
lnkgDmnNm: "tbWrRuseFcltFile",
lnkgDmnId: "fileTkn",
};
console.log('fileInfo ', fileInfo)
const fileParse = JSON.parse(fileInfo) as ReturnFileType;
putFectch(
`/attach-service/api/v1/atfl/attachments/${fileParse.atflCd}/info`,
WrTableInfo,
fetchType.CS
)
.then((res) => {
resolve(fileParse);
})
.catch((error) => {
reject({
causingError: {
cause: "파일 업로드 업데이트 실패",
},
} as DetailedError);
});
} else reject({} as DetailedError);
},
});
@@ -71,6 +96,8 @@ export const TusClientUpload = (oFile: FilesType, options?: UploadOptions) =>
} catch (e) {
console.log(e);
reject(e as DetailedError);
toast.error(String(e));
}
// setUpload(uploader);
});
@@ -0,0 +1,143 @@
"use client";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { formH1Class } from "@/components/lib/class/comClass";
import { safeDate } from "@/components/lib/func/commom";
import { DatePicker, FormField, FormUi, SelectBox } from "@common/business_components";
import { Button, Checkbox, Input } from "@common/components";
import { useEffect, useState } from "react";
import { useFieldArray } from "react-hook-form";
export default function BdstAdd({ form, readonly }: { form: any; readonly?: boolean; }) {
const { code } = useWrStore((state) => {
return state;
});
const { fields, append, remove } = useFieldArray({
name: "chgBuilding",
control: form?.control, // Provide a fallback to avoid errors
});
const plusHist = () => {
append({
bdstNm: "",
daddr: "",
bdstUsgCd: "",
});
};
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const toggleSelect = (id: string) => {
setSelectedIds((prev) =>
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
);
};
const removeSelected = () => {
const idxsToRemove = fields
.map((item, idx) => (selectedIds.includes(item.id) ? idx : -1))
.filter((index) => index > -1)
.sort((prevIndex, nextIndex) => nextIndex - prevIndex);
remove(idxsToRemove);
setSelectedIds([]);
};
return (
<>
<h1 className={formH1Class}>중축, 개축, 재축</h1>
{!readonly && (
<div className="flex justify-end gap-2 mt-4">
<Button type="button" className="" size={"sm"} onClick={plusHist}>
이력추가
</Button>
<Button
type="button"
className="!bg-red-500 text-white"
size={"sm"}
onClick={removeSelected}
>
삭제
</Button>
</div>
)}
{fields.map((field, index) => (
<div
key={field.id}
// className="grid grid-cols-[auto,1fr,1fr,1fr] gap-2 mt-4 items-center"
className={`grid ${
readonly ? "grid-cols-3" : "grid-cols-[auto,1fr,1fr,1fr]"
} gap-2 mt-4 items-center`}
>
{!readonly && (
<div className="col-span-1 flex justify-center mr-4">
<Checkbox
checked={selectedIds.includes(field.id)}
onCheckedChange={() => toggleSelect(field.id)}
/>
</div>
)}
<div className="">
<FormField
control={form.control}
name={`chgBuilding.${index}.bdstChgNm`}
render={({ field: f }) => (
<FormUi label="구분">
<SelectBox
selectClass={selectClass}
selectArray={code.chBuCd}
onSelectValue={(val) => f.onChange(val)}
value={f.value ?? code.chBuCd[0]?.val}
className={""}
label={""}
/>
</FormUi>
)}
/>
</div>
<div className="">
<FormField
control={form.control}
name={`chgBuilding.${index}.archChgPrmsnDt`}
render={({ field: f }) => (
<FormUi label="허가일">
<DatePicker
mode="single"
field={{
value: safeDate(f.value) || new Date(),
onChange: (date) => f.onChange(date),
}}
selectClass="w-full"
/>
</FormUi>
)}
/>
</div>
<div className="">
<FormField
control={form.control}
name={`chgBuilding.${index}.archChgArea`}
render={({ field: f }) => (
<FormUi label="면적">
<Input
placeholder="-"
type="text"
onChange={(e) => f.onChange(e.target.value)}
value={f.value || ''}
className={selectClass}
/>
</FormUi>
)}
/>
</div>
</div>
))}
<div className="border-b border-gray-300 my-4"> </div>
</>
)
}
@@ -28,28 +28,6 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
const [useCityCd, setUserCityCd] = useState<SelectDataType[]>([])
const [useSggCd, setUserSggCd] = useState<SelectDataType[]>([])
const { fields, append, remove } = useFieldArray({
name: "chgBuilding",
control: form?.control, // Provide a fallback to avoid errors
});
const plusHist = () => {
append({
bdstNm: "",
daddr: "",
bdstUsgCd: "",
});
};
const removeSelected = () => {
const idxsToRemove = fields
.map((item, idx) => (selectedIds.includes(item.id) ? idx : -1))
.filter((index) => index > -1)
.sort((prevIndex, nextIndex) => nextIndex - prevIndex);
remove(idxsToRemove);
setSelectedIds([]);
};
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | undefined>();
const [objSearch, setObjSearch] = useState<objSearchType>({
sgg: "",
@@ -80,9 +58,6 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
}
},[form, userSiSggCd, code.cityCd, code.sggCd])
const getSelCity = (val: string) => {
const data = getCity(val, code.sggCd);
setSelectSgg(data);
@@ -110,13 +85,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
return () => subscription?.unsubscribe();
}, [form.form]);
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const toggleSelect = (id: string) => {
setSelectedIds((prev) =>
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
);
};
useEffect(() =>{
@@ -296,88 +265,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) {
/>
</div>
</div>
<h1 className={formH1Class}>중축, 개축, 재축</h1>
<div className="flex justify-end gap-2 mt-4">
<Button type="button" className="" size={"sm"} onClick={plusHist}>
이력추가
</Button>
<Button
type="button"
className="!bg-red-500 text-white"
size={"sm"}
onClick={removeSelected}
>
삭제
</Button>
</div>
{fields.map((field, index) => (
<div
key={field.id}
className="grid grid-cols-[auto,1fr,1fr,1fr] gap-2 mt-4 items-center"
>
<div className="col-span-1 flex justify-center mr-4">
<Checkbox
checked={selectedIds.includes(field.id)}
onCheckedChange={() => toggleSelect(field.id)}
/>
</div>
<div className="">
<FormField
control={form.control}
name={`chgBuilding.${index}.bdstChgNm`}
render={({ field: f }) => (
<FormUi label="구분">
<SelectBox
selectClass={selectClass}
selectArray={code.chBuCd}
onSelectValue={(val) => f.onChange(val)}
value={f.value ?? code.chBuCd[0]?.val}
className={""}
label={""}
/>
</FormUi>
)}
/>
</div>
<div className="">
<FormField
control={form.control}
name={`chgBuilding.${index}.archChgPrmsnDt`}
render={({ field: f }) => (
<FormUi label="허가일">
<DatePicker
mode="single"
field={{
value: safeDate(f.value) || new Date(),
onChange: (date) => f.onChange(date),
}}
selectClass="w-full"
/>
</FormUi>
)}
/>
</div>
<div className="">
<FormField
control={form.control}
name={`chgBuilding.${index}.archChgArea`}
render={({ field: f }) => (
<FormUi label="면적">
<Input
placeholder="-"
type="text"
onChange={(e) => f.onChange}
value={f.value||''}
className={selectClass}
/>
</FormUi>
)}
/>
</div>
</div>
))}
<div className="border-b border-gray-300 my-4"> </div>
</>
</>
) : (
<></>
)}
@@ -19,11 +19,13 @@ import {
import { useEffect, useState } from "react";
import BdstReg from "../bdstReg/bdstReg";
import {
confirmToast,
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class } from "@/components/lib/class/comClass";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function HtdflRegForm({
htdflForm,
@@ -48,20 +50,25 @@ export default function HtdflRegForm({
const bdstNm = form.watch("bdstNm");
const isPowerPlant = bdstNm?.includes("발전소");
const { t } = useTranslation('base');
const onSubmit = (values: z.infer<typeof hdtflZod>) => {
if (isModal) {
const data = regCtpvCd ? regCtpvCd : regSggcd ? regSggcd : "noCd";
if (data === "noCd") {
toast.error("시도/시군구 코드를 선택해야합니다.");
return;
}
htdflBdstReg(values, data);
if (setIsModal) setIsModal(false);
const handleConfirm = async () => {
htdflBdstReg(values, data);
if (setIsModal) setIsModal(false);
};
confirmToast(t('isInsertMsg'), handleConfirm);
} else {
const handleConfirm = async () => {
htdflBdstUpdate(values, "0");
};
confirmToast(t('isUpdateMsg'), handleConfirm);
htdflBdstUpdate(values, "0");
}
};
@@ -90,15 +97,15 @@ export default function HtdflRegForm({
};
};
const [divStyle, setDivStyle] = useState("h-[53vh]");
useEffect(()=>{
if(isModal===true){
useEffect(() => {
if (isModal === true) {
setDivStyle("h-[53vh] max-h-[37vh]")
}else if(readonly===true){
} else if (readonly === true) {
setDivStyle("h-[62vh]")
}else{
} else {
setDivStyle("h-[53vh]")
}
},[isModal,readonly]);
}, [isModal, readonly]);
useEffect(() => {
console.log("form errors:", form.formState.errors);
@@ -111,12 +111,9 @@ export function PbnstBuseForm({
const fcOrgwts = form.watch(`items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`);
useEffect(() => {
console.log("fcOrgwts", fcOrgwts);
if (fcOrgwts && fcOrgwts?.length > 0) {
const swtrmtFcltFkList = fcOrgwts?.map((item) => item.swtrmtFcltFk);
console.log("swtrmtFcltFkList ", swtrmtFcltFkList);
if (swtrmtFcltFkList.length > 0) {
getFcOrgwts(
swtrmtFcltFkList.filter((item): item is string => item != null),
@@ -317,8 +314,6 @@ useEffect(() =>{
const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`);
return (
<>
{pbnstBiz && pbnstBiz?.length > 0 ? (
@@ -488,24 +483,11 @@ useEffect(() =>{
}
headList={pbnst.pplInstlsHeadLisat}
code={ppiSelectList ?? []}
//ref={ref}
indexMng={indexMng}
indexBiz={indexBiz}
form={form}
formNm={"pplInstls"}
upHeadList={[]}
onTableSet={(val) => {
const pplSum = form.watch(
`items.${indexMng}.fcBizs.${indexBiz}.pplInstls`
);
if (!pplSum) return;
let sum = 0;
pplSum.forEach((item) => {
sum += Number(item.cpct) || 0;
});
setSumCpct(sum);
}}
/>
</div>
@@ -9,9 +9,8 @@ import {
} from "@common/business_components/ui";
import { Form, FormField } from "@common/business_components/ui/form";
import { Button, Input } from "@common/components";
import { useEffect, useState } from "react";
import { useFieldArray, useWatch, type UseFormReturn } from "react-hook-form";
import { z } from "zod";
import { useEffect, useRef, useState } from "react";
import { useFieldArray, type UseFormReturn } from "react-hook-form";
import {
commonButonSize,
formH1Class,
@@ -19,7 +18,6 @@ import {
} from "@/components/lib/class/comClass";
import { TusClientUpload } from "@/components/lib/useTus_wr/useTusWr";
import {
pbnstRegZod,
type customPbnst,
type fcFileType,
type PbnstRegArrrayType,
@@ -50,15 +48,19 @@ export function PbnstMngForm({
indexMng: number;
setIndexMng: (index: number) => void;
}) {
const { code, pbnst, pbnstInsert, fileDelete, fileDownLoad } = useWrStore(
const { code, pbnst, fileDelete, fileDownLoad } = useWrStore(
(state) => state
);
const { pbnstRes } = pbnst;
const fileInitRef = useRef<{ index: number; key: string } | null>(null);
const [regFileList, setRegFileList] = useState<fcFileType[] | null>(null);
const [planFiles, setPlanFiles] = useState<FilesType[] | null>(null);
const [relationfiles, setRelationFiles] = useState<FilesType[] | null>(null);
const [fcltNm, setFcltNm] = useState<selectLabel[] | null>(null);
const [opersKey, setOpersKey] = useState(0);
useEffect(() => {
setOpersKey((prev) => prev + 1);
}, [indexMng]);
const [mngMtdCode, setMngMtdCode] =
useState<{ name: string; val: SelectDataType[] }[]>();
@@ -71,9 +73,9 @@ export function PbnstMngForm({
name: `items.${indexMng}.fcOpers`,
});
const watchedItem = useWatch({
const { fields: mngRegForm } = useFieldArray({
control: form.control,
name: `items.${indexMng}`,
name: `items`,
});
const plusHist = () => {
@@ -88,6 +90,8 @@ export function PbnstMngForm({
bgngDt: new Date(),
endDt: new Date(),
});
//trigger(`items.${indexMng}.fcOpers`);
};
const removeSelected = () => {
@@ -171,68 +175,69 @@ export function PbnstMngForm({
};
useEffect(() => {
if (indexMng >= 0) {
const defaultFiles = watchedItem?.fcFiles;
if (defaultFiles && defaultFiles.length > 0) {
const relationFileList = defaultFiles.filter(
(item) => item.fileKnd === "계통도"
);
const currentItem = form.getValues(`items.${indexMng}`);
const currentKey = JSON.stringify(currentItem);
const isNewItem =
!fileInitRef.current ||
fileInitRef.current.index !== indexMng ||
fileInitRef.current.key !== currentKey;
const planFileList = defaultFiles.filter(
(item) => item.fileKnd === "평면도"
);
if (relationFileList.length > 0)
setRelationFiles(
relationFileList.map((item) => ({
atflCd: item.fileTkn,
file: new File([] as BlobPart[], item.filePath),
}))
);
else {
setRelationFiles(null);
}
if (!isNewItem) return; //
if (planFileList.length > 0)
setPlanFiles(
planFileList.map((item) => ({
atflCd: item.fileTkn,
file: new File([] as BlobPart[], item.filePath),
}))
);
else {
setPlanFiles(null);
}
const defaultFiles = currentItem?.fcFiles || [];
const relationFileList = defaultFiles.filter(
(item) => item.fileKnd === "계통도"
);
const planFileList = defaultFiles.filter(
(item) => item.fileKnd === "평면도"
);
setRelationFiles(
relationFileList.length > 0
? relationFileList.map((item) => ({
atflCd: item.fileTkn,
file: new File([], item.filePath),
}))
: null
);
setPlanFiles(
planFileList.length > 0
? planFileList.map((item) => ({
atflCd: item.fileTkn,
file: new File([], item.filePath),
}))
: null
);
// ✅ 다음 루프에선 다시 안 하도록 저장
fileInitRef.current = {
index: indexMng,
key: currentKey,
};
}, [indexMng, form.getValues(`items.${indexMng}`)]);
useEffect(
() => {
//폼이 들어오면 폼에 대한 이름 셀렉트박스를만들기 위해
if (mngRegForm && mngRegForm.length >= 0) {
const fcltNmList = mngRegForm?.map((item, index) => ({
text: item.ruseUtztnFcltNm,
val: String(index),
}));
if (fcltNmList) setFcltNm(fcltNmList);
} else {
setRelationFiles(null);
setPlanFiles(null);
setFcltNm(null);
}
}
}, [watchedItem, indexMng]);
}, //pbnst.pbnstRes
[mngRegForm]
);
useEffect(() => {
// pbnstMngData(pbnst.pbnstMngRegData);
}, [pbnst.pbnstMngRegData]);
useEffect(() => {
if (pbnst.pbnstRes && pbnst.pbnstRes?.length >= 0) {
const fcltNmList = pbnst.pbnstRes?.map((item, index) => ({
text: item.ruseUtztnFcltNm,
val: String(index),
}));
console.log(" fcltNmList ", fcltNmList);
if (fcltNmList) setFcltNm(fcltNmList);
} else {
setFcltNm(null);
if (regFileList && regFileList?.length > 0) {
form.setValue(`items.${indexMng}.fcFiles`, regFileList);
}
}, [pbnst.pbnstRes]);
// 2. Define a submit handler.
function onSubmit(values: z.infer<typeof pbnstRegZod>) {
values.fcOpers = [];
values.fcFiles = regFileList || [];
pbnstInsert(values);
}
}, [regFileList, indexMng]);
useEffect(() => {
if (relationfiles) {
@@ -251,14 +256,6 @@ export function PbnstMngForm({
}
}, [code]);
useEffect(() => {
// console.log(' fcltNm ', watchedItem?.ruseUtztnFcltNm)
}, [watchedItem]);
useEffect(() => {
console.log(" form indexMng ", indexMng);
}, [indexMng]);
return (
<div className=" !mx-[1.6rem] !p-0r">
<div>
@@ -338,26 +335,25 @@ export function PbnstMngForm({
</div>
<div className="h-[200px] overflow-y-auto">
<RuseMngTableForm
tableData={fcOpersField}
key={`ruse-table-${indexMng}-${fcOpersField.length}`} // 🔥 length까지 넣어야 동기화됨
tableData={form.getValues(`items.${indexMng}.fcOpers`) ||[]}
headList={
formType === "sttn"
? pbnst.pbnstMngUpHeadList.map((item) => {
return {
...item,
readonly: true,
};
})
return {
...item,
readonly: true,
};
})
: pbnst.pbnstMngUpHeadList
}
upHeadList={pbnst.pbnstMngNm}
code={mngMtdCode || []}
onTableSet={(val) => {
//setFcOpresData(val);
}}
indexMng={indexMng}
form={form}
formNm={"fcOpers"}
// ref={ref}
// ref={ref}
></RuseMngTableForm>
</div>
</div>
@@ -374,6 +370,7 @@ export function PbnstMngForm({
<Input
key={`items.${indexMng}.fcltCpct`}
placeholder=""
maxLength={10} //입력제한
type="text"
onChange={(e) => {
if (formType !== "sttn")
@@ -397,6 +394,7 @@ export function PbnstMngForm({
<Input
key={`items.${indexMng}.ruseCpct`}
placeholder=""
maxLength={10} //입력제한
type="text"
onChange={(e) => {
if (formType !== "sttn")
@@ -419,6 +417,7 @@ export function PbnstMngForm({
<Input
key={`items.${indexMng}.splyExtraCpct`}
placeholder=""
maxLength={10} //입력제한
type="text"
onChange={(e) => {
if (formType !== "sttn")
@@ -518,3 +517,7 @@ export function PbnstMngForm({
</div>
);
}
function trigger(arg0: string) {
throw new Error("Function not implemented.");
}
@@ -110,6 +110,9 @@ export default function MngRegFormDetail({
<Input
placeholder=""
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={formHead.option === "readOnly" ? true : false}
@@ -8,6 +8,7 @@ import {
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import {
confirmToast,
formatCommaNumber,
handleMinusNumberChange,
} from "@/components/lib/func/commom";
@@ -20,6 +21,8 @@ import { useForm } from "react-hook-form";
import { z } from "zod";
import BdstReg from "../bdstReg/bdstReg";
import toast from "react-hot-toast";
import BdstAdd from "../bdstReg/bdstAdd";
import { useTranslation } from "react-i18next";
export default function RainRegForm({
rainForm,
@@ -39,9 +42,10 @@ export default function RainRegForm({
const defaultValues: z.infer<typeof rainZod> = rainZod.parse({});
const form = useForm<z.infer<typeof rainZod>>({
resolver: zodResolver(rainZod),
// defaultValues: defaultValues,
// defaultValues: defaultValues,
});
const instlCst = form.watch(`reuseBuilding.0.instlCst`);
const { t } = useTranslation('base');
const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn] = form.watch([
"reuseBuilding.0.grntchBdstCertYn",
@@ -50,15 +54,15 @@ export default function RainRegForm({
]);
const [divStyle, setDivStyle] = useState("h-[53vh]");
useEffect(()=>{
if(isModal===true){
useEffect(() => {
if (isModal === true) {
setDivStyle("h-[53vh] max-h-[37vh]")
}else if(readonly===true){
} else if (readonly === true) {
setDivStyle("h-[62vh]")
}else{
} else {
setDivStyle("h-[53vh]")
}
},[isModal,readonly]);
}, [isModal, readonly]);
const cleanRainData = (raw: any): z.infer<typeof rainZod> => {
const buildings =
@@ -92,9 +96,6 @@ export default function RainRegForm({
})),
};
};
useEffect(()=>{
console.log("이즈모달", isModal)
}, [])
useEffect(() => {
if (grntchBdstCertYn === "N") {
@@ -124,7 +125,7 @@ export default function RainRegForm({
// form 값이 변경될 때마다 로그 출력
useEffect(() => {
const subscription = form.watch((value) => {});
const subscription = form.watch((value) => { });
return () => subscription.unsubscribe();
}, [form]);
@@ -141,10 +142,16 @@ export default function RainRegForm({
return;
}
rainBdstReg(values, data);
if (setIsModal) setIsModal(false);
const handleConfirm = async () => {
rainBdstReg(values, data);
if (setIsModal) setIsModal(false);
};
confirmToast(t('isInsertMsg'), handleConfirm);
} else {
rainBdstUpdate(values, "41");
const handleConfirm = async () => {
rainBdstUpdate(values, "41");
};
confirmToast(t('isUpdateMsg'), handleConfirm);
}
};
@@ -176,150 +183,97 @@ export default function RainRegForm({
</div>
</div>
<ScrollArea className={divStyle}>
{isModal && rainForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="pl-3 text-xl col-span-10 mt-4">
빗물이용시설 정보{" "}
</h1>
) : (
<></>
)}
<h1 className={formH1Class}>기본정보</h1>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.instlCmptnDt"
render={({ field }) => (
<FormUi label="설치완료일">
<DatePicker
mode="single"
field={{
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(),
onChange: (date: Date | undefined) =>
field.onChange(date),
}}
selectClass={"w-full"}
readOnly={readonly}
/>
</FormUi>
)}
/>
</div>
{isModal && rainForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="pl-3 text-xl col-span-10 mt-4">
빗물이용시설 정보{" "}
</h1>
) : (
<></>
)}
<h1 className={formH1Class}>기본정보</h1>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.instlCmptnDt"
render={({ field }) => (
<FormUi label="설치완료일">
<DatePicker
mode="single"
field={{
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(),
onChange: (date: Date | undefined) =>
field.onChange(date),
}}
selectClass={"w-full"}
readOnly={readonly}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name={`reuseBuilding.0.instlCst`}
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === -1; // 또는 Number(value) === -1
return (
<FormUi label="설치비(원)">
<Input
placeholder="1,000,000"
type="text"
className={selectClass}
readOnly={readonly}
value={
isUnknown ? "확인불가" : formatCommaNumber(value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
<div className="col-span-3">
<FormField
control={form.control}
name={`reuseBuilding.0.instlCst`}
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === -1; // 또는 Number(value) === -1
return (
<FormUi label="설치비(원)">
<Input
placeholder="1,000,000"
type="text"
className={selectClass}
readOnly={readonly}
value={
isUnknown ? "확인불가" : formatCommaNumber(value)
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
/>
</FormUi>
);
}}
/>
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
/>
</FormUi>
);
}}
/>
</div>
</div>
</div>
<div className="">
<h1 className={formH1Class}>빗물이용시설 정보</h1>
</div>
<div>
<h2 className={formH2Class}>집수면 및 저류조</h2>
</div>
<div className="grid grid-cols-12 gap-4 mt-4 ">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.dtfcltCpct"
render={({ field }) => {
const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1
return (
<FormUi label="저류조 용량(m³)">
<Input
className={selectClass}
placeholder="1,000,000"
type="text"
value={
isUnknown
? "확인불가"
: formatCommaNumber(field.value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
readOnly={readonly}
/>
</FormUi>
);
}}
/>
<div className="">
<h1 className={formH1Class}>빗물이용시설 정보</h1>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColKnd"
render={({ field }) => (
<FormUi label="집수면 종류">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={rainFcltCd}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? rainFcltCd[0]?.val}
readOnly={readonly}
/>
</FormUi>
)}
/>
<div>
<h2 className={formH2Class}>집수면 및 저류조</h2>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColArea"
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === -1; // 또는 Number(value) === -1
return (
<div className="flex items-center gap-2">
<FormUi label="집수면 용량(m³)">
<div className="grid grid-cols-12 gap-4 mt-4 ">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.dtfcltCpct"
render={({ field }) => {
const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1
return (
<FormUi label="저류조 용량(m³)">
<Input
className={selectClass}
placeholder="1,000,000"
type="text"
value={
isUnknown ? "확인불가" : formatCommaNumber(value)
isUnknown
? "확인불가"
: formatCommaNumber(field.value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
@@ -334,273 +288,327 @@ export default function RainRegForm({
readOnly={readonly}
/>
</FormUi>
</div>
);
}}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>여과 등 처리시설</h2>
</div>
<div className="grid grid-cols-12 gap-2 mt-4">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctYn"
render={({ field }) => (
<FormUi label="유무">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstFilterYn}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={readonly}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctCpct"
render={({ field }) => {
const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1
return (
<FormUi label="용량(m³/일)">
<Input
className={selectClass}
placeholder="1,000,000"
type="text"
value={
isUnknown
? "확인불가"
: formatCommaNumber(field.value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
);
}}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColKnd"
render={({ field }) => (
<FormUi label="집수면 종류">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={rainFcltCd}
onSelectValue={readonly ? () => { } : field.onChange}
value={field.value ?? rainFcltCd[0]?.val}
readOnly={readonly}
disabled={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
);
}}
/>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColArea"
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === -1; // 또는 Number(value) === -1
return (
<div className="flex items-center gap-2">
<FormUi label="집수면 용량(m³)">
<Input
className={selectClass}
placeholder="1,000,000"
type="text"
value={
isUnknown ? "확인불가" : formatCommaNumber(value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
readOnly={readonly}
/>
</FormUi>
</div>
);
}}
/>
</div>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctPrs"
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === "-"; // 또는 Number(value) === -1
return (
<div className="flex items-center gap-2">
<FormUi label="처리공정">
<div>
<h2 className={formH2Class}>여과 등 처리시설</h2>
</div>
<div className="grid grid-cols-12 gap-2 mt-4">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctYn"
render={({ field }) => (
<FormUi label="유무">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstFilterYn}
onSelectValue={readonly ? () => { } : field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={readonly}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctCpct"
render={({ field }) => {
const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1
return (
<FormUi label="용량(m³/일)">
<Input
className={" h-full !text-xl col-span-4"}
placeholder="처리공정 입력"
className={selectClass}
placeholder="1,000,000"
type="text"
value={field.value ?? ""}
onChange={field.onChange}
value={
isUnknown
? "확인불가"
: formatCommaNumber(field.value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
readOnly={readonly}
disabled={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
</div>
);
}}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>인센티브 정보</h1>
</div>
<div>
<h2 className={formH2Class}>설치비 지원</h2>
</div>
<div className="grid grid-cols-10 gap-2 mt-4">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtYn"
render={({ field }) => (
<FormUi label="지원 여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstIncenYn}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={readonly}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtAmt"
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === -1; // 또는 Number(value) === -1
return (
<FormUi label="지원금액(원)">
<Input
className={selectClass}
placeholder=""
type="text"
readOnly={readonly || isUnknown}
value={
isUnknown ? "확인불가" : formatCommaNumber(value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
disabled={instlCstSprtYn !== "Y"}
/>
</FormUi>
);
}}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>녹색건축물 인증</h2>
</div>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYn"
render={({ field }) => (
<FormUi label="인증여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstIncenYn}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? code.yn[0]?.val}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYr"
render={({ field }) => {
const isUnknown =
field.value === null || field.value === "0000"; // 또는 Number(value) === -1
return (
<FormUi label="인증년도">
<Input
className={selectClass}
placeholder="2024"
type=""
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
value={isUnknown ? "확인불가" : (field.value ?? "")}
onChange={(e) => {
if (readonly) return;
if (e.target.value === "확인불가") {
field.onChange("0000");
} else {
field.onChange(e.target.value);
}
}}
disabled={grntchBdstCertYn !== "Y"}
max={4}
/>
</FormUi>
);
}}
/>
</div>
<div className="col-span-3 ">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertNo"
render={({ field }) => {
const isUnknown =
field.value === null || field.value === "0000"; // 또는 Number(value) === -1
return (
<FormUi label="인증번호">
<Input
className={selectClass}
placeholder="ab1234678"
type=""
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
value={isUnknown ? "확인불가" : (field.value ?? "")}
onChange={(e) => {
if (readonly) return;
);
}}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctPrs"
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === "-"; // 또는 Number(value) === -1
if (e.target.value === "확인불가") {
field.onChange("");
} else {
field.onChange(e.target.value);
}
}}
disabled={grntchBdstCertYn !== "Y"}
return (
<div className="flex items-center gap-2">
<FormUi label="처리공정">
<Input
className={" h-full !text-xl col-span-4"}
placeholder="처리공정 입력"
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={readonly}
disabled={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
</div>
);
}}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>인센티브 정보</h1>
</div>
<div>
<h2 className={formH2Class}>설치비 지원</h2>
</div>
<div className="grid grid-cols-10 gap-2 mt-4">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtYn"
render={({ field }) => (
<FormUi label="지원 여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstIncenYn}
onSelectValue={readonly ? () => { } : field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={readonly}
/>
</FormUi>
);
}}
/>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtAmt"
render={({ field }) => {
const value = field.value;
const isUnknown = value === null || value === -1; // 또는 Number(value) === -1
return (
<FormUi label="지원금액(원)">
<Input
className={selectClass}
placeholder=""
type="text"
readOnly={readonly || isUnknown}
value={
isUnknown ? "확인불가" : formatCommaNumber(value)
}
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
onChange={(e) => {
if (readonly) return;
handleMinusNumberChange(e, field.onChange);
}}
disabled={instlCstSprtYn !== "Y"}
/>
</FormUi>
);
}}
/>
</div>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertGrd"
render={({ field }) => (
<FormUi label="인증등급">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstGrntchClass}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? code.bdstGrntchClass[0]?.val}
readOnly={readonly}
/>
</FormUi>
)}
/>
<div>
<h2 className={formH2Class}>녹색건축물 인증</h2>
</div>
</div>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYn"
render={({ field }) => (
<FormUi label="인증여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstIncenYn}
onSelectValue={readonly ? () => { } : field.onChange}
value={field.value ?? code.yn[0]?.val}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYr"
render={({ field }) => {
const isUnknown =
field.value === null || field.value === "0000"; // 또는 Number(value) === -1
return (
<FormUi label="인증년도">
<Input
className={selectClass}
placeholder="2024"
type=""
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
value={isUnknown ? "확인불가" : (field.value ?? "")}
onChange={(e) => {
if (readonly) return;
if (e.target.value === "확인불가") {
field.onChange("0000");
} else {
field.onChange(e.target.value);
}
}}
disabled={grntchBdstCertYn !== "Y"}
max={4}
/>
</FormUi>
);
}}
/>
</div>
<div className="col-span-3 ">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertNo"
render={({ field }) => {
const isUnknown =
field.value === null || field.value === "0000"; // 또는 Number(value) === -1
return (
<FormUi label="인증번호">
<Input
className={selectClass}
placeholder="ab1234678"
type=""
onFocus={() => {
// 입력 시 자동으로 확인불가 해제
if (isUnknown && !readonly) {
field.onChange(""); // 초기화
}
}}
value={isUnknown ? "확인불가" : (field.value ?? "")}
onChange={(e) => {
if (readonly) return;
if (e.target.value === "확인불가") {
field.onChange("");
} else {
field.onChange(e.target.value);
}
}}
disabled={grntchBdstCertYn !== "Y"}
/>
</FormUi>
);
}}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertGrd"
render={({ field }) => (
<FormUi label="인증등급">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.bdstGrntchClass}
onSelectValue={readonly ? () => { } : field.onChange}
value={field.value ?? code.bdstGrntchClass[0]?.val}
readOnly={readonly}
/>
</FormUi>
)}
/>
</div>
</div>
<BdstAdd form={form} readonly={readonly}></BdstAdd>
</ScrollArea>
</form>
</Form>
@@ -8,9 +8,9 @@ import {
} from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import {
confirmToast,
formatCommaNumber,
handleCommaNumberChange,
handleMinusNumberChange,
handleMinusNumberChange
} from "@/components/lib/func/commom";
import {
DatePicker,
@@ -22,9 +22,11 @@ import {
import { Button, Input, ScrollArea } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
import { useForm, type UseFormReturn } from "react-hook-form";
import { useForm } from "react-hook-form";
import { z } from "zod";
import BdstAdd from "../bdstReg/bdstAdd";
import BdstReg from "../bdstReg/bdstReg";
import { useTranslation } from "react-i18next";
export default function RswtrRegForm({
rswtForm,
@@ -41,20 +43,20 @@ export default function RswtrRegForm({
(state) => state
);
const [divStyle, setDivStyle] = useState("h-[53vh]");
useEffect(()=>{
if(isModal===true){
useEffect(() => {
if (isModal === true) {
setDivStyle("h-[53vh] max-h-[37vh]")
}else if(readonly===true){
} else if (readonly === true) {
setDivStyle("h-[62vh]")
}else{
} else {
setDivStyle("h-[53vh]")
}
},[isModal,readonly]);
// const defaultValues: z.infer<typeof rswtZod> = rswtZod.parse({});
}, [isModal, readonly]);
// const defaultValues: z.infer<typeof rswtZod> = rswtZod.parse({});
const form = useForm<z.infer<typeof rswtZod>>({
resolver: zodResolver(rswtZod),
// defaultValues: defaultValues,
// defaultValues: defaultValues,
});
const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs] = form.watch([
@@ -63,12 +65,20 @@ export default function RswtrRegForm({
"reuseBuilding.0.rswtrObj.filtrTrtmfctProcs",
]);
const { t } = useTranslation('base');
const onSubmit = (values: z.infer<typeof rswtZod>) => {
if (isModal) {
rswtBdstReg(values, "41");
if (setIsModal) setIsModal(false);
const handleConfirm = async () => {
rswtBdstReg(values, "41");
if (setIsModal) setIsModal(false);
};
confirmToast(t('isInsertMsg'), handleConfirm);
} else {
rswtBdstUpdate(values, "41");
const handleConfirm = async () => {
rswtBdstUpdate(values, "41");
};
confirmToast(t('isUpdateMsg'), handleConfirm);
}
};
@@ -459,9 +469,6 @@ export default function RswtrRegForm({
}
}}
disabled={grntchBdstCertYn == "N" || grntchBdstCertYn == "D"}
/>
</FormUi>
)
@@ -488,6 +495,7 @@ export default function RswtrRegForm({
/>
</div>
</div>
<BdstAdd form={form} readonly={readonly}></BdstAdd>
</ScrollArea>
</form>
</Form>
@@ -35,9 +35,7 @@ export function PbnstRegModal({
setPbnstInsert: (data: customPbnst) => void;
onInvalid:(errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>) => void
}) {
const { pbnstInsert } = useWrStore((state) => {
return state;
});
const [pbnstBdstForm, setPbnstBdstForm] = useState<customPbnst>(
{} as customPbnst
);
@@ -126,6 +124,7 @@ export function PbnstRegModal({
}, [form.formState.errors]);
useEffect(() => {
const firstForm = form.getValues("items");
if (firstForm) {
setIndexMng(firstForm.length - 1);
@@ -1,3 +1,4 @@
import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead";
import { useWrStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common/type/header";
import {
@@ -14,8 +15,10 @@ import {
type fcBizType,
} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import { formH1Class } from "@/components/lib/class/comClass";
import { InputTableBody } from "@/components/lib/func/TableData";
import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm";
import { ScrollArea, Title, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import {
Tabs,
@@ -23,6 +26,9 @@ import {
TabsTrigger,
Button,
TabsContent,
TableHeader,
TableBody,
Table,
} from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
@@ -30,7 +36,7 @@ import { set, useForm } from "react-hook-form";
import { z } from "zod";
export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titleVisible?: boolean }) {
const { code, pbnstSttn, prfmnc } = useWrStore((state) => state);
const { code, pbnstSttn, prfmnc , pbnstPrfmncSttn} = useWrStore((state) => state);
const [fcltTab, setFcltTab] = useState<string>("fclt");
const [ruseTab, setRuseTab] = useState<string>("totalPrf");
const [ruseBizData, setRuseBizData] = useState<{
@@ -180,6 +186,7 @@ export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titl
const usageKey = inData.usage;
const value = (prfItem as any)[usageKey] ?? 0;
inData[monthKey] = value;
inData.total += value;
@@ -231,7 +238,6 @@ export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titl
)?.prfList;
if (prfList) {
console.log("prfList", prfList);
getTablePrfData(prfList);
}
@@ -324,17 +330,23 @@ export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titl
</TabsList>
<TabsContent className="max-h-full" value={ruseTab}>
{prfData && (
<UiTable
headName={undefined}
tableData={prfData[ruseTab as keyof typeof prfData] || []}
headlist={monthHeadList || []}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}
></UiTable>
<ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
<Table variant="secondary">
<TableHeader className="font-bold">
{headMakeprfmnc(pbnstPrfmncSttn.monthSttnHeadList, [])}
</TableHeader>
<TableBody key={prfmnc.pageKey + 'pageKey'}>
{InputTableBody(prfData[ruseTab as keyof typeof prfData] || [], pbnstPrfmncSttn.monthSttnHeadList, setPrfData as any)}
</TableBody>
</Table>
</ScrollArea>
</ListDiv>
)}
</TabsContent>
</Tabs>
@@ -11,26 +11,18 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import type { z } from "zod";
export default function SttnDetailModal({
formObj,
export default function CoDetailModal({
formObj,onFormSubmit
}: {
formObj: any;
onFormSubmit:(values:customLdrmnt)=>void;
}) {
const { ldrmntUpdate } = useWrStore(
(state) => state
);
const defaultValues: z.infer<typeof ldrmntZod> = ldrmntZod.parse({});
const form = useForm<z.infer<typeof ldrmntZod>>({
resolver: zodResolver(ldrmntZod),
defaultValues: defaultValues,
});
const onSubmit = (values: customLdrmnt) => {
console.log("values", values)
if (values)
ldrmntUpdate(values)
};
useEffect(() => {
if (ldrmntZod) form.reset(formObj);
}, [formObj]);
@@ -55,7 +47,7 @@ export default function SttnDetailModal({
<div>
<ScrollArea className="h-[85vh]">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<form onSubmit={form.handleSubmit(onFormSubmit)}>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">
잠재수요처 상세정보
@@ -155,4 +155,6 @@ const RuseMngTable = React.forwardRef<any, RuseMngTableProps>(
);
}
);
RuseMngTable.displayName = "RuseMngTable";
export default RuseMngTable;
@@ -1,7 +1,6 @@
import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead";
import type { TableUpperProps } from "@/app/store/common/type/header";
import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList";
import type { customPbnst as OriginalCustomPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
formatCommaNumber,
@@ -32,7 +31,6 @@ type RuseMngTableProps = {
headList: TableUpperProps[];
upHeadList: chHeadType[];
code?: { name: string; val: SelectDataType[] }[];
onTableSet?: (item: any) => void;
indexMng?: number;
indexBiz?: number;
form?: any;
@@ -46,7 +44,6 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
headList,
upHeadList,
code,
onTableSet,
indexMng,
form,
formNm,
@@ -56,7 +53,8 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
) => {
return (
<>
<Table key={`table-${indexMng}-${indexBiz}`} variant="secondary">
<Table key={`${formNm}-table-${indexMng}-${indexBiz}`} variant="secondary"
ref={ref}>
<TableHeader className="font-bold">
{upHeadList ? (
headMakeprfmnc(headList, upHeadList)
@@ -114,6 +112,7 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
: field.value || ""
}
readOnly={head.readonly ? true : false}
maxLength={head.maxLength?head.maxLength:15}
/>
) : head.type === "select" ? (
<SelectBox
@@ -183,4 +182,7 @@ const RuseMngTableForm = React.forwardRef<any, RuseMngTableProps>(
);
}
);
RuseMngTableForm.displayName = "RuseMngTableForm";
export default RuseMngTableForm;
+5 -1
View File
@@ -1,6 +1,6 @@
import { FlatCompat } from "@eslint/eslintrc";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -11,6 +11,10 @@ const compat = new FlatCompat({
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{rules:{
// other rules..
"@typescript-eslint/no-explicit-any": "off",
}},
];
export default eslintConfig;
+2
View File
@@ -16,11 +16,13 @@
"@tanstack/react-table": "^8.21.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"i18next": "^25.3.2",
"lucide-react": "^0.474.0",
"moment": "^2.30.1",
"next": "15.1.7",
"react-hook-form": "^7.54.2",
"react-hot-toast": "^2.5.2",
"react-i18next": "^15.6.0",
"tailwind-merge": "^3.0.1",
"tailwindcss-animate": "^1.0.7",
"tus-js-client": "^4.3.1",
+23
View File
@@ -0,0 +1,23 @@
{
"deleteMsg" : "{{name}}가 삭제되었습니다.",
"insertMsg": "{{name}}가 저장되었습니다.",
"updateMsg": "{{name}}가 수정되었습니다.",
"errorMsg": "{{name}} 중 오류가 발생했습니다.",
"fileErrMsg": "빈 파일 또는 파일 오류입니다.",
"selectErrMsg": "{{name}}을(를) 선택해주세요.",
"noChangedErrMsg": "변경사항이 없습니다.",
"dateErrMsg": "시작년도를 종료년도 이후로 설정할 수 없습니다.",
"fileDateErrMsg": "파일업로드 이전에 등록년도를 작성해주세요.",
"isDeleteMsg": "선택한 정보를 삭제하시겠습니까?",
"isUpdateMsg": "수정한 정보를 저장하시겠습니까?",
"isInsertMsg": "등록하시겠습니까?",
"excelDownErrMsg": "다운로드할 데이터가 없습니다.",
"alreadyInsertedErrMsg": "사업자번호 {{num}}(은)는 이미 등록되었습니다. 다시 등록해주세요.",
"saveRowErrMsg": "수정을 원하는 행을 선택하고 저장버튼을 눌러주세요.",
"costCalcErrMsg":"공사비 계산 진행 후 저장을 눌러주세요",
"maxYearErrMsg": "최대 {{year}}년까지 선택가능합니다.",
"checkQtyMsg": "요구수량(M3/d)을 확인해주세요 계산할 수 없습니다."
}
+1
View File
@@ -2,6 +2,7 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"noImplicitAny": false,
"baseUrl": ".",
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
@@ -74,17 +74,25 @@ const CustomLegend = (props: any) => {
);
};
// chartData의 모든 값 중 최대값 구하기
const getGlobalMax = (data: Record<string, number>[]) => {
if (!data || data.length === 0) return 0;
return Math.max(
...data.flatMap(item =>
Object.entries(item)
.filter(([key]) => key !== 'label')
.map(([, value]) => value as number)
)
);
};
export default function Chart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) {
const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#FED16A", "#FFEEAD", "#D4A5A5"];
const getMaxValue = (data: any[], keyPrefix: string) => {
return Math.max(...data.map(item =>
Object.keys(item)
.filter(key => key.includes(keyPrefix))
.map(key => item[key])
).flat());
};
const barMaxValue = getMaxValue(chartData, 'barVal');
const globalMax = getGlobalMax(chartData);
const tickCount = 5;
// interval을 globalMax / tickCount로 계산
const interval = globalMax / tickCount;
const ticks = Array.from({ length: tickCount + 1 }, (_, i) => Math.round(interval * i));
return (
<>
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
@@ -93,8 +101,8 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle='
</div>): null}
<div className="flex justify-center">
<ChartContainer config={chartConfig ?? {}} className={chartClassName + " !w-[90%]"}>
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<BarChart accessibilityLayer data={chartData} className="!h-[88%]">
<CartesianGrid vertical={false} className="!h-[85%]" />
<XAxis
dataKey="label"
tickLine={true}
@@ -106,12 +114,12 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle='
<YAxis
tickLine={true}
tickMargin={10}
tickCount={6}
axisLine={true}
fontSize={12}
fontWeight={500}
domain={[0, Math.ceil(barMaxValue * 1)]}
label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: 20 } : undefined}
domain={[0, globalMax]}
ticks={ticks}
label={yFieldText ? { value: yFieldText, className:"text-[15px] font-bold", position: 'insideLeft', angle: -90, offset: -30 } : undefined}
/>
<ChartTooltip cursor={false} content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />}/>
{chartData && chartData.length > 0 && (
@@ -6,62 +6,100 @@ import { format } from "date-fns";
import { CalendarIcon } from "lucide-react";
import type { DayPickerSingleProps } from "react-day-picker";
import { Button, Calendar, Popover, PopoverContent, PopoverTrigger } from "@common/components/ui";
import {
Button,
Calendar,
Popover,
PopoverContent,
PopoverTrigger,
} from "@common/components/ui";
import { cn } from "../../lib/utils";
import { FormControl } from "../form";
import { ko } from "date-fns/locale";
import { useState } from "react";
type DatePickerProps = DayPickerSingleProps & { field: { value: Date; onChange: (date: Date | undefined) => void }, selectClass: string | undefined, readOnly?: boolean };
type DatePickerProps = DayPickerSingleProps & {
field: { value: Date; onChange: (date: Date | undefined) => void };
selectClass: string | undefined;
readOnly?: boolean;
};
export function DatePicker({
field,
selectClass,
readOnly = false,
...props
}: DatePickerProps) {
const [open, setOpen] = useState(false);
export function DatePicker({ field, selectClass, readOnly = false, ...props }: DatePickerProps) {
const [open, setOpen] = useState(false);
const handleSelect = (date: Date | undefined) => {
if (!readOnly) {
// readOnly가 아닐 때만 변경 허용
field.onChange(date); // React Hook Form 업데이트
setOpen(false);
}
};
const handleSelect = (date: Date | undefined) => {
if (!readOnly) { // readOnly가 아닐 때만 변경 허용
field.onChange(date); // React Hook Form 업데이트
setOpen(false);
}
};
return (
<Popover
open={open && !readOnly}
onOpenChange={readOnly ? undefined : setOpen}
>
<PopoverTrigger asChild>
<FormControl>
<Button
color="white"
variant={"outline"}
className={
!selectClass
? cn(
"w-[240px] pl-3 text-left font-normal",
!field?.value && "text-muted-foreground",
return (
<Popover open={open && !readOnly} onOpenChange={readOnly ? undefined : setOpen}>
<PopoverTrigger asChild>
<FormControl>
<Button
color="white"
variant={"outline"}
disabled={readOnly} // readOnly일 때 버튼 비활성화
className={!selectClass ? cn(
"w-[240px] pl-3 text-left font-normal",
!field?.value && "text-muted-foreground",
readOnly && "opacity-60 cursor-not-allowed" // readOnly 스타일 추가
) : cn(selectClass, readOnly && "opacity-60 cursor-not-allowed")}
>
{field?.value ? (
format(
field.value instanceof Date && !isNaN(field.value.getTime())
? field.value
: new Date()
//감리 지적사항으로 삭제 예정 리드온리는 리드온리 로만 보일수 있도록 수정
// disabled={readOnly} // readOnly일 때 버튼 비활성화
//readOnly && "opacity-60 cursor-not-allowed" 감리 지적사항으로
)
: cn(
selectClass,
// , readOnly && "opacity-60 cursor-not-allowed"
)
}
>
{field?.value ? (
format(
field.value instanceof Date && !isNaN(field.value.getTime())
? field.value
: new Date(),
, "PPP", { locale: ko })
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="h-4" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field?.value}
onSelect={handleSelect}
initialFocus
locale={ko}
/>
</PopoverContent>
</Popover>
);
"PPP",
{ locale: ko },
)
) : (
<span>날짜를 선택해주세요 </span>
)}
<CalendarIcon className="h-4" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
className="w-full"
//-- 데이트 피커가 드롭박스 형태로 년월을 선택할수 있도록 하는 Props 로 활성화 되면 드롭박스로 선택
// captionLayout="dropdown-buttons"
// fromYear={2000}
// toYear={new Date().getFullYear()}
toDate={new Date()} // 금일까지만 표시됨 (금일 이후는 안보임)
showOutsideDays={false} // 금월 까지만 표출되도록
mode="single"
selected={field?.value}
onSelect={handleSelect}
initialFocus
locale={ko}
/>
</PopoverContent>
</Popover>
);
}
@@ -0,0 +1,46 @@
import "../date_picker/date-picker.css";
import { format } from "date-fns";
import { CalendarIcon } from "lucide-react";
import type { DayPickerSingleProps } from "react-day-picker";
import { cn } from "../../lib/utils";
import { Button } from "@common/components/ui";
import { Calendar } from "@common/components/ui";
import { Popover, PopoverContent, PopoverTrigger } from "@common/components/ui";
type DatePickerRangeProps = {
placeholder?: string;
disabled?: boolean | ((date: Date) => boolean);
} & DayPickerSingleProps;
export function DatePickerRange({
className,
placeholder = "날짜 선택",
disabled,
...props
}: DatePickerRangeProps) {
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn("date-picker-trigger", className)}
disabled={typeof disabled === "boolean" ? disabled : false}
>
<CalendarIcon className="!size-[1.6rem]" />
{props.selected ? (
format(props.selected, "yyyy-MM-dd")
) : (
<span>{placeholder}</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar initialFocus classNames={{ cell: "rounded-md" }} {...props}
disabled={typeof disabled === "function" ? disabled : undefined}
/>
</PopoverContent>
</Popover>
);
}
@@ -21,4 +21,5 @@ export * from "./table_div";
export * from "./title";
export * from "./sdmInsertForm";
export * from "./popover_ui";
export * from "./date_month_picker";
export * from "./date_month_picker";
export * from "./date_picker_range"
@@ -1,11 +1,10 @@
import React from 'react';
import { FormField } from "../form";
import { SelectBox } from "../select";
import { Input } from "@common/components";
import { FormUi } from "../form_ui";
import { useCallback, useEffect, useState } from "react";
import { Input } from "@common/components";
import { AsYouType } from "libphonenumber-js";
import React, { useCallback, useEffect, useState } from 'react';
import type { ControllerRenderProps, UseFormReturn } from "react-hook-form";
import { FormField } from "../form";
import { FormUi } from "../form_ui";
import { SelectBox } from "../select";
import { SdmSearchForm } from "./sdmNameBox";
// 수요처 정보
@@ -54,7 +53,6 @@ interface FormSchema {
daddr: string;
telno: string;
bznmTotNo?: string;
rgtr?: string;
dmdAplyPswd?: string;
dmdAplyPswdConfirm?: string;
}
@@ -301,7 +301,7 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel
resetAllValues();
// 리셋 후 트리거를 false로 설정
if (setRtnPartValue) {
setRtnPartValue('0');
setRtnPartValue('A');
}
}
}, [resetTrigger]);
@@ -1,57 +1,103 @@
// selectBox
import { Label } from "@common/components";
import React from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./lib/selectBox";
import React, { useEffect, useState } from "react";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "./lib/selectBox";
/** 차트 기본 스타일 */
interface SelectBox1 {
text:string,
val:string|number,
hidden?: boolean
text: string;
val: string | number;
hidden?: boolean;
}
const SelectBox = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & {
label:string,
selectClass:string|undefined,
className:string,
selectArray:SelectBox1[],
onSelectValue:(val:string|number)=>void,
value?: string|number,
readOnly?: boolean // readOnly 속성 추가
label: string;
selectClass: string | undefined;
className: string;
selectArray: SelectBox1[];
onSelectValue: (val: string | number) => void;
value?: string | number;
readOnly?: boolean; // readOnly 속성 추가
}
>(({ label, className, selectClass, selectArray, onSelectValue, value, readOnly = false }, ref)=>{
// hidden이 true인 첫 번째 항목을 placeholder로 사용
const hiddenItem = selectArray.find(item => item.hidden);
const placeHolder = hiddenItem?.text ?? "선택";
// hidden이 true인 항목만 드롭다운에서 제외
const visibleItems = selectArray.filter(item => !item.hidden);
>(
(
{
label,
className,
selectClass,
selectArray,
onSelectValue,
value,
readOnly = false,
},
ref,
) => {
// hidden이 true인 첫 번째 항목을 placeholder로 사용
const hiddenItem = selectArray.find((item) => item.hidden);
const placeHolder = hiddenItem?.text ?? "선택";
const [isOpen, setIsOpen] = useState(false);
// hidden이 true인 항목만 드롭다운에서 제외
const visibleItems = selectArray.filter((item) => !item.hidden);
// 현재 값이 hidden 항목이거나 빈 값인 경우 undefined로 설정하여 placeholder가 표시되도록 함
const currentValue = value !== undefined &&
hiddenItem &&
(String(value) === String(hiddenItem.val) || value === "" || value === "0" || value === 0)
? undefined
: (value !== undefined ? String(value) : undefined);
// 현재 값이 hidden 항목이거나 빈 값인 경우 undefined로 설정하여 placeholder가 표시되도록 함
const currentValue =
value !== undefined &&
hiddenItem &&
(String(value) === String(hiddenItem.val) ||
value === "" ||
value === "0" ||
value === 0)
? undefined
: value !== undefined
? String(value)
: undefined;
return (
<div className={className+" flex items-center"} ref={ref}>
{label && <Label className="whitespace-nowrap w-20 text-xl text-gray-600">{label}</Label>}
<Select onValueChange={onSelectValue} value={currentValue} disabled={readOnly}>
<SelectTrigger className={selectClass?selectClass:"flex-1 p-2 border rounded"}>
<SelectValue placeholder={placeHolder} />
</SelectTrigger>
<SelectContent>
{visibleItems.map((selectArray) => (
<SelectItem key={selectArray.val} value={String(selectArray.val)}>{selectArray.text}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)
})
return (
<div className={className + " flex items-center"} ref={ref}>
{label && (
<Label className="w-20 whitespace-nowrap text-xl text-gray-600">
{label}
</Label>
)}
<Select
open={isOpen}
onOpenChange={(open) => {
if (readOnly === true) {
// readOnly 일경우 Select 선택안하게 하기
setIsOpen(false);
} else {
setIsOpen(open);
}
}}
onValueChange={onSelectValue}
value={currentValue}
// disabled={readOnly} //물재이용 감리 지적사항으로 일단 주석처리
>
<SelectTrigger
className={selectClass ? selectClass : "flex-1 rounded border p-2"}
>
<SelectValue placeholder={placeHolder} />
</SelectTrigger>
<SelectContent>
{visibleItems.map((selectArray) => (
<SelectItem key={selectArray.val} value={String(selectArray.val)}>
{selectArray.text}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
},
);
export { SelectBox };
@@ -130,7 +130,13 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
)
))}
</TableRow>
)) : <></>}
)) : (
<TableRow>
<TableCell colSpan={headlist.filter((item) => item.visible != false).length} className="h-24 text-center text-gray-500">
조회된 결과가 없습니다.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
{total <= 0 ? <></> : <UiPageNations total={total} cur={cur} pageSize={pageSize} onPageChange={handlePageChange} />}
+10261
View File
File diff suppressed because it is too large Load Diff