Merge remote-tracking branch 'origin/iipbs/wr/dev' into iipbs/wr/gis

This commit is contained in:
swcho
2025-07-01 10:37:53 +09:00
52 changed files with 2284 additions and 1038 deletions
+173 -45
View File
@@ -1,30 +1,162 @@
"use client";
import { useWrStore } from "@/app/store";
import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList";
import { getCity } from "@/components/lib/func/commom";
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 { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui";
import {
ContentsDiv,
filePickedBlobArray,
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 } from "@common/components/ui";
import { useState } from "react";
export default function RuseCaseMng() {
const { code,ruseCase} = useWrStore((state) => state);
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state);
const { ruseCaseMngList } = ruseCaseMng;
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>([]);
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [selectUtz, setSelectUtz] = useState<string[] | []>()
const [searchTxt, setSearchTxt] = useState<string>()
const [sidoCd, setSidoCd] = useState<string>("");
const [sigunguCd, setSigunguCd] = useState<string>("");
const [selectUtz, setSelectUtz] = useState<string[] | []>(["조경용수"]);
const [name, setName] = useState<string>("");
// 물재이용사례 조회
const searchRuseCaseMng = async () => {
const res = await selectRuseCaseMng(
sidoCd ?? "",
sigunguCd ?? "",
selectUtz[0] ?? "",
name ?? ""
);
}
// 시도 선택
const getSelCity = (val: string | number) => {
const cityValue = String(val);
const cityText = code.cityCd.find((item: { val: string; text: string }) => item.val === cityValue)?.text;
setSidoCd(cityText || "");
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: '' });
setObjSearch({ city: cityValue, sgg: "" });
};
// 시군구 선택
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) })
const sggValue = String(val);
const sggText = selectSgg?.find((item: { val: string; text: string }) => item.val === sggValue)?.text;
setSigunguCd(sggText || "");
setObjSearch({ ...objSearch, sgg: String(val) });
};
// 엑셀 등록 모달
const [isExcelOpen, setIsExcelOpen] = useState<{
isModal: boolean;
}>({ isModal: false });
// 다운로드
const download = () => {
searchRuseCaseMng();
fetch(`/sample_ruseCaseMng.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = ruseCaseMngList.map((item) => {
return [
item.ruseBizNm,
item.bizBgngDate,
item.bizEndDate,
item.splyFcltKnd,
item.splyFcltNm,
item.splyFcltCpct,
item.dmdFcltNm,
item.dmdFcltAddr,
item.ruseFcltCpct,
item.ruseFcltPplLen,
item.ruseFcltUsg,
item.ruseFcltSplyCpct,
item.ruseFcltPrcsMsrc,
item.grnteWqUsg,
item.grnteWqGrssClfg,
item.grnteWqCombResdch,
item.grnteWqTrbt,
item.grnteWqBod,
item.grnteWqSmll,
item.grnteWqCrmty,
item.grnteWqTn,
item.grnteWqTp,
item.grnteWqPh,
item.grnteWqClIonQnt,
item.grnteWqCndctStat,
item.grnteWqRmrk,
item.reqBizGrssCst,
item.reqBizReuseFcltCst,
item.reqBizPplCst,
item.reqBizArchCst,
item.reqBizMchnCst,
item.reqBizMtrngCst,
item.reqBizGisCst,
item.reqBizCctvCst,
item.reqBizCmcnCst,
item.reqBizWstCst,
item.reqBizDesignCst,
item.reqBizSpvsCst,
item.reqBizSbsdCst,
item.reqBizEtc,
item.prcmPlanGrssCst,
item.prcmPlanNlep,
item.prcmPlanLcep,
item.prcmPlanBrdnCst,
item.prcmPlanEcqrRt,
item.clclnGrssCst,
item.clclnNlep,
item.clclnLcep,
item.clclnBrdnCst,
item.clclnEcqrRt,
];
});
const sheetParameter = [
{
data: excelData1,
headerNum: 2,
headerRange: 'A1:AX1',
sheetName: '물재이용사례',
startCol: 'A'
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter);
});
}
// 양식받기
const sampleDownLoad = () => {
excelDownload(
[[]],
"물재이용사례 관리",
"sample_ruseCaseMng.xlsx",
"A1:AX1",
2
);
}
return (
<>
<ContentsDiv>
@@ -54,47 +186,32 @@ export default function RuseCaseMng() {
selectClass={""}
selectArray={ruseUtzType}
onSelectValue={(val) => {
if(val)
setSelectUtz([String(val)])}}
if (val) setSelectUtz([String(val)]);
}}
/>
<SearchInput
className={"text-2xl"}
label="수요처명"
textValue={""}
textValue={name}
setText={(e) => {
setSearchTxt(e)
setName(e);
}}
/>
</div>
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2">
<div className="space-y">
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { searchRuseCaseMng() }}>
조회
</Button>
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { setIsExcelOpen({ isModal: true }); }}>
엑셀등록
</Button>
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { download() }}>
다운로드
</Button>
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { sampleDownLoad() }}>
양식받기
</Button>
</div>
@@ -103,24 +220,35 @@ export default function RuseCaseMng() {
</SearchDiv>
</div>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="grid grid-cols-6 gap-4">
<div className="col-span-6">
<ListDiv className="overflow-x-auto">
<UiTable
headName={"물재이용사례 현황"}
tableData={[]}
children={undefined}
headlist={ruseCase.ruseHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
}}
onPageChange={() => { }}
></UiTable>
<div className="w-[350%]">
<UiTable
headName={"물재이용사례 현황"}
tableData={ruseCaseMngList}
children={undefined}
headlist={ruseCaseMng.ruseHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {}}
onPageChange={() => {}}
/>
</div>
</ListDiv>
</div>
</div>
</ContentsDiv>
{/* 엑셀 등록 모달 */}
<UiModal
open={isExcelOpen.isModal}
onOpenChange={() => {
setIsExcelOpen({ isModal: !isExcelOpen.isModal })
}}
bodyClass={"w-[40vw] max-w-none"}
body={<RuseCaseMngExcel />}
/>
</>
)
);
}
+118 -22
View File
@@ -1,32 +1,68 @@
"use client";
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type { customCoSttnBdst } from "@/app/store/matDmd/coSttn";
import type { customDrgt } from "@/app/store/matDmd/drgtSttn";
import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList";
import { excelDownload, getCity } from "@/components/lib/func/commom";
import DrgtDetailModal from "@/components/ui/modal/coMat/drgtDetailModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui";
import { ContentsDiv, DateMonthPicker, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui";
import { MonthPicker } from "@common/business_components/ui/date_month_picker/mothPicker";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, DatePicker, Label } from "@common/components/ui";
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { toast, Toaster } from "react-hot-toast";
export default function DrgtSttn() {
const { code, } = useWrStore((state) => state);
const [fromDate, setFromDate] = useState<Date>(new Date());
const [toDate, setToDate] = useState<Date>(new Date());
const { code, drgt, drgtSearch, drgtModalSearch } = useWrStore((state) => state);
const { drgtRes, drgtMoRes } = drgt;
const [fromDate, setFromDate] = useState<string>();
const [toDate, setToDate] = useState<string>();
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>();
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [tableData, setTableData] = useState<customDrgt[]>();
const [selectedSgg, setSelectedSgg] = useState<string>('');
const [drgtModal, setDrgtModal] = useState<{
isOpen: boolean;
data: customDrgt | undefined;
}>({ data: undefined, isOpen: false });
const getSelCity = (val: string | number) => {
const cityValue = String(val); // Ensure val is treated as a string
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: '' });
const cellClick = (index: number | undefined, headId: string) => {
if (fromDate && toDate) {
drgtModalSearch(fromDate, toDate, drgtRes[index].sggNm);
} else {
toast.error("조회기간을 선택해주세요.");
}
}
useEffect(() => {
if (drgtMoRes && drgtMoRes.length > 0) {
setDrgtModal({ isOpen: true, data: drgtMoRes });
}
}, [drgtMoRes]);
const getCityVal = (val: string, sggCd: SelectDataType[]) => {
const data = sggCd
.filter((item) => String(item.val).substring(0, 2) === val)
.map((item) => ({
val: item.val,
text: item.text, // 한국어 텍스트
}));
return data;
};
const getSelCity = (val: string | number) => {
const cityValue = String(val);
const data = getCityVal(cityValue, code.sggCd);
setSelectSgg(data);
const sggVal = data.length > 0 ? data[0].val : '';
setSelectedSgg(sggVal); // 시군구 value 세팅
setObjSearch({ city: cityValue, sgg: sggVal });
};
const getSgg = (val: string | number) => {
console.log(val);
setObjSearch({ ...objSearch, sgg: String(val) })
setSelectedSgg(String(val));
setObjSearch({ ...objSearch, sgg: String(val) });
};
const excelClick = useCallback(() => {
@@ -37,6 +73,17 @@ export default function DrgtSttn() {
"A1:H1"
);
}, []);
useEffect(() => {
if (fromDate === undefined && toDate === undefined) {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const formatted = `${year}${month}`;
setFromDate(formatted);
setToDate(formatted);
}
}, [fromDate, toDate])
return (
<>
<ContentsDiv>
@@ -59,18 +106,41 @@ export default function DrgtSttn() {
selectClass={""}
selectArray={selectSgg || []}
onSelectValue={getSgg}
value={selectedSgg}
/>
<div className="col-span-5 w-auto">
<div className="flex flex-row items-center">
<Label className="text-xl text-gray-600">조회기간</Label>
<div className="flex flex-row w-1/4">
<DatePicker mode={'single'}
selected={fromDate}
onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} />
<DateMonthPicker
field={{
value: fromDate,
onChange: (date: string) => {
const year = date.slice(0, 4);
let month = date.slice(4);
if (month.length < 2) {
month = month.padStart(2, "0");
}
const formatted = `${year}${month}`;
setFromDate(formatted);
}
}}
/>
<span>~</span>
<DatePicker mode={'single'}
selected={toDate}
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
<DateMonthPicker
field={{
value: toDate,
onChange: (date: string) => {
const year = date.slice(0, 4);
let month = date.slice(4);
if (month.length < 2) {
month = month.padStart(2, "0");
}
const formatted = `${year}${month}`;
setToDate(formatted);
}
}}
/>
</div>
</div>
</div>
@@ -81,7 +151,22 @@ export default function DrgtSttn() {
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
onClick={(e) => { }}
onClick={(e) => {
let sidoText = '';
let sggText = '';
if (objSearch?.city.toString().length > 0) {
sidoText = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? '';
}
if (objSearch?.sgg.toString().length > 1) {
sggText = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
}
if (sidoText === '세종특별자치시') {
sidoText = '세종시';
sggText = sidoText;
}
drgtSearch(fromDate ?? "", toDate ?? "", sidoText, sggText)
}}
>
조회
</Button>
@@ -116,16 +201,27 @@ export default function DrgtSttn() {
<ListDiv>
<UiTable
headName={[]}
tableData={[]}
tableData={drgtRes || []}
headlist={drgtHeadListExport}
pageSize={20}
total={10000}
cellClick={(index: number | undefined, headId: string) => { }}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index ?? undefined, headId);
}}
onPageChange={() => { }}
></UiTable>
</ListDiv>
</div>
</div>
<UiModal
open={drgtModal.isOpen}
onOpenChange={() => {
setDrgtModal({ ...drgtModal, isOpen: !drgtModal.isOpen });
}}
bodyClass={"w-[50vw] max-w-none h-[80vh]"}
body={<DrgtDetailModal data={drgtModal.data} /*formObj={drgtModal.data}*/ />}
/>
<Toaster position="top-center" />
</ContentsDiv>
</>
)
@@ -99,8 +99,8 @@ export default function LdrmntCoSttn() {
const tbData = Array.isArray(data)
? data.map((item: customLdrmnt) => ({
...item,
ctpvCd: item.fctryAddr.split(" ")[0] || "",
sggCd: item.fctryAddr.split(" ")[1] || ""
ctpvCd: item.fctryAddr?.split(" ")[0] || "",
sggCd: item.fctryAddr?.split(" ")[1] || ""
}))
: [];
@@ -114,8 +114,8 @@ export default function LdrmntCoSttn() {
const data = Array.isArray(exData)
? exData.map((item: customLdrmnt) => {
return {
"시도": item.fctryAddr.split(" ")[0] || "",
"시군구": item.fctryAddr.split(" ")[1] || "",
"시도": item.fctryAddr?.split(" ")[0] || "",
"시군구": item.fctryAddr?.split(" ")[1] || "",
"회사명": item.dmdCoNm,
"공장주소": item.fctryAddr,
"공장관리번호": item.fctryMngNo,
+180 -92
View File
@@ -29,9 +29,9 @@ import {
import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm";
import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm";
import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal";
import toast, { Toaster } from "react-hot-toast";
import toast, { Toaster } from "react-hot-toast"; // toast 이미 import 되어 있음
import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg";
import { useFieldArray, useForm } from "react-hook-form";
import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
@@ -39,8 +39,8 @@ export default function PublicReuseFaciltyInsert() {
const {
code,
pbnst,
searchFclts,
pubHasuInit,
pbnstInsert,
pbnstRegClick,
pbnstRuseFcltUpdate,
pubHasuSearch,
@@ -50,17 +50,15 @@ export default function PublicReuseFaciltyInsert() {
const [currentTab, setCurrentTab] = useState<string>("mng");
const [isRuseReg, setIsRuseReg] = useState<{
isModal: boolean;
data: pubHasuType;
}>({ isModal: false, data: {} as pubHasuType });
}>({ isModal: false });
const [indexMng, setIndexMng] = useState<number>(0);
const [indexBiz, setIndexBiz] = useState<number>(0);
const [searchTxt, setSearchTxt] = useState<string>();
const [bdstTable, setbdstTable] = useState<pubHasuType[]>();
const [selectRow, setSelectRow] = useState<pubHasuType>();
const [pageNation, setPageNation] = useState<number>(1);
const form = useForm<{
items: z.infer<typeof pbnstRegZod>[];
}>({
@@ -76,6 +74,8 @@ export default function PublicReuseFaciltyInsert() {
control: form.control,
name: `items`,
});
const formWatch = form.watch("items");
useEffect(() => {
if (!pbnst.isInit) {
pubHasuInit("1", "41");
@@ -88,32 +88,84 @@ export default function PublicReuseFaciltyInsert() {
data.forEach((fclt) => {
bizs = [...bizs, ...fclt.fcBizs];
});
const pbnst =data.map((fclt) => {
return({
pbnstRuseFcltPk: fclt.pbnstRuseFcltPk,
fcltCpct: fclt.fcltCpct,
ruseCpct: fclt.ruseCpct,
splyExtraCpct: fclt.splyExtraCpct,
rgtr: fclt.rgtr,
mdfr: fclt.mdfr,
fcFiles: fclt.fcFiles,
fcOpers: fclt.fcOpers,
})
})
const pbnst = data.map((fclt) => {
return {
pbnstRuseFcltPk: fclt.pbnstRuseFcltPk,
fcltCpct: fclt.fcltCpct,
ruseCpct: fclt.ruseCpct,
splyExtraCpct: fclt.splyExtraCpct,
rgtr: fclt.rgtr,
mdfr: fclt.mdfr,
fcFiles: fclt.fcFiles,
fcOpers: fclt.fcOpers,
};
});
await pbnstRuseFcltUpdate(pbnst as customPbnst[]);
await pbnstRuseBizUpdate(bizs);
};
useEffect(() => {
if (code.sggCd.length) {
if (searchFclts) {
setbdstTable(tableData(searchFclts, { code }));
} else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) {
setbdstTable(tableData(pbnstHasuRes, { code }));
}
// 폼 유효성 검사 실패 시 호출될 함수
const onInvalid = (
errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>
) => {
// 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다.
const findAllErrors = (
errorObject: any,
path: (string | number)[] = []
): { message: string; path: (string | number)[] }[] => {
if (!errorObject) return [];
return Object.keys(errorObject).flatMap((key) => {
const value = errorObject[key];
const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key;
const newPath = [...path, currentKey];
if (value && typeof value === "object") {
if (typeof value.message === "string") {
return [{ message: value.message, path: newPath }]; // 에러 메시지와 경로 반환
}
return findAllErrors(value, newPath);
}
return [];
});
};
const errorList = findAllErrors(errors);
if (errorList.length > 0) {
errorList.forEach((errorInfo) => {
const { message, path } = errorInfo;
let prefix = "";
const itemsIndex = path.indexOf("items");
if (itemsIndex > -1 && path.length > itemsIndex + 1) {
const itemIdx = path[itemsIndex + 1] as number;
const facilityName = form.getValues(
`items.${itemIdx}.ruseUtztnFcltNm`
);
if (facilityName) {
prefix += `[${facilityName}] `;
}
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`
);
if (cycle) {
prefix += `[${cycle}차수] `;
}
}
}
toast.error(`${prefix}${message}`);
});
}
}, [code, pbnstHasuRes, searchFclts]);
};
useEffect(() => {
if (code.sggCd.length && pbnstHasuRes.length > 0) {
setbdstTable(tableData(pbnstHasuRes, { code }));
}
}, [code, pbnstHasuRes]);
const cellClick = (val: number | undefined) => {
if (val === undefined) return;
@@ -121,6 +173,8 @@ export default function PublicReuseFaciltyInsert() {
setSelectRow(pbnstHasuRes?.[val]);
if (selectRow) {
pbnstRegClick(selectRow);
} else {
console.error("선택된 행이 없습니다.");
}
};
@@ -129,12 +183,9 @@ export default function PublicReuseFaciltyInsert() {
};
useEffect(() => {
if (pbnst.pbnstRes&& pbnst.pbnstRes?.length>0) {
console.log("pbnst.pbnstRes", pbnstRes);
if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) {
form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) });
}else{
console.log("pbnst.pbnstRes is null", pbnstRes);
} else {
form.reset({ items: [] });
}
}, [pbnst.pbnstRes]);
@@ -150,11 +201,9 @@ export default function PublicReuseFaciltyInsert() {
const appendPbnstRuseFclt = useCallback(() => {
const selecTableRow = selectRow;
if (pbnstRes && pbnstRes.length > 0) {
const lastPbnst = pbnstRes[pbnstRes.length - 1];
if (lastPbnst && selecTableRow) {
append(
pbnstRegZod.parse({
pbnstRuseFcltPk:
@@ -170,32 +219,33 @@ export default function PublicReuseFaciltyInsert() {
] as fcBizType[],
})
);
setIsRuseReg({ ...isRuseReg, isModal: true });
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[],
})
)
setIsRuseReg({ ...isRuseReg, isModal: true });
pbnstRegZod.parse({
pbnstRuseFcltPk: String(selecTableRow.bscFctCd) + "1",
ruseUtztnFcltNm: "",
regDt: null,
mdfcnDt: null,
fcBizs: [
fcBizSchema.parse({
pbnstRuseFcltFk: String(selecTableRow.bscFctCd) + "1",
cycl: 1,
}),
] as fcBizType[],
})
);
setIsRuseReg({ isModal: true });
}
}
}, [pbnstRegZod, pbnstRes, append, selectRow, form]);
useEffect(() => {
// console.log(" page getgetvaslue ", form.getValues());
}, [form.getValues()]);
pubHasuInit(String(pageNation), "41");
}, [pageNation, pubHasuInit]);
useEffect(() =>{
pubHasuInit(String(pageNation), "41");
},[pageNation,pubHasuInit])
return (
<>
<ContentsDiv>
@@ -206,7 +256,7 @@ useEffect(() =>{
<SearchFormLeft>
<SearchInput
className={"col-span-3 my-7 !text-3xl"}
label="건물명"
label="시설명"
textValue={searchTxt || ""}
setText={(e) => {
setSearchTxt(e);
@@ -228,7 +278,7 @@ useEffect(() =>{
size={"sm"}
className=""
onClick={() => {
// pbnstMngData([] as customPbnst[]);
// pbnstMngData([] as customPbnst[]);
if (!selectRow) {
toast.error(
"하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요"
@@ -251,6 +301,8 @@ useEffect(() =>{
<div className="col-span-4 h-full min-h-0">
<ListDiv className="min-h-0">
<ScrollArea className="h-[60vh]">
{" "}
{/* 높이 조정 */}
<UiTable
tableData={bdstTable || []}
headlist={pubFacHeadList}
@@ -266,59 +318,95 @@ useEffect(() =>{
</div>
<ListDiv className="col-span-2 pr-6 ">
<ScrollArea className="h-[60vh]">
<Tabs defaultValue="mng" onValueChange={handleTabChange}>
<TabsList className="">
<TabsTrigger className="!text-xl" value="mng">
시설정보
</TabsTrigger>
<TabsTrigger className="!text-xl" value="buse">
차수별정보
</TabsTrigger>
<Button
{formWatch && formWatch[0]?.pbnstRuseFcltPk ? (
<Tabs defaultValue="mng" onValueChange={handleTabChange}>
<TabsList className="">
<TabsTrigger className="!text-xl" value="mng">
시설정보
</TabsTrigger>
<TabsTrigger className="!text-xl" value="buse">
차수별정보
</TabsTrigger>
<Button
size={"sm"}
onClick={form.handleSubmit(onSubmit, onInvalid)}
className="!justify-end" // 저장 버튼 위치 조정
>
저장
</Button>
</TabsList>
<TabsContent className="max-h-full" value="mng">
<PbnstMngForm
formType="update"
form={form}
setIndexMng={(index: number) => {
console.log("setIndexMng", index);
setIndexMng(index);
}}
indexMng={indexMng}
/>
</TabsContent>
<TabsContent className="max-h-full" value="buse">
<PbnstBuseForm
formType="update"
form={form}
setIndexBiz={(index: number) => {
setIndexBiz(index);
}}
indexMng={indexMng}
indexBiz={indexBiz}
/>
</TabsContent>
</Tabs>
) : (
<>
<div className="card-header text-2xl">
등록된 재이용시설 정보가 없습니다.
</div>
<div className="!mx-[1.6rem] !p-0r flex flex-col items-center justify-center h-64 border rounded-md">
<p className="text-3xl mb-4">
신규등록 버튼을 클릭하여 재이용시설을 등록해주세요.
</p>
<Button
size={"sm"}
onClick={form.handleSubmit(onSubmit)}
className="!justify-end"
className=""
onClick={() => {
// pbnstMngData([] as customPbnst[]);
if (!selectRow) {
toast.error(
"하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요"
);
return;
} else {
appendPbnstRuseFclt();
}
}}
>
저장
신규등록
</Button>
</TabsList>
<TabsContent className="max-h-full" value="mng">
<PbnstMngForm
formType="update"
form={form}
setIndexMng={(index: number) => {
console.log("setIndexMng", index);
setIndexMng(index);
}}
indexMng={indexMng}
/>
</TabsContent>
<TabsContent className="max-h-full" value="buse">
<PbnstBuseForm
formType="update"
form={form}
setIndexBiz={(index: number) => {
setIndexBiz(index);
}}
indexMng={indexMng}
indexBiz={indexBiz}
/>
</TabsContent>
</Tabs>
</div>
</>
)}
</ScrollArea>
</ListDiv>
<UiModal
body={
<PbnstRegModal
form={form}
data={isRuseReg.data || ({} as customPbnst)}
form={form} // form prop 전달
setPbnstInsert={(data) => {
if (data) {
pbnstInsert(data); // pbnstInsert 함수 호출
setIsRuseReg({ isModal: false });
}
}}
></PbnstRegModal>
}
bodyClass="w-[68vw] max-w-none h-[90vh]"
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={isRuseReg.isModal}
onOpenChange={(open) => {
remove(form.getValues("items").length - 1);
setIsRuseReg({ data: {} as pubHasuType, isModal: open });
setIsRuseReg({ isModal: open });
}}
/>
</div>
@@ -38,8 +38,10 @@ export default function Home() {
}>({ isOpen: false, chgBuilding: [] });
useEffect(() => {
setIsNewReg( false)
if (!rain.isInit) {
initRain('41');
}
}, [rain.isInit, initRain]);
@@ -80,7 +82,6 @@ export default function Home() {
};
useEffect(() => {
console.log("getPageNation", pageNation);
getRainData(pageNation, "41");
}, [pageNation]);
@@ -42,8 +42,8 @@ export default function Home() {
}
}, [rswt.isInit, initRswt]);
useEffect(() => {
if (code.sggCd.length > 0) {
const rswtData = tableData(rswtRes, { code }).map(
if (code.sggCd.length > 0 && rswtRes?.length > 0) {
const rswtData = tableData(rswtRes, { code })?.map(
(item: customRswtBdst) => {
return {
...item,
@@ -151,7 +151,7 @@ export default function Home() {
</div>
<UiModal
body={<BdstReg moType="RST"></BdstReg>}
body={<BdstReg moType="RST" setIsModal={setIsNewReg}></BdstReg>}
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={issNewReg}
onOpenChange={(open) => {
@@ -162,7 +162,7 @@ export default function Home() {
bodyClass={""}
open={bdstChgProps.isOpen}
body={
<BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal>
<BdstChgModal chgBuilding={bdstChgProps.chgBuilding} ></BdstChgModal>
}
onOpenChange={(open) => {
setBdstChgProps({ ...bdstChgProps, isOpen: open });
@@ -65,7 +65,7 @@ export default function RswtSttn() {
}, [rswtSttn.isInit, rswtSttnInit]);
useEffect(() => {
if (rswtSttnRes.length > 0 && code.cityCd.length > 0) {
if (rswtSttnRes?.length > 0 && code.cityCd.length > 0) {
const data = tableData(rswtSttnRes, { code });
const chData = data
.map((item: customRswtBdst) => ({
@@ -68,7 +68,12 @@ export default function Page() {
data: {},
});
const cellClick = () => {
if (searchYear) rainChkInit(searchYear, "0");
if (searchYear)
if (selFcltDiv === "RNB") {
rainChkInit(searchYear, "0");
} else {
rswtChkgdnInit(searchYear, "0");
}
};
const [searchYear, setSearchYear] = useState<string>(
@@ -91,8 +96,7 @@ export default function Page() {
}, [rainChkgdn.isInit, rainChkInit]);
useEffect(() => {
if (rswtChkgdn.excelList[0]&&rswtChkgdn.excelList[0]?.data.length > 1) {
if (rswtChkgdn.excelList[0] && rswtChkgdn.excelList[0]?.data.length > 1) {
excelArrayDownload(
"중수도시설 지도점검",
"sample_rs_chk.xlsx",
@@ -189,10 +193,10 @@ export default function Page() {
label={""}
selectClass={""}
selectArray={rainChkgdn.fcltDiv}
onSelectValue={(val: string | number) =>{
if(val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val))
}
onSelectValue={(val: string | number) => {
if (val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val))
}
}
value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val}
/>
@@ -1,14 +1,10 @@
"use client";
import { useWrStore } from "@/app/store";
import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn";
import {
excelArrayDownload,
excelDownload,
yearOptions,
yearOptions
} from "@/components/lib/func/commom";
import ChkgdExcel from "@/components/ui/modal/chkgd/chkgdExcel/chkgdExcel";
import ChkgdModal from "@/components/ui/modal/chkgd/chkgdModal/chkgdModal";
import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable";
import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable";
@@ -17,29 +13,27 @@ import {
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
UiModal
} from "@common/business_components/ui";
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";
//-----------------------
export default function Page() {
const {
code,
rainChkSttn,
rswtChkSttn,
rainChkSttnInit,
rainChkSttnSearch,
rswtChkSttnSearch,
rswtChkSttnInit,
isRainSttn,
isRswtSttn
isRswtSttn,
rswtChkPolInit,
} = useWrStore((state) => {
return state;
});
@@ -51,25 +45,31 @@ export default function Page() {
});
const [searchYear, setSearchYear] = useState<string>(String(new Date().getFullYear()));
const cellClick = () => {
if (searchYear) rainChkSttnSearch(searchYear, "0");
if (searchYear) {
if (selFcltDiv === "RNB") {
rainChkSttnSearch(searchYear, "0");
} else {
rswtChkSttnSearch(searchYear, "0");
}
}
};
useEffect(() => {
if (!rainChkSttn.isInit) {
isRainSttn(true)
rainChkSttnInit( "0");
rainChkSttnInit(new Date().getFullYear().toString(), "0");
setIsModal({ data: {}, open: false });
}
}, [rainChkSttn.isInit, rainChkSttnInit]);
useEffect(() => {
useEffect(() => {
if (!rswtChkSttn.isInit) {
setIsModal({ data: {}, open: false });
rswtChkSttnInit( "0");
rswtChkSttnInit(new Date().getFullYear().toString(), "0");
isRswtSttn(true)
}
},[rswtChkSttn.isInit, rswtChkSttnInit])
}, [rswtChkSttn.isInit, rswtChkSttnInit])
const handleClick = useCallback(
@@ -78,25 +78,25 @@ export default function Page() {
},
[isModal]
);
const excelDown=() =>{
if(rainChkSttn.sttnExcelList && selFcltDiv === "RNB"){
excelDownload(
rainChkSttn.sttnExcelList,
"빗물이용시설 지도점검 현황",
"sample_rainChkSttn.xlsx",
"A1:Q2",
3
);
}else if(rswtChkSttn.sttnExcelList && selFcltDiv === "RST"){
excelDownload(
rswtChkSttn.sttnExcelList,
"중수도시설 지도점검 현황",
"sample_rswtChkSttn.xlsx",
"A1:X2",
3
);
const excelDown = () => {
if (rainChkSttn.sttnExcelList && selFcltDiv === "RNB") {
excelDownload(
rainChkSttn.sttnExcelList,
"빗물이용시설 지도점검 현황",
"sample_rainChkSttn.xlsx",
"A1:Q2",
3
);
} else if (rswtChkSttn.sttnExcelList && selFcltDiv === "RST") {
excelDownload(
rswtChkSttn.sttnExcelList,
"중수도시설 지도점검 현황",
"sample_rswtChkSttn.xlsx",
"A1:X2",
3
);
}
}
}
return (
<ContentsDiv>
@@ -125,10 +125,12 @@ const excelDown=() =>{
label={""}
selectClass={""}
selectArray={rainChkSttn.fcltDiv}
onSelectValue={(val: string | number) =>
setSelFcltDiv(String(val))
}
value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val}
onSelectValue={(val: string | number) => {
if (val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val))
}
}
value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val}
/>
</div>
</SearchFormLeft>
@@ -143,7 +145,7 @@ const excelDown=() =>{
>
조회
</Button>
<Button color="green" size={"sm"} onClick={(e) =>{excelDown()}}>
<Button color="green" size={"sm"} onClick={(e) => { excelDown() }}>
다운로드
</Button>
</div>
@@ -157,15 +159,15 @@ const excelDown=() =>{
<ListDiv>
{selFcltDiv === "RNB" ? (
<RainChkgdTable
deleteData={() => {}}
detailUp={() => {}}
deleteData={() => { }}
detailUp={() => { }}
handleClick={handleClick}
rainChkData={rainChkSttn.rainChkRes}
/>
) : (
<RswtChkgdTable
detaiRswtlUpdate={() => {}}
deleteRswtData={() => {}}
detaiRswtlUpdate={() => { }}
deleteRswtData={() => { }}
handleRswtClick={handleClick}
rswtChkData={rswtChkSttn.rswtChkRes}
/>
+34
View File
@@ -10,6 +10,32 @@ const yn: SelectDataType[] = [
{ val: 'N', text: 'N' },
]
//처리장연계/자체+연계/자체처리
const erctUswtrPrcsMeth : SelectDataType[]= [
{ val: '처리장연계', text: '처리장연계' },
{ val: '자체+연계', text: '자체+연계' },
{ val: '자체처리', text: '자체처리' },
]
const rusePrcsMthdCd : SelectDataType[] = [
{ val: '물리학적처리', text: '물리학적처리' },
{ val: '생물학적처리', text: '생물학적처리' },
]
const ruseSeCd : SelectDataType[] = [
{ val: '신규', text: '신규' },
{ val: '증설', text: '증설' },
{ val: '관로', text: '관로' },
{ val: '감량', text: '감량' },
{ val: '개량', text: '개량' },
]
const ruseBizMthdCd: SelectDataType[] = [
{ val: '재정', text: '재정' },
{ val: '민간투자', text: '민간투자' },
{ val: '자체', text: '자체' },
]
//공통 건축물 용도
const bdstUsgCd: SelectDataType[] = [
{ val: '-1', text: "확인불가" },
@@ -174,6 +200,10 @@ export type comCodeState = {
HTB:string
}
ruseMngMtd:SelectDataType[]
ruseSeCd: SelectDataType[];
ruseBizMthdCd: SelectDataType[];
rusePrcsMthdCd: SelectDataType[];
erctUswtrPrcsMeth: SelectDataType[];
};
};
@@ -323,6 +353,10 @@ export const comCodeStateExport: comCodeState = {
dmdUszCd:dmdUszCd,
buildingType:buildingType,
ruseMngMtd:ruseMngMtd,
ruseSeCd:ruseSeCd,
ruseBizMthdCd: ruseBizMthdCd,
rusePrcsMthdCd:rusePrcsMthdCd,
erctUswtrPrcsMeth:erctUswtrPrcsMeth
},
};
+46 -3
View File
@@ -7,8 +7,50 @@ import {
import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import type { conCostFeeType, rateFeeType } from "./type";
export const comFcltStateExport: comFcltState = {
searchFclts: [] as pubHasuType[],
demandCompList: [],
dmdIsSucees: '',
checkDemand: {
index: 0,
dmdInfoPk: "",
dmdInfoFk: "",
bzmnNo: "",
dmdCoNm: "",
daddr: "",
telno: "",
rgtr: "",
useUsgCd: "",
ctpvCd: "",
sggCd: "",
dmdAplyPswd: "",
wntUntprc: 0,
dmndTmng: "",
dmndQty: 0,
grssClfg: 0,
tn: 0,
combResdch: 0,
tph: 0,
trbt: 0,
ph: 0,
blgcOxygDmndQnt: 0,
clIonQnt: 0,
smll: 0,
cdtnv: 0,
crmty: 0,
rmrk: "",
reqDt: ""
},
constCostRes:[],
rateFeeRes:[],
};
export type comFcltType = comFcltState & comFcltActions;
export type comFcltState = {
searchFclts: pubHasuType;
searchFclts: pubHasuType[];
demandCompList: DemandCompType[];
dmdIsSucees: string;
checkDemand: DemandCompType;
@@ -16,7 +58,7 @@ export type comFcltState = {
rateFeeRes:rateFeeType[],
};
export type comFcltActions = {
type comFcltActions = {
getSwtrmtFcltNm: (searchNm: string) => void;
getDemandCompList: (searchNm: string, cnt: number) => void;
regDemandCompany: (data: DemandCompType) => void;
@@ -25,7 +67,7 @@ export type comFcltActions = {
getRateFee: (year: string) => void;
};
export const searchActions: (set: any) => comFcltActions = (set: any) => {
export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
return {
getSwtrmtFcltNm: async (searchNm: string) => {
const fcltNm = await getFetch(
@@ -40,6 +82,7 @@ export const searchActions: (set: any) => comFcltActions = (set: any) => {
addr: item.addr,
fctCpc: item.fctCpc,
}));
set((state: comFcltState) => ({
...state,
searchFclts: searchFclts,
+16 -17
View File
@@ -1,5 +1,19 @@
import { z } from "zod";
import type { CommonTableProps } from "../../common/type/header";
export const drgtZod = z.object({
ctpvCd: z.string().default(''),
sggNm: z.string().default(''),
normal: z.string().default(''),
attention: z.string().default(''),
caution: z.string().default(''),
warning: z.string().default(''),
ranking: z.string().default(''),
year: z.string().default(''),
month: z.string().default(''),
drgtState: z.string().default('')
})
export const drgtHeadListExport: CommonTableProps[] = [
{
id: "ctpvCd",
@@ -10,21 +24,13 @@ export const drgtHeadListExport: CommonTableProps[] = [
colSpan: 1,
},
{
id: "sggCd",
id: "sggNm",
title: "시군구",
visible: true,
row: 1,
rowSpan: 1,
colSpan: 1,
},
{
id: "haengjung",
title: "행정동",
visible: true,
row: 1,
rowSpan: 1,
colSpan: 1,
},
{
id: "normal",
title: "정상(횟수)",
@@ -83,16 +89,9 @@ export const drgtDetailHeadListExport: CommonTableProps[] = [
row: 1,
rowSpan: 1,
colSpan: 1,
},{
id: "day",
title: "일",
visible: true,
row: 1,
rowSpan: 1,
colSpan: 1,
},
{
id: "state",
id: "drgtState",
title: "가뭄상태",
visible: true,
row: 1,
+129 -9
View File
@@ -1,27 +1,147 @@
import { fetchType, getFetch } from "@/components/lib/api";
import type { CommonTableProps } from "../../common/type/header";
import { drgtHeadListExport } from "./headList";
import { drgtHeadListExport, drgtZod } from "./headList";
import type { z } from "zod";
const drgtHeadList = drgtHeadListExport;
export type customDrgt = z.infer<typeof drgtZod>;
const drgtRes: customDrgt[] = [];
export type drgtState = {
drgt:{
drgtHeadList: CommonTableProps[];
}
drgt: {
drgtHeadList: CommonTableProps[];
drgtRes: customDrgt[];
drgtMoRes: customDrgt[];
}
}
type drgtActions = {
drgtSearch: (fromDate: string, toDate: string, sido: string, sgg: string) => void
drgtModalSearch: (fromDate: string, toDate: string, sgg: string) => void
};
export type drgtType = drgtState & drgtActions;
export const drgtStateExport: drgtState = {
drgt: {
drgtHeadList: drgtHeadList,
}
drgt: {
drgtHeadList: drgtHeadList,
drgtRes: drgtRes,
drgtMoRes: drgtRes,
}
}
export const drgtActionsExport: (set: any) => drgtActions = (
set: any
) => {
return {};
};
return {
drgtSearch: async (fromDate: string, toDate: string, sido: string, sgg: string) => {
try {
let searchUrl = "";
if (sido === "" || sido === "전체") {
searchUrl = `/il/getDataCount/${fromDate}/${toDate}`;
} else {
searchUrl = `/il/getDataCount/${fromDate}/${toDate}/${sido}/${sgg}`;
}
console.log("searchUrl", searchUrl);
const drgtData = await getFetch(
fetchType.WR,
searchUrl
);
console.log("drgtRes", drgtData)
//주의, 관심, 정상 추출
const statusData = Array.from(
new Set(drgtData.map((e) => e.lvngMfgUswtrDrgtInfo))
);
const statusMap = drgtData.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => {
if (!acc[sggNm]) {
acc[sggNm] = { sggNm, ...Object.fromEntries(statusData.map((e) => [e, 0])) };
}
acc[sggNm][status] = Number(countDrgt);
return acc;
}, {});
const data = Object.values(statusMap);
const formatStatus = data.map((item) => {
const attention = item['관심'] || 0;
const caution = item['주의'] || 0;
return {
sggNm: item.sggNm,
normal: String(item['정상'] || 0),
caution: String(caution),
attention: String(attention),
warning: String(attention + caution),
};
});
const ranking = Array.from(
new Set(formatStatus.map(e => Number(e.warning)))
).sort((a, b) => b - a);
const rankData = ranking.reduce((acc, val, idx) => {
acc[val] = idx + 1;
return acc;
}, {});
const drgtRes = formatStatus.map(item => ({
...item,
warning: String(item.warning),
ranking: rankData[Number(item.warning)],
}));
set((state: drgtState) => {
return {
...state,
drgt: {
...state.drgt,
drgtRes: drgtRes,
},
}
});
} catch (e) {
console.log(e)
}
},
drgtModalSearch: async (fromDate: string, toDate: string, sgg: string) => {
try {
const drgtMoData = await getFetch(
fetchType.WR,
`/il/getData/${fromDate}/${toDate}/${sgg}/${sgg}`
);
console.log("drgtMoData", drgtMoData)
const drgtMoRes = drgtMoData.map((item) => {
console.log("item", item)
const year = item.anlsYmd.slice(0, 4);
const month = item.anlsYmd.slice(4, 6);
const drgtState = item.lvngMfgUswtrDrgtInfo;
const sggNm = item.sggNm;
return {
year: year,
month: month,
drgtState: drgtState,
sggNm: sggNm
}
});
set((state: drgtState) => {
return {
...state,
drgt: {
...state.drgt,
drgtMoRes: drgtMoRes,
},
}
});
} catch (e) {
console.log(e)
}
},
}
}
@@ -0,0 +1,109 @@
import { z } from "zod";
export const reUseCaseExcel = z.object({
file: z.any()
});
type nameMap = {
[key: string]: string;
}
export const reUseCaseNames: nameMap = {
"사업명": "ruseBizNm",
"사업시작일": "bizBgngDate",
"사업종료일": "bizEndtDate",
"공급처_시설종류": "splyFcltKnd",
"공급처명": "splyFcltNm",
"공급처_시설용량(㎥/일)": "splyFcltCpct",
"수요처명": "dmdFcltNm",
"수요처_주소": "dmdFcltAddr",
"재이용시설_시설용량(㎥/일)": "ruseFcltCpct",
"재이용시설_관로(km)": "ruseFcltPplLen",
"재이용시설_용도": "ruseFcltUsg",
"재이용시설_공급량(㎥/일)": "ruseFcltSplyCpct",
"재이용시설_처리공법": "ruseFcltPrcsMsrc",
"보증수질_용도별": "grnteWqUsg",
"보증수질_총대장균군수": "grnteWqGrssClfg",
"보증수질_결합잔류염소": "grnteWqCombResdch",
"보증수질_탁도": "grnteWqTrbt",
"보증수질_생물학적산소요구량(BOD)(mg/L)": "grnteWqBod",
"보증수질_냄새": "grnteWqSmll",
"보증수질_색도(도)": "grnteWqCrmty",
"보증수질_총질소(T-N)(mg/L)": "grnteWqTn",
"보증수질_총인(T-P)(mg/L)": "grnteWqTp",
"보증수질_수소이온농도(pH)": "grnteWqPh",
"보증수질_염화물": "grnteWqClIonQnt",
"보증수질_전기전도도": "grnteWqCndctStat",
"보증수질_비고": "grnteWqRmrk",
"소요사업비_총(백만원)": "reqBizGrssCst",
"소요사업비_토목_재이용시설(백만원)": "reqBizReuseFcltCst",
"소요사업비_토목_관로(백만원)": "reqBizPplCst",
"소요사업비_건축(백만원)": "reqBizArchCst",
"소요사업비_기계(백만원)": "reqBizMchnCst",
"소요사업비_전기.계측(백만원)": "reqBizMtrngCst",
"소요사업비_GIS DB(백만원)": "reqBizGisCst",
"소요사업비_CCTV(백만원)": "reqBizCctvCst",
"소요사업비_시공 및 준공(백만원)": "reqBizCmcnCst",
"소요사업비_폐기물(백만원)": "reqBizWstCst",
"소요사업비_설계비(백만원)": "reqBizDesignCst",
"소요사업비_감리비(백만원)": "reqBizSpvsCst",
"소요사업비_시설부대비(백만원)": "reqBizSbsdCst",
"소요사업비_기타(백만원)": "reqBizEtc",
"재원조달계획_총(백만원)": "prcmPlanGrssCst",
"재원조달계획_국비(백만원)": "prcmPlanNlep",
"재원조달계획_지방비(백만원)": "prcmPlanLcep",
"재원조달계획_원인자부담(백만원)": "prcmPlanBrdnCst",
"재원조달계획_국고비율(백만원)": "prcmPlanEcqrRt",
"사업비 정산_총(백만원)": "clclnGrssCst",
"사업비 정산_국비(백만원)": "clclnNlep",
"사업비 정산_지방비(백만원)": "clclnLcep",
"사업비 정산_원인자부담(백만원)": "clclnBrdnCst",
"사업비 정산_국고비율(백만원)": "clclnEcqrRt"
}
interface PbnstSttnItem {
rvbsn: string;
lgbsn: number;
mdmbsn?: string;
rgnNm?: string;
sido?: number;
gugun?: number;
bscFctNm?: string;
efflWqltStndrdNm?: string;
fctCpc?: number;
tel?: string;
ruseUtztnFcltNm?: string;
fcltCpct?: number;
splyExtraCpct?: number;
ruseCpct?: number;
usageTotal?: number;
inUsage?: number;
outUsage?: number;
}
export const excelPbnstSttnParsing = (data: PbnstSttnItem[]): string[][] => {
const excelExportData: string[][] = [];
data.forEach((item) => {
excelExportData.push([
item.rvbsn,
String(item.lgbsn),
item.mdmbsn ?? "",
item.rgnNm ?? "",
String(item.sido ?? ""),
String(item.gugun ?? ""),
String(item.bscFctNm ?? ""),
String(item.efflWqltStndrdNm ?? ""),
String(item.fctCpc ?? ""),
String(item.tel ?? ""),
String(item.ruseUtztnFcltNm ?? ""),
String(item.fcltCpct ?? ""),
String(item.splyExtraCpct ?? ""),
String(item.ruseCpct ?? ""),
String(item.usageTotal ?? ""),
String(item.inUsage ?? ""),
String(item.outUsage ?? ""),
])
})
return excelExportData;
};
@@ -4,15 +4,22 @@ import type { TableUpperProps } from "../../common/type/header";
//★☆★☆ 여기서부터 수정하기 ★☆★☆
export const ruseCaseHeadListExport: TableUpperProps[] = [
{
id: "a",
id: "ruseBizNm",
title: "사업명",
visible: true,
row: 0,
rowSpan: 2,
},
{
id: "b",
title: "사업기간",
id: "bizBgngDate",
title: "사업시작일",
visible: true,
row: 0,
rowSpan: 2,
},
{
id: "bizEndDate",
title: "사업종료일",
visible: true,
row: 0,
rowSpan: 2,
@@ -25,19 +32,19 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 3,
},
{
id: "d",
id: "splyFcltKnd",
title: "시설종류",
visible: true,
row: 1,
},
{
id: "e",
id: "splyFcltNm",
title: "공급처명",
visible: true,
row: 1,
},
{
id: "f",
id: "splyFcltCpct",
title: "시설용량(m³/일)",
visible: true,
row: 1,
@@ -50,13 +57,13 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 2,
},
{
id: "h",
id: "dmdFcltNm",
title: "수요처명",
visible: true,
row: 1,
},
{
id: "i",
id: "dmdFcltAddr",
title: "주소",
visible: true,
row: 1,
@@ -69,31 +76,31 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 5,
},
{
id: "q2",
id: "ruseFcltCpct",
title: "시설용량(m³/일)",
visible: true,
row: 1,
},
{
id: "q3",
id: "ruseFcltPplLen",
title: "관로(km)",
visible: true,
row: 1,
},
{
id: "q4",
id: "ruseFcltUsg",
title: "용도",
visible: true,
row: 1,
},
{
id: "q5",
id: "ruseFcltSplyCpct",
title: "공급량(m³/일)",
visible: true,
row: 1,
},
{
id: "q6",
id: "ruseFcltPrcsMsrc",
title: "처리공법",
visible: true,
row: 1,
@@ -106,79 +113,79 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 13,
},
{
id: "k1",
id: "grnteWqUsg",
title: "용도별",
visible: true,
row: 1,
},
{
id: "k2",
id: "grnteWqGrssClfg",
title: "총대장균군수​(개/100ml)",
visible: true,
row: 1,
},
{
id: "k3",
id: "grnteWqCombResdch",
title: "결합잔류염소(mg/L)",
visible: true,
row: 1,
},
{
id: "k4",
id: "grnteWqTrbt",
title: "탁도(NTU)",
visible: true,
row: 1,
},
{
id: "k5",
id: "grnteWqBod",
title: "생물학적​산소요구량(BOD)(mg/L)​",
visible: true,
row: 1,
},
{
id: "k6",
id: "grnteWqSmll",
title: "냄새​",
visible: true,
row: 1,
},
{
id: "k7",
id: "grnteWqCrmty",
title: "색도​",
visible: true,
row: 1,
},
{
id: "k8",
id: "grnteWqTn",
title: "총질소(T-N)(mg/L)​",
visible: true,
row: 1,
},
{
id: "k9",
id: "grnteWqTp",
title: "총인(T-P)(mg/L)",
visible: true,
row: 1,
},
{
id: "k10",
id: "grnteWqPh",
title: "수소이온농도(PH)​",
visible: true,
row: 1,
},
{
id: "k11",
title: "수소이온농도(PH)​",
id: "grnteWqClIonQnt",
title: "염화물",
visible: true,
row: 1,
},
{
id: "k12",
title: "전기전도도​(㎲/cm",
id: "grnteWqCndctStat",
title: "전기전도도​(㎲/cm)",
visible: true,
row: 1,
},
{
id: "k13",
id: "grnteWqRmrk",
title: "비고​",
visible: true,
row: 1,
@@ -191,85 +198,85 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 14,
},
{
id: "h1",
id: "reqBizGrssCst",
title: "총",
visible: true,
row: 1,
},
{
id: "h2",
title: "토목재이용시설",
id: "reqBizReuseFcltCst",
title: "토목재이용시설",
visible: true,
row: 1,
},
{
id: "h3",
id: "reqBizPplCst",
title: "토목관로",
visible: true,
row: 1,
},
{
id: "h4",
id: "reqBizArchCst",
title: "건축",
visible: true,
row: 1,
},
{
id: "h5",
id: "reqBizMchnCst",
title: "기계",
visible: true,
row: 1,
},
{
id: "h6",
id: "reqBizMtrngCst",
title: "전기・계측",
visible: true,
row: 1,
},
{
id: "h7",
id: "reqBizGisCst",
title: "GIS・DB",
visible: true,
row: 1,
},
{
id: "h8",
id: "reqBizCctvCst",
title: "CCTV",
visible: true,
row: 1,
},
{
id: "h9",
id: "reqBizCmcnCst",
title: "시공및준공",
visible: true,
row: 1,
},
{
id: "h10",
id: "reqBizWstCst",
title: "페기물",
visible: true,
row: 1,
},
{
id: "h11",
title: "설계비",
id: "reqBizDesignCst",
title: "설계비",
visible: true,
row: 1,
},
{
id: "h12",
id: "reqBizSpvsCst",
title: "감리비",
visible: true,
row: 1,
},
{
id: "h13",
id: "reqBizSbsdCst",
title: "시설부대비",
visible: true,
row: 1,
},
{
id: "h14",
id: "reqBizEtc",
title: "기타",
visible: true,
row: 1,
@@ -279,32 +286,38 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
title: "자원조달계획(백만원)",
visible: true,
row: 0,
colSpan: 4,
colSpan: 5,
},
{
id: "i1",
id: "prcmPlanGrssCst",
title: "총",
visible: true,
row: 1,
},
{
id: "i2",
id: "prcmPlanNlep",
title: "국비",
visible: true,
row: 1,
},
{
id: "i3",
id: "prcmPlanLcep",
title: "지방비",
visible: true,
row: 1,
},
{
id: "i4",
id: "prcmPlanBrdnCst",
title: "원인자부담",
visible: true,
row: 1,
},
{
id: "prcmPlanEcqrRt",
title: "국고비율",
visible: true,
row: 1,
},
{
id: "j",
title: "사업비정산(백만원)",
@@ -313,31 +326,31 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 5,
},
{
id: "j1",
id: "clclnGrssCst",
title: "총",
visible: true,
row: 1,
},
{
id: "j2",
id: "clclnNlep",
title: "국비",
visible: true,
row: 1,
},
{
id: "j3",
id: "clclnLcep",
title: "지방비",
visible: true,
row: 1,
},
{
id: "j4",
id: "clclnBrdnCst",
title: "원인자부담",
visible: true,
row: 1,
},
{
id: "j5",
id: "clclnEcqrRt",
title: "국고비율",
visible: true,
row: 1,
+101 -8
View File
@@ -1,24 +1,117 @@
import { fetchType, getFetch, postFectch } from "@/components/lib/api";
import type { TableUpperProps } from "../../common/type/header";
import { ruseCaseHeadListExport } from "./headList";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
export type ruseCaseState = {
ruseCase: {
ruseHeadList:TableUpperProps[];
};
ruseCaseMng: {
ruseHeadList:TableUpperProps[];
ruseCaseMngList:ruseCaseMngExcelProps[];
};
type ruseCaseActions = {
};
export interface ruseCaseMngExcelProps {
ruseBizNm: string,
bizBgngDate: string,
bizEndDate: string,
splyFcltKnd: string,
splyFcltNm: string,
splyFcltCpct: number | 0 |'',
dmdFcltNm: string,
dmdFcltAddr: string,
ruseFcltCpct: number | 0 |'',
ruseFcltPplLen: string,
ruseFcltUsg: string,
ruseFcltSplyCpct: number | 0 |'',
ruseFcltPrcsMsrc: string,
grnteWqUsg: string,
grnteWqGrssClfg: number | 0 |'',
grnteWqCombResdch: number | 0 |'',
grnteWqTrbt: string,
grnteWqBod: string,
grnteWqSmll: string,
grnteWqCrmty: string,
grnteWqTn: string,
grnteWqTp: string,
grnteWqPh: string,
grnteWqClIonQnt: string,
grnteWqCndctStat: string,
grnteWqRmrk: string,
reqBizReuseFcltCst: number | 0 |'',
reqBizPplCst: number | 0 |'',
reqBizArchCst: number | 0 |'',
reqBizMchnCst: number | 0 |'',
reqBizMtrngCst: number | 0 |'',
reqBizGisCst: number | 0 |'',
reqBizCctvCst: number | 0 |'',
reqBizCmcnCst: number | 0 |'',
reqBizWstCst: number | 0 |'',
reqBizDesignCst: number | 0 |'',
reqBizSpvsCst: number | 0 |'',
reqBizSbsdCst: number | 0 |'',
reqBizEtc: string,
reqBizGrssCst: number | 0 |'',
prcmPlanNlep: number | 0 |'',
prcmPlanLcep: number | 0 |'',
prcmPlanBrdnCst: number | 0 |'',
prcmPlanEcqrRt: number | 0 |'',
prcmPlanGrssCst: number | 0 |'',
clclnNlep: number | 0 |'',
clclnLcep: number | 0 |'',
clclnBrdnCst: number | 0 |'',
clclnEcqrRt: number | 0 |'',
clclnGrssCst: number | 0 |'',
}
type ruseCaseActions = {
selectRuseCaseMng: (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => Promise<void>;
insertRuseCaseMng: (data: any) => Promise<void>;
};
export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => {
const tailUrl = ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : '');
const resp = await getFetch(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`);
return resp;
};
export const fetchInsertRuseCaseMng = async (data: any) => {
const response = await postFectch('/reuseFc/pubFcRuseCase', data, fetchType.WR);
return response.data;
};
export type ruseCaseType = ruseCaseState & ruseCaseActions;
export const ruseCaseStateExport: ruseCaseState = {
ruseCase: {
ruseHeadList:ruseCaseHeadListExport,
ruseCaseMng: {
ruseHeadList:ruseCaseHeadListExport,
ruseCaseMngList:[],
},
};
export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
set: any
) => {
return {
selectRuseCaseMng: async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => {
const res = await fetchSearchRuseCaseMng(sidoCd, sigunguCd, usageCd, name);
console.log('res : ', res);
set((state: ruseCaseState) => ({
ruseCaseMng: {
...state.ruseCaseMng,
ruseCaseMngList: res,
},
}));
},
insertRuseCaseMng: async (data: any) => {
const response = await fetchInsertRuseCaseMng(data);
if(response.status === 200){
ToastFormUi({
body: "잠재수요처 등록이 완료되었습니다. ",
title: "잠재수요처 등록",
});
}
},
};
};
@@ -17,7 +17,7 @@ export const chkgdRegExcel = z.object({
},
{ message: "현재 년도보다 높은 수를 입력할 수 없습니다." }
),
file: z.instanceof(File).optional(),
file: z.instanceof(File).nullish(),
});
export const guideDetail = z.object({
@@ -61,7 +61,7 @@ type rainChkgdnActions = {
): Promise<void>;
rainChkExcelDataUPload(excelData: rainChkZodType[]): Promise<void>;
rainChkDelete(chkgdnPks: number[]): Promise<void>;
rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit :boolean) => void;
rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit: boolean) => void;
};
export type rainChkgdnType = rainChkgdnState & rainChkgdnActions;
@@ -99,9 +99,10 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => {
};
const initFetch = async (txt: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rainRes = await getFetch(
"wr",
`/reuseChk/rainy` + (txt ? `/${txt}` : "2024") + (sgg ? `/${sgg}` : "/0")
`/reuseChk/rainy` + (txt ? `/${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0")
);
return rainRes;
};
@@ -130,13 +131,16 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => {
try {
const res = await postFectch("/reuseChk/rainy", excelData);
console.log("res", res);
console.log("res.ok", res.ok)
if (res.ok) {
ToastFormUi({
body: "지도점검 등록이 완료되었습니다. ",
title: "지도점검 등록",
});
}
const rainChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(currentYear, "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
@@ -178,7 +182,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
title: "지도점검 수정",
});
}
const rainChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(currentYear, "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
@@ -201,7 +206,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
title: "지도점검 수정",
});
}
const rainChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(currentYear, "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
@@ -223,7 +229,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
title: "지도점검 삭제",
});
}
const rainChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(currentYear, "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
@@ -235,23 +242,23 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" });
}
},
rainChkHeadChange: async (rainChkgdn: rainChkgdnState , isEdit :boolean) => {
rainChkHeadChange: async (rainChkgdn: rainChkgdnState, isEdit: boolean) => {
try {
const rainChkgdnHeadList = rainChkgdn.rainChkgdn.rainChkgdnHeadList;
const splHeadList = rainChkgdn.rainChkgdn.splHeadList;
const ruseHeadList = rainChkgdn.rainChkgdn.ruseHeadList;
rainChkgdnHeadList.forEach(( head) => {
if(head.edit!== undefined)
head.edit=isEdit;
rainChkgdnHeadList.forEach((head) => {
if (head.edit !== undefined)
head.edit = isEdit;
});
splHeadList.forEach((head) => {
if(head.edit!== undefined)
head.edit=isEdit;
if (head.edit !== undefined)
head.edit = isEdit;
});
ruseHeadList.forEach((head) => {
if(head.edit!== undefined)
head.edit=isEdit;
if (head.edit !== undefined)
head.edit = isEdit;
});
return set((state: rainChkgdnState) => ({
@@ -259,8 +266,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
rainChkgdn: {
...state.rainChkgdn,
rainChkgdnHeadList: rainChkgdnHeadList,
splHeadList:splHeadList,
ruseHeadList:ruseHeadList
splHeadList: splHeadList,
ruseHeadList: ruseHeadList
},
}));
} catch (error) {
@@ -80,10 +80,11 @@ export const rswtChkgdnStateExport: rswtChkgdnState = {
};
const initFetch = async (year: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetch(
"wr",
`/reuseChk/reuseWater/` +
(year ? `${year}` : "2024") +
(year ? `${year}` : currentYear) +
(sgg ? `/${sgg}` : "/0")
);
return rswtChkRes;
@@ -121,12 +122,12 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
excelList.push({
data: exceldata.guideDetail,
headerNum: 0,
headerRange: "A1:AI1",
headerRange: "A1:AJ1",
});
excelList.push({
data: exceldata.polutions,
headerNum: 0,
headerRange: "A1:AZ1",
headerRange: "A1:BB1",
});
return set((state: rswtChkgdnState) => ({
@@ -138,7 +139,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
},
rswtChkgdnInit: async (year: string, sggCd: string) => {
try {
const rswtChkRes = await initFetch("2024", sggCd);
const rswtChkRes = await initFetch(year, sggCd);
return set((state: rswtChkgdnState) => ({
...state,
@@ -161,11 +162,13 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
title: "지도점검 등록",
});
}
// const rainChkRes = await initFetch('2024', '0');
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
...state.rswtChkgdn,
rswtChkRes: rswtChkRes
},
}));
} catch (error) {
@@ -184,7 +187,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
title: "지도점검 수정",
});
}
const rswtChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -207,7 +211,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
title: "지도점검 삭제",
});
}
const rswtChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -259,7 +264,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
title: "지도점검 수정",
});
}
const rswtChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -77,7 +77,6 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
prfmncHasuInit: async (page: string, sggOrCity: string) => {
try {
const prfmncRes = await hasuListFetch(page, sggOrCity);
console.log("prfmncRes", prfmncRes);
return set((state: pbnstPrfmncState) => ({
...state,
prfmnc: {
@@ -35,7 +35,7 @@ export type rainChkSttnState = {
};
type rainChkSttnActions = {
rainChkSttnInit: ( sggCd: string) => void;
rainChkSttnInit: (searchTxt: string, sggCd: string) => void;
rainChkSttnSearch: (searchTxt: string, sggCd: string) => void;
isRainSttn:(isSttn:boolean) => void;
getRainSttnExcel:(excelList:rainChkZodType[]) => void;
@@ -58,9 +58,10 @@ export const rainChkSttnStateExport: rainChkSttnState = {
};
const initFetch = async (txt: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rainRes = await getFetch(
"wr",
`/reuseChk/rainy/` + (txt ? `${txt}` : "2024") + (sgg ? `/${sgg}` : "/0")
`/reuseChk/rainy/` + (txt ? `${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0")
);
return rainRes;
};
@@ -68,9 +69,9 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = (
set: any
) => {
return {
rainChkSttnInit: async ( sggCd: string) => {
rainChkSttnInit: async (searchTxt: string, sggCd: string) => {
try {
const rainChkRes = await initFetch(undefined, sggCd);
const rainChkRes = await initFetch(searchTxt, sggCd);
return set((state: rainChkSttnState) => ({
...state,
@@ -19,6 +19,7 @@ export const buildingExcelRswtSttn = (excelList: any[]) => {
String(item.trbt) ?? "",
String(item.bod) ?? "",
String(item.smll) ?? "",
String(item.crmty) ?? "",
String(item.tn) ?? "",
String(item.tph) ?? "",
String(item.ph) ?? "",
@@ -1,15 +1,15 @@
import type { TableUpperProps } from "@/app/store/common/type/header";
export const rswtChkSttnHeadListExport: TableUpperProps[] = [
{ id: "order", title: "순번", rowSpan: true },
{ id: "cityText", title: "시도", rowSpan: true },
{ id: "sggText", title: "시군구", rowSpan: true },
{ id: "bdstNm", title: "시설명", rowSpan: true },
{ id: "operMngCd", title: "운영관리형태", rowSpan: true },
{ id: "anlOperCst", title: "연간운영비(원/년)", rowSpan: true },
{ id: "prcsCpct", title: "처리용량(m³)", rowSpan: true },
{ id: "spwtPositn", title: "채수지점", rowSpan: true },
{ id: "chckYmd", title: "점검일", rowSpan: true },
{ id: "order", title: "순번", mergeCell: true },
{ id: "cityText", title: "시도", mergeCell: true },
{ id: "sggText", title: "시군구", mergeCell: true },
{ id: "bdstNm", title: "시설명", mergeCell: true },
{ id: "operMngCd", title: "운영관리형태", mergeCell: true },
{ id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true },
{ id: "prcsCpct", title: "처리용량(m³)", mergeCell: true },
{ id: "spwtPositn", title: "채수지점", mergeCell: true },
{ id: "chckYmd", title: "점검일", mergeCell: true },
{ id: "utztnUsgCd", title: "이용용도" },
{ id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)", type: "number" },
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number" },
@@ -24,10 +24,10 @@ export type rswtChkSttnState = {
};
type rswtChkSttnActions = {
rswtChkSttnInit: ( sggCd: string) => void;
isRswtSttn: (isSttn:boolean) => void;
getRswtSttnExcel:(excelList:rswtChkZodType[]) => void;
rswtChkSttnInit: (searchTxt: string, sggCd: string) => void;
isRswtSttn: (isSttn: boolean) => void;
getRswtSttnExcel: (excelList: rswtChkZodType[]) => void;
rswtChkSttnSearch: (searchTxt: string, sggCd: string) => void;
};
export type rswtChkSttnType = rswtChkSttnState & rswtChkSttnActions;
@@ -40,16 +40,17 @@ export const rswtChkStateExport: rswtChkSttnState = {
sttnExcelList: [],
rswtChkRes: [],
isInit: false,
isSttn:false,
isSttn: false,
},
};
const initFetch = async (year: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetch(
"wr",
`/reuseChk/reuseWater/` +
(year ? `${year}` : "2024") +
(sgg ? `/${sgg}` : "/0")
(year ? `${year}` : currentYear) +
(sgg ? `/${sgg}` : "/0")
);
return rswtChkRes;
};
@@ -59,9 +60,9 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = (
set: any
) => {
return {
rswtChkSttnInit: async ( sggCd: string) => {
rswtChkSttnInit: async (searchTxt: string, sggCd: string) => {
try {
const rswtChkRes = await initFetch("2024", sggCd);
const rswtChkRes = await initFetch(searchTxt, sggCd);
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
@@ -73,35 +74,50 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = (
} catch (error) {
console.log(error);
}
},isRswtSttn :(isSttn:boolean) =>{
try {
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
isSttn:isSttn
},
}));
} catch (error) {
console.log(error);
}
},getRswtSttnExcel: async (excelList:rswtChkZodType []) => {
try {
const excelData = buildingExcelRswtSttn(excelList)
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
sttnExcelList:excelData
},
}));
} catch (error) {
console.log(error);
}
}
},
isRswtSttn: (isSttn: boolean) => {
try {
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
isSttn: isSttn
},
}));
} catch (error) {
console.log(error);
}
},
getRswtSttnExcel: async (excelList: rswtChkZodType[]) => {
try {
const excelData = buildingExcelRswtSttn(excelList)
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
sttnExcelList: excelData
},
}));
} catch (error) {
console.log(error);
}
},
rswtChkSttnSearch: async (searchTxt: string, sggCd: string) => {
try {
const rswtChkRes = await initFetch(searchTxt, sggCd);
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
rswtChkRes: rswtChkRes
},
}));
} catch (error) {
console.log(error);
}
},
};
};
@@ -1,10 +1,63 @@
import { getFetch, postFectch, putFectch } from "@/components/lib/api";
import { upHeadList, type chgBuilding} from "../rainReg";
import { upHeadList, type chgBuilding } from "../rainReg";
import { z } from "zod";
import { makeMdfr, makeRstr } from "../bdstReg";
import {
baseReuseBuildingSchema,
chgBuildingSchema,
validationMessages,
} from "../rainReg/type"; // Import baseReuseBuildingSchema, chgBuildingSchema, validationMessages
import type { TableUpperProps } from "@/app/store/common/type/header";
const hdtflObjSchema = z.object({
htdflFcltPk: z.number().nullish(),
bdstRuseFcltFk: z.number().nullish(),
etgtType: z
.string()
.min(1, { message: validationMessages.required("에너지원 종류") }),
fclCpct: z
.number()
.positive({ message: validationMessages.required("시설 용량") }),
plwtYn: z.enum(["Y", "N"]).default("Y"),
orgwtWtritSpt: z
.number()
.positive({ message: validationMessages.required("원수 취수량") }),
orgwtOcrnProcs: z
.string()
.min(1, { message: validationMessages.required("원수 발생 공정") }),
wtritEntrcTp: z
.number()
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
.default(0), // Changed max to 100 based on message
amtgnr: z.number().default(0),
prcsProcsYn: z.enum(["Y", "N"]).default("Y"),
prcsFcltCpct: z
.number()
.positive({ message: validationMessages.required("처리시설 용량") }),
fmYn: z.string().default(""),
utztnCpct: z
.number()
.positive({ message: validationMessages.required("이용 용량") }),
utztnUsg: z
.string()
.min(1, { message: validationMessages.required("이용 용도") }),
grssClfg: z.number().default(0), // Measurements, 0 is fine as default
combResdch: z.number().default(0), // Measurements, 0 is fine as default
trbt: z.number().default(0), // Measurements, 0 is fine as default
bod: z.number().default(0), // Measurements, 0 is fine as default
smll: z.number().default(0), // Measurements, 0 is fine as default
crmty: z.number().default(0), // Measurements, 0 is fine as default
tn: z.number().default(0), // Measurements, 0 is fine as default
tph: z.number().default(0), // Measurements, 0 is fine as default
ph: z.number().default(0), // Measurements, 0 is fine as default
clIonQnt: z.number().default(0), // Measurements, 0 is fine as default
cdtnv: z.number().default(0), // Measurements, 0 is fine as default
emsnExitTp: z.number().default(0), // Measurements, 0 is fine as default
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
});
export const hdtflZod = z.object({
bdstInfoPk: z.number().nullish(),
ctpvCd: z.number().default(0),
@@ -14,85 +67,25 @@ export const hdtflZod = z.object({
daddr: z.string().default(" "),
bdstNm: z.string().default(" "),
frstArchPrmsnDt: z.date().default(() => new Date()),
archChgPrmsnDt: z.date().default(() => new Date()).nullish() ,
archChgPrmsnDt: z
.date()
.default(() => new Date())
.nullish(),
archArea: z.number().default(0),
archChgArea: z.number().nullish().default(0),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
reuseBuilding: z
.array(
z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish().default(0),
operMngCd: z.string().nullish().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.enum(["Y", "N"]).nullish(),
instlCstSprtAmt: z.number().nullish().default(0),
grntchBdstCertNo: z.string().nullish().default(""),
grntchBdstCertGrd: z.string().nullish().default(""),
ruseFcltFlg: z.string().nullish().default(""),
bdstUsgCd: z.number().default(0),
lawTrgtYn: z.enum(["Y", "N"]).nullish(),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
// htdflObj 매핑
htdflObj: z
.object({
htdflFcltPk: z.number().nullish(),
bdstRuseFcltFk: z.number().nullish(),
etgtType: z.string().default(""),
fclCpct: z.number().default(0),
plwtYn: z.enum(["Y", "N"]),
orgwtWtritSpt: z.number().default(0),
orgwtOcrnProcs: z.string().default(""),
wtritEntrcTp: z.number().max(4, "온도는 100이상이 될 수 없습니다.")
.default(0),
amtgnr: z.number().default(0),
prcsProcsYn: z.enum(["Y", "N"]),
prcsFcltCpct: z.number().default(0),
fmYn: z.string().default(""),
utztnCpct: z.number().default(0),
utztnUsg: z.string().default(""),
grssClfg: z.number().default(0),
combResdch: z.number().default(0),
trbt: z.number().default(0),
bod: z.number().default(0),
smll: z.number().default(0),
crmty: z.number().default(0),
tn: z.number().default(0),
tph: z.number().default(0),
ph: z.number().default(0),
clIonQnt: z.number().default(0),
cdtnv: z.number().default(0),
emsnExitTp: z.number().default(0),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
})
.default({
plwtYn: "N",
prcsProcsYn: "N",
}),
})
)
.default([]),
chgBuilding: z
.array(
z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date() ,
archChgArea: z.string().default("-").nullish(),
bdstChgNm: z.string().default(""),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
baseReuseBuildingSchema.extend({
htdflObj: hdtflObjSchema,
})
)
.default([]),
chgBuilding: z.array(chgBuildingSchema).default([]), // Use imported chgBuildingSchema
});
export type customHtdflBdst = z.infer<typeof hdtflZod>
export type customHtdflBdst = z.infer<typeof hdtflZod>;
export type htdflFacState = {
htdfl: {
@@ -102,11 +95,10 @@ export type htdflFacState = {
};
};
type htdflFacSActions = {
initHtdfl: (code: string ) => void;
htdflBdstUpdate:(data: customHtdflBdst, code:string|number) => void;
htdflBdstReg:(data: customHtdflBdst, code:string|number) => void;
getHtdflBdstData:(pageNation:number, code:string|number) =>void;
initHtdfl: (code: string) => void;
htdflBdstUpdate: (data: customHtdflBdst, code: string | number) => void;
htdflBdstReg: (data: customHtdflBdst, code: string | number) => void;
getHtdflBdstData: (pageNation: number, code: string | number) => void;
};
const htdflRes: customHtdflBdst[] = [];
@@ -121,25 +113,22 @@ export const htdflFacStateExport: htdflFacState = {
},
};
const initFetch = async (code: string | number) => {
const rswtRes = await getFetch("wr", `/facilityList/1/20/${code}`);
const initFetch = async (code:string|number) => {
const rswtRes = await getFetch(
"wr",
`/facilityList/1/20/${code}`)
return rswtRes;
};
export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
set: any
) => {
return {
getHtdflBdstData: async (pageNation: number, code: string | number) => {
getHtdflBdstData: async (pageNation: number, code: string | number) => {
try {
const rswtRes = await getFetch(
"wr",
`/drain/facilityList/${pageNation}/20/${code}`
);
console.log(pageNation)
console.log(pageNation);
// 여기서 상태 업데이트
return set((state: htdflFacState) => ({
...state,
@@ -154,8 +143,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
}
},
initHtdfl: async (code: string ) => {
initHtdfl: async (code: string) => {
try {
const htdflRes = await initFetch(code);
set((state: htdflFacState) => {
@@ -172,9 +160,9 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
console.log(error);
}
},
htdflBdstUpdate: async (data: customHtdflBdst, code:string|number) => {
htdflBdstUpdate: async (data: customHtdflBdst, code: string | number) => {
try {
const chData=await makeMdfr(data)
const chData = await makeMdfr(data);
const res = await putFectch("/drain/facility", data);
if (res.ok) {
alert(" 수정이 완료되었습니다.");
@@ -196,18 +184,17 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
);
}
},
htdflBdstReg: async (data: customHtdflBdst , code:string|number) => {
htdflBdstReg: async (data: customHtdflBdst, code: string | number) => {
try {
const chData=await makeRstr(data)
const chData = await makeRstr(data);
const res = await postFectch("/drain/facility", data);
if (res.ok) {
if(data.chgBuilding.length > 0){
if (data.chgBuilding.length > 0) {
await res
.json()
.then((resData: any) => {
const chPK = resData?.data?.bdstInfoPk;
const makeCh = resData.data.chgBuilding.map(
(item: chgBuilding) => ({
...item,
@@ -222,9 +209,8 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
alert("등록이 완료되었습니다.");
}
});
}else{
} else {
alert("등록이 완료되었습니다.");
}
}
const htdflRes = await initFetch(code);
@@ -238,7 +224,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
}));
} catch (errorReg) {
alert("등록 오류입니다.");
}
}
},
};
};
@@ -31,8 +31,8 @@ export const pbnstMngUpHeadListExport: TableUpperProps[] = [
export const pplInstlsHeadListExport: TableUpperProps[] = [
{ id: "choose", title: "선택", type: "checkbox" },
{ id: "ppMqlt", title: "관재질", type: "select", selectData: "pplQulity" },
{ id: "pdmt", title: "관경", type: "select", selectData: "pplLenght" },
{ id: "cpct", title: "연장", edit: true, type: "number" },
{ id: "pdmt", title: "관경(mm)", type: "select", selectData: "pplLenght" },
{ id: "cpct", title: "연장(m)", edit: true, type: "number" },
];
export const pbnstDemandHeadListExport: TableUpperProps[] = [
@@ -69,20 +69,21 @@ export type formDetailType = {
id: string;
lable: string;
type?: string;
selectData?: string; // select box일 경우
option?:string
};
export const wasteWaterFcltINfo: formDetailType[] = [
{ id: "wwtEmsnFcltPrmsnYn", lable: "폐수배출물신고 허가여부", type: "selectYn" },
{ id: "tmsInstlYn", lable: "Tms설치여부", type: "selectYn" },
{ id: "pbnstDrngInflwExclYn", lable: "공공하수도유입제외 허가여부", type: "selectYn" },
{ id: "wwtEmsnFcltPrmsnYn", lable: "폐수배출물신고 허가여부", type: "select" , selectData: "yn"},
{ id: "tmsInstlYn", lable: "Tms설치여부", type: "select" , selectData: "yn" },
{ id: "pbnstDrngInflwExclYn", lable: "공공하수도유입제외 허가여부", type: "select" , selectData: "yn" },
];
export const concentratedWaterInfo: formDetailType[] = [
{ id: "erctWtrOcrnCpct", lable: "눙축수 발생량(m³/일)", type: "number" },
{ id: "erctUswtrLinkSpt", lable: "눙축수연계지점" },
{ id: "erctUswtrFcltCpct", lable: "시설용량(m³/일)", type: "number" },
{ id: "erctUswtrPrcsMeth", lable: "눙축수처리방식" },
{ id: "erctUswtrPrcsMeth", lable: "눙축수처리방식", type : "select", selectData: "erctUswtrPrcsMeth" },
{ id: "erctUswtrPrcsMsrcCd", lable: "눙축수처리공법" },
];
@@ -90,8 +91,8 @@ export const ruseFcltinfo: formDetailType[] = [
{ id: "rprcsFcltCpct", lable: "시설용량(m³/일)", type: "number" },
{ id: "majorPrcsMsrcCd", lable: "주처리공법" },
{ id: "addr", lable: "위치(주소)" },
{ id: "rprcsMthdCd", lable: "재처리방식" },
{ id: "inoutYn", lable: "위치(처리장 내외)" ,type: "selectYn" }, // 머가 내? 외?,
{ id: "rprcsMthdCd", lable: "재처리방식", type: "select", selectData: "rusePrcsMthdCd" },
{ id: "inoutYn", lable: "위치(처리장 내외)" ,type: "select" , selectData: "yn"}, // 머가 내? 외?,
{ id: "mainSplyPumpCpct", lable: "메인공급펌프 용량", type: "number" },
];
@@ -100,12 +101,12 @@ export const businessMoneyInfo: formDetailType[] = [
{ id: "lcep", lable: "지방비", type: "number" },
{ id: "cusrAlotm", lable: "민간투자비", type: "number" },
{ id: "etc", lable: "기타" },
{ id: "totalMoney", lable: "합계", type: "number" ,option:"readOnly" },
{ id: "totalMoney", lable: "합계", type: "number" ,option:"disabled" },
];
export const businessSttnInfo: formDetailType[] = [
{ id: "seCd", lable: "구분" },
{ id: "bizMthdCd", lable: "사업방식" },
{ id: "seCd", lable: "구분", type: "select", selectData: "ruseSeCd" },
{ id: "bizMthdCd", lable: "사업방식", type: "select", selectData: "ruseBizMthdCd" },
{ id: "bizCmcnDay", lable: "사업준공일", type: "date" },
{ id: "instlAprvSncnDt", lable: "설치승인인가일", type: "date" },
];
@@ -118,15 +119,15 @@ export const ruseFcltIntakeList: formDetailType[] = [
];
export const ruseWaterQulityList: formDetailType[] = [
{ id: "bod", lable: "BOD(mg/L)", type: "number" },
{ id: "grssClfg", lable: "총대장균군수(개/mg)", type: "number" },
{ id: "combResdch", lable: "결합잔류염소(mh/L)", type: "number" },
{ id: "trbt", lable: "탁도(NTU)", type: "number" },
{ id: "smll", lable: "냄새", type: "number" },
{ id: "crmty", lable: "색도", type: "number" },
{ id: "tn", lable: "총질소(mg/L)", type: "number" },
{ id: "tph", lable: "총인(mg/L)", type: "number" },
{ id: "ph", lable: "수소이온농도", type: "number" },
{ id: "clIonQnt", lable: "염화물(mg/L)", type: "number" },
{ id: "cdtnv", lable: "전기전도도(μs/cm)​", type: "number" },
{ id: "bod", lable: "BOD(mg/L)", type: "text" },
{ id: "grssClfg", lable: "총대장균군수(개/mg)", type: "text" },
{ id: "combResdch", lable: "결합잔류염소(mh/L)", type: "text" },
{ id: "trbt", lable: "탁도(NTU)", type: "text" },
{ id: "smll", lable: "냄새", type: "text" },
{ id: "crmty", lable: "색도", type: "text" },
{ id: "tn", lable: "총질소(mg/L)", type: "text" },
{ id: "tph", lable: "총인(mg/L)", type: "text" },
{ id: "ph", lable: "수소이온농도", type: "text" },
{ id: "clIonQnt", lable: "염화물(mg/L)", type: "text" },
{ id: "cdtnv", lable: "전기전도도(μs/cm)​", type: "text" },
];
@@ -52,6 +52,7 @@ export type pubFacState = {
fcOrgwtsHeadList: TableUpperProps[];
pbnstMngRegData: customPbnst[];
pbnsUpdateIndex: pbnstInsertIndex;
fcOrgwtsNmList:pubHasuType[];
dmdComList: any[];
};
};
@@ -70,6 +71,7 @@ type pubFacActions = {
pbnstRuseFcltUpdate: (ruseRes: customPbnst[]) => void;
pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void;
pubHasuSearch :(page: string, sggOrCity: string , searchTxt:string) => void
getFcOrgwts: (fcltPkList: string[], size:number) => void;
};
export type pubFacType = pubFacState & pubFacActions;
@@ -90,6 +92,7 @@ export const pubFacStateExport: pubFacState = {
pbnstBizCycle: [{ text: "없음", val: "1" }],
pbnstMngRegData: [],
pbnsUpdateIndex: {} as pbnstInsertIndex,
fcOrgwtsNmList:[],
dmdComList: [],
},
};
@@ -102,12 +105,10 @@ const checkWrRegList = async () => {
const hasuListFetch = async (page: string, sggOrCity: string, searchTxt?:string) => {
const checkWrRegListRes = await checkWrRegList();
console.log(' checkWrRegListRes ', checkWrRegListRes)
let url = `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity? sggOrCity : "11"}/0/1/SW`;
const data = checkWrRegListRes.filter((item:string) => item !== "")
console.log(' checkWrRegListRes ' )
if(searchTxt){
url= url +`/${searchTxt}`
@@ -177,6 +178,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
try {
const pbnstHasuRes = await hasuListFetch(page, sggOrCity);
return set((state: pubFacState) => ({
...state,
pbnst: {
@@ -399,6 +401,25 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
} catch (e) {
console.log(e);
}
},getFcOrgwts : async (fcltPkList: string[] ,size:number) => {
let url = `/prcsFclt/ruseInfo/1/${size}/A/0/0/1/SW`;
const fcOrgwtsNmList = await postFectch(
url,
fcltPkList,
fetchType.COMMON
);
return set((state: pubFacState) => ({
...state,
pbnst: {
...state.pbnst,
fcOrgwtsNmList:fcOrgwtsNmList.data??[]
},
}));
}
};
};
@@ -14,10 +14,10 @@ export type selectLabel = {
val: string;
};
export type pbnstInsertIndex ={
mng:number,
biz:number
}
export type pbnstInsertIndex = {
mng: number;
biz: number;
};
export type publicReuseFacilityType = {
sido: string;
@@ -45,20 +45,20 @@ export type ruseFcltType = {
};
export type PbnstDemandComType = {
dmdInfoPk : string | '';
bzmnNo : string | '';
dmdCoNm : string | '';
daddr : string | '';
telno : string | '';
rgtr : string | '';
useUsgCd : string | '';
ctpvCd : string | '';
sggCd : string | '';
dmdAplyPswd: string | '';
dmdInfoPk: string | "";
bzmnNo: string | "";
dmdCoNm: string | "";
daddr: string | "";
telno: string | "";
rgtr: string | "";
useUsgCd: string | "";
ctpvCd: string | "";
sggCd: string | "";
dmdAplyPswd: string | "";
splyCpct: number | 0;
splyFmYn:string|'Y'
// dmdInfoFk:string|''
}
splyFmYn: string | "Y";
// dmdInfoFk:string|''
};
const pubHasuZod = z.object({
choose: z.boolean().default(false),
@@ -68,23 +68,27 @@ const pubHasuZod = z.object({
bscFctNm: z.string().default(""),
efflWqltStndrdNm: z.string().default(""),
fctCpc: z.number().default(0),
addr:z.string().nullish()
addr: z.string().nullish(),
});
export type pubHasuType = z.infer<typeof pubHasuZod>;
const zDate = z.preprocess((v) => (v ? new Date(v as string) : undefined), z.date()).nullish() ;
const zDate = z
.preprocess((v) => (v ? new Date(v as string) : undefined), z.date())
.nullish();
const zDateNullish = zDate.nullish();
const zDateDefaultNow = zDate.default(new Date());
const zNum = z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number());
const zNum = z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number()
);
const zNumNullish = zNum.nullish();
const zNumDefaultZero = zNum.default(0).nullish();
const pplInstlSchema = z.object({
ruseFcltBizFk: zNumNullish,
choose: z.boolean().default(false),
pdmt: z.string().default(''),
pdmt: z.string().default(""),
ppMqlt: z.string().default(""),
cpct: zNumDefaultZero,
});
@@ -115,22 +119,23 @@ export const fcOperSchema = z.object({
});
const fcOrgwtSchema = z.object({
pbnstRuseFcltBizFk:zNumNullish,
pbnstRuseFcltBizFk: zNumNullish,
swtrmtFcltFk: z.string().nullish(),
orgwtWtritQnt: zNum,
tmsYn: z.enum(["Y", "N"]).default("N"),
dschgFmYn: z.enum(["Y", "N"]).default("N"),
dtlPstnExpln: z.string().default(''),
dtlPstnExpln: z.string().default(""),
regDt: zDateNullish,
rgtr: z.string().default(''),
rgtr: z.string().default(""),
mdfcnDt: zDateNullish,
mdfr: z.string().default(''),
mdfr: z.string().default(""),
choose: z.boolean().default(false),
ruseUtztnFcltNm:z.string().nullish()
});
const dmdColist = z.object({
dmdInfoPk: z.string().default(''),
dmdCoNm: z.string().default(''),
dmdInfoPk: z.string().default(""),
dmdCoNm: z.string().default(""),
daddr: z.string(),
useUsgCd: z.string().nullish(),
splyBgngDay: zDateDefaultNow,
@@ -148,18 +153,18 @@ export const fcBizSchema = z.object({
pbnstRuseFcltFk: z.string().nullish(),
superkey: z.string().default("").nullish(),
cycl: zNum.default(1),
seCd: z.string().default(""),
seCd: z.string().max(10, "구분은 10자를 초과할 수 없습니다.").default(""),
bizMthdCd: z.string().default(""),
instlAprvSncnDt: zDateDefaultNow,
bizCmcnDay: zDateDefaultNow,
gvsbs: zNumDefaultZero,
lcep: zNumDefaultZero,
cusrAlotm: zNumDefaultZero,
etc: z.string().default(''),
etc: z.string().default(""),
totalMoney: zNumNullish.default(0),
rprcsFcltCpct: zNumDefaultZero,
majorPrcsMsrcCd: z.string().default(""),
addr: z.string().default(''),
addr: z.string().default(""),
rprcsMthdCd: z.string().default(""),
inoutYn: z.enum(["Y", "N"]).default("Y"),
mainSplyPumpCpct: zNumNullish,
@@ -171,17 +176,29 @@ export const fcBizSchema = z.object({
wwtEmsnFcltPrmsnYn: z.string().default("Y"),
tmsInstlYn: z.string().default("Y"),
pbnstDrngInflwExclYn: z.string().default("Y"),
bod: zNumDefaultZero,
grssClfg: zNumDefaultZero,
combResdch: zNumDefaultZero,
trbt: zNumDefaultZero,
clIonQnt: zNumDefaultZero,
smll: zNumDefaultZero,
crmty: zNumDefaultZero,
tph: zNumDefaultZero,
tn: zNumDefaultZero,
ph: zNumDefaultZero,
cdtnv: zNumDefaultZero,
// bod: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "BOD 값은 5자리 미만이어야 합니다.")).nullish(),
// grssClfg: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총대장균군 값은 5자리 미만이어야 합니다.")).nullish(),
// combResdch: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "결합잔류염소 값은 5자리 미만이어야 합니다.")).nullish(),
// trbt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "탁도 값은 5자리 미만이어야 합니다.")).nullish(),
// clIonQnt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "염소이온량 값은 5자리 미만이어야 합니다.")).nullish(),
// smll: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "냄새 값은 5자리 미만이어야 합니다.")).nullish(),
// crmty: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "색도 값은 5자리 미만이어야 합니다.")).nullish(),
// tph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총인 값은 5자리 미만이어야 합니다.")).nullish(),
// tn: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총질소 값은 5자리 미만이어야 합니다.")).nullish(),
// ph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "수소이온농도 값은 5자리 미만이어야 합니다.")).nullish(),
// cdtnv: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "전기전도도 값은 5자리 미만이어야 합니다.")).nullish(),
bod: z.string().nullish(),
grssClfg: z.string().nullish(),
combResdch: z.string().nullish(),
trbt: z.string().nullish(),
clIonQnt: z.string().nullish(),
smll: z.string().nullish(),
crmty: z.string().nullish(),
tph: z.string().nullish(),
tn: z.string().nullish(),
ph: z.string().nullish(),
cdtnv: z.string().nullish(),
designCst: zNumNullish,
cstrnSpvsCst: zNumNullish,
fcltSbsdCst: zNumNullish,
@@ -217,7 +234,7 @@ export const fcBizSchema = z.object({
});
export const pbnstRegZod = z.object({
pbnstRuseFcltPk: z.string().default(''),
pbnstRuseFcltPk: z.string().default(""),
swtrmtFcltFk: z.string().default(""),
ruseUtztnFcltNm: z.string().default(""),
fcltCpct: zNumDefaultZero,
@@ -232,13 +249,12 @@ export const pbnstRegZod = z.object({
fcOpers: z.array(fcOperSchema).default([]).nullish(),
});
export const pbnstRegArrayZod = z.array(pbnstRegZod)
export const pbnstRegArrayZod = z.array(pbnstRegZod);
export type PbnstRegArrrayType = z.infer<typeof pbnstRegArrayZod>
export type PbnstRegArrrayType = z.infer<typeof pbnstRegArrayZod>;
export type fcBizType = z.infer<typeof fcBizSchema>;
export type fcOrgwtType = z.infer<typeof fcOrgwtSchema>;
export type ppInstlType = z.infer<typeof pplInstlSchema>;
export type operType = z.infer<typeof fcOperSchema>;
export type fcFileType = z.infer<typeof fcFileSchema>;
@@ -2,76 +2,29 @@ import type { TableUpperProps } from "@/app/store/common/type/header";
import { getFetch, postFectch, putFectch } from "@/components/lib/api";
import { z } from "zod";
import { makeMdfr, makeRstr } from "../bdstReg";
const validationMessages = {
required: (field: string) => `${field}을(를) 입력해주세요.`,
requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`,
yearFormat: '4자리 연도만 입력하세요',
};
import toast from "react-hot-toast";
import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "./type";
export const TEST_UUID= '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e'
const rainyObjSchema = z.object({
bdstReuseFcltFk: z.number().nullish(),
pntfColKnd: z.string().default(""),
pntfColArea: z.number().default(0),
pntfColKnd: z.string().min(1, { message: validationMessages.requiredSelect("종류") }),
pntfColArea: z.number().positive({ message: validationMessages.required("면적(m³)") }),
dtfcltCpct: z.number().default(0),
filtrTrtmfctYn: z.string().default("Y").nullish(),
filtrTrtmfctYn: z.string().default("Y"),
filtrTrtmfctCpct: z.number().nullish().default(0),
filtrTrtmfctPrs: z.string().default(""),
annRnwtQty: z.number().default(0),
oilqMeasMthd: z.string().default("").nullish(),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
}).refine((data) => data.pntfColKnd.length > 0, {
message: validationMessages.requiredSelect("종류"),
path: ["pntfColKnd"],
}).refine((data) => data.pntfColArea > 0, {
message: validationMessages.required("면적(m³)"),
path: ["pntfColArea"],
});
const reuseBuildingSchema = z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk:z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish(),
operMngCd: z.string().optional().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.string().default("Y"),
instlCstSprtAmt: z.number().nullish(),
grntchBdstCertYn: z.string().default("Y"),
grntchBdstCertYr: z.string().default("")
.refine((year)=> year.length===0 || /^\d{4}$/.test(year),{
message: validationMessages.yearFormat
}),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z.number().default(0),
lawTrgtYn: z.string().default("Y"),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
rainyObj: rainyObjSchema.default({})
}).refine((data) => data.bdstUsgCd > 0, {
message: validationMessages.requiredSelect("건축물용도"),
path: ["bdstUsgCd"],
});
const chgBuildingSchema = z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}),
archChgArea: z.string().default("-"),
bdstChgNm: z.string().default(""),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
}).refine(data => data.bdstChgNm.length > 0, {
message: validationMessages.requiredSelect("구분"),
path: ["bdstChgNm"]
});
const rainBuildingSchema = baseReuseBuildingSchema.extend({
rainyObj: rainyObjSchema,
})
export const rainZod = z.object({
bdstInfoPk: z.number().optional(),
@@ -85,7 +38,7 @@ export const rainZod = z.object({
archChgArea: z.number().default(0),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
reuseBuilding: z.array(reuseBuildingSchema).default([]),
reuseBuilding: z.array(rainBuildingSchema).default([]),
chgBuilding: z.array(chgBuildingSchema).default([])
})
@@ -219,15 +172,18 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = (
bdstInfoFk: chPK,
})
);
const chch= postFectch("/reuseFc/chgBuilding", makeCh);
const chch= postFectch("/reuseFc/chgBuilding", makeCh);
return chch;
}).then((chch: any) => {
if(chch.ok) {
alert("등록이 완료되었습니다.");
console.log('chch ', chch)
if(chch.status ===200) {
toast.success("등록이 완료되었습니다.");
}
})
}else{
alert("등록이 완료되었습니다.");
toast.success("등록이 완료되었습니다.");
}
}
const rainRes = await initFetch(code);
@@ -0,0 +1,45 @@
import { z } from "zod";
export const validationMessages = {
required: (field: string) => `${field}을(를) 입력해주세요.`,
requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`,
yearFormat: '4자리 연도만 입력하세요',
};
export const baseReuseBuildingSchema = z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish(),
operMngCd: z.string().optional().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.string().default("Y"),
instlCstSprtAmt: z.number().nullish(),
grntchBdstCertYn: z.string().default("Y"),
grntchBdstCertYr: z.string().default("").refine(
(year) => year.length === 0 || /^\d{4}$/.test(year),
{
message: validationMessages.yearFormat,
}
),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z.number().min(1, { message: validationMessages.requiredSelect("건축물용도") }).default(0),
lawTrgtYn: z.string().default("Y"),
rgtr: z.string().nullish(),
mdfr: z.string().nullish(),
});
export const chgBuildingSchema = z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}),
archChgArea: z.string().default("-"),
bdstChgNm: z.string().min(1, { message: validationMessages.requiredSelect("구분") }),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
});
@@ -1,10 +1,32 @@
import { getFetch, postFectch, putFectch } from "@/components/lib/api";
import { upHeadList, type chgBuilding } from "../rainReg";
import { upHeadList, type chgBuilding } from "../rainReg";
import type { TableUpperProps } from "@/app/store/common/type/header";
import { z } from "zod";
import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "../rainReg/type";
import { makeMdfr, makeRstr } from "../bdstReg";
const rswtrObjSchema = z.object({
bdstRuseFcltFk: z.number().nullish(),
wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }),
oblgTrgtWtrUsqty: z.number().nullish().default(0),
orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }),
filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }).positive({ message: validationMessages.required("처리시설 용량") }),
filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }),
filtrTrtmfctUsgCd: z.string().default("").nullish(),
rswtrUsqty: z.number().default(0),
altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값
altUswtrKndCd: z.string().default("").nullish(), //안쓰는값
altUswtrUtztnQnt: z.number().default(0), //안쓰는값
altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
});
const rswtBuildingSchema = baseReuseBuildingSchema.extend({
rswtrObj: rswtrObjSchema,
})
export const rswtZod = z.object({
bdstInfoPk: z.number().nullish(),
ctpvCd: z.number().default(0),
@@ -21,73 +43,8 @@ export const rswtZod = z.object({
bdstUsgText: z.string().default(""),
cityText: z.string().default(""),
sggText: z.string().default(""),
reuseBuilding: z
.array(
z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().default(0),
operMngCd: z.string().nullish().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.enum(["Y", "N"]),
instlCstSprtAmt: z.number().default(0),
grntchBdstCertYn: z.enum(["Y", "N"]),
grntchBdstCertYr: z
.string()
.default("")
.refine((year) => year.length === 0 || /^\d{4}$/.test(year), {
message: "4자리 연도만 입력하세요",
}),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), // 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z.number().default(0),
lawTrgtYn: z.string().default(""),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
rswtrObj: z
.object({
bdstRuseFcltFk: z.number().nullish(),
wtrUsqty: z.number().default(0),
oblgTrgtWtrUsqty: z.number().nullish().default(0),
orgwtKndCd: z.string().default(""),
filtrTrtmfctCpct: z.number().default(0),
filtrTrtmfctProcs: z.string().default(""),
filtrTrtmfctUsgCd: z.string().default("").nullish(),
rswtrUsqty: z.number().default(0),
altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값
altUswtrKndCd: z.string().default("").nullish(), //안쓰는값
altUswtrUtztnQnt: z.number().default(0), //안쓰는값
altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
})
.default({}),
})
)
.default([]),
chgBuilding: z
.array(
z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date(),
archChgArea: z.string().default("-"),
bdstChgNm: z.string().default(""),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
})
)
.default([]),
reuseBuilding: z.array(rswtBuildingSchema).default([]),
chgBuilding: z.array(chgBuildingSchema).default([]),
});
export type customRswtBdst = z.infer<typeof rswtZod>;
+7 -5
View File
@@ -86,7 +86,7 @@ import {
import { rswtChkStateExport, rswtChkSttnActionsExport, type rswtChkSttnState, type rswtChkSttnType } from "./prfmncSttn/chkgdnSttn/rswtChkgdnSttn";
import { pbnstSttnActionsExport, pbnstSttnStateExport, type pbnstSttnState, type pbnstSttnType } from "./ruseFcltMng/fcltSttn/pbnstSttn";
import { coSttnActionsExport, coSttnStateExport, type coSttnState, type coSttnType } from "./matDmd/coSttn"; // 옮겨야함
import { searchActions, type comFcltActions, type comFcltState } from "./common/searchCode";
import { comFcltStateExport, comFcltSearchAction, type comFcltState, type comFcltType } from "./common/searchCode";
import { pbnstPrfmncActionsExport, pbnstPrfmncStateExport, type pbnstPrfmncState, type pbnstPrfmncType } from "./prfmncReg/pbnstPrfmncReg";
import { coRvwActionsExport, coRvwStateExport, type coRvwState, type coRvwType } from "./matDmd/coMatRvw";
import { ruseHstryActionsExport, ruseHstryStateExport, type ruseHstryState, type ruseHstryType } from "./matDmd/ruseRvwHstry";
@@ -113,7 +113,6 @@ export type WrStore = comCodeType &
rainChkSttnType&
rswtChkSttnType&
pbnstSttnType&
comFcltActions&comFcltState&
coSttnType& // 옮겨야함
ldrmntType& // 옮겨야함
pbnstPrfmncType&
@@ -124,7 +123,8 @@ export type WrStore = comCodeType &
ruseCaseType&
rainStatType&
rswtStatType&
matStatType
matStatType&
comFcltType
;
export const StoreZus = (
@@ -141,6 +141,7 @@ export const StoreZus = (
rswtSttnState &
htdflSttnState &
rainChkSttnState&
comFcltState&
rswtChkSttnState &
pbnstSttnState&
coSttnState& // 옮겨야함
@@ -180,7 +181,8 @@ export const StoreZus = (
...ruseCaseStateExport,
...rainStatExport,
...rswtStatExport,
...matStatExport
...matStatExport,
...comFcltStateExport
}
) => {
return createStore<WrStore>()((set) => {
@@ -192,7 +194,7 @@ export const StoreZus = (
getEnvPartCd(set);
return {
...initState,
...searchActions(set),
...comFcltSearchAction(set),
...pubFacActionsExport(set),
...rainFacActionsExport(set),
...rswtFacActionsExport(set),
@@ -171,7 +171,7 @@ export default function BdstReg({ form }: { form: any }) {
<FormUi label="위치">
<Input
className={selectClass}
placeholder="1,000,000"
placeholder=""
type="search"
{...field}
/>
@@ -193,7 +193,7 @@ export default function BdstReg({ form }: { form: any }) {
onSelectValue={(val) => {
field.onChange(Number(val));
}}
value={field.value}
value={field.value??code.bdstUsgCd[0]?.val}
/>
</FormUi>
)}
@@ -208,7 +208,7 @@ export default function BdstReg({ form }: { form: any }) {
<DatePicker
mode={"single"}
field={{
value: field.value || new Date(),
value: field.value?? new Date(),
onChange: (date: Date | undefined) => field.onChange(date),
}}
selectClass={'w-full'}
@@ -226,7 +226,7 @@ export default function BdstReg({ form }: { form: any }) {
<Input
className={selectClass}
placeholder="1,000,000"
type="number"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
@@ -250,7 +250,7 @@ export default function BdstReg({ form }: { form: any }) {
onSelectValue={(val) => {
field.onChange(val);
}}
value={field.value}
value={field.value??code.yn[0]?.val }
/>
</FormUi>
)}
@@ -55,18 +55,15 @@ export function PbnstBuseForm({
code,
pbnst,
demandCompList,
getpbnstInsertIndex,
appendRuseFclt,
getDemandCompList,
getFcOrgwts,
regDemandCompany,
} = useWrStore((state) => state);
const { pbnstBizCycle, pbnstRes } = pbnst;
const { fcOrgwtsNmList } = pbnst;
const { fields: fcBizs, append: appendBiz } = useFieldArray({
control: form.control,
name: `items.${indexMng}.fcBizs`,
});
const [bizCycle, setBizCycle] = useState("1");
const [ppiSelectList, setPpiSelectList] =
useState<[{ name: string; val: SelectDataType[] }]>();
const [ppiYn, setPpiYn] = useState("Y");
@@ -79,25 +76,72 @@ export function PbnstBuseForm({
>([{ text: "없음", val: 0 }]);
const [isFcOrgwts, setIsFcOrgwts] = useState<boolean>(false);
const [sumCpct, setSumCpct] = useState<number>(0);
const [getDmdList, setGetFmdList] = useState<number>(0);
const [isDmdModal, setIsDmdModal] = useState<{
isMoal: boolean;
data: DemandCompType;
}>({ isMoal: false, data: {} as DemandCompType });
useEffect(() => {
const total =
const etc= isNaN(Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`)))
let total =
(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`) ?? 0) +
(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`) ?? 0) +
(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0);
(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0)
if( !etc ) {
total+=Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`))
}
form.setValue(`items.${indexMng}.fcBizs.${indexBiz}.totalMoney`, total);
}, [
form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`),
form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`),
form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`),
form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`)
]);
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), fcOrgwts?.length)
}
}
},[fcOrgwts])
useEffect(() =>{
if (fcOrgwtsNmList && fcOrgwtsNmList.length > 0) {
console.log('fcOrgwtsNmList ', fcOrgwtsNmList)
console.log('fcOrgwts ', fcOrgwts)
fcOrgwts?.forEach((item, index) => {
const orgwtNm = fcOrgwtsNmList.find(
(orgwt) => orgwt.bscFctCd === item.swtrmtFcltFk
)?.bscFctNm;
console.log('orgwtNm ', orgwtNm)
if (orgwtNm) {
form.setValue(
`items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts.${index}.ruseUtztnFcltNm`,
orgwtNm
);
}
});
}
},[fcOrgwtsNmList,fcOrgwts])
// fcBizs 데이터가 변경될 때마다 차수 선택 목록을 업데이트합니다.
useEffect(() => {
const cycleLength = fcBizs.length;
@@ -250,23 +294,6 @@ export function PbnstBuseForm({
setSumCpct(sum);
}, [form.formState, indexMng, indexBiz]);
// // 차수별 정보(fcBizs)가 하나도 없을 경우 플레이스홀더 UI를 보여줍니다.
// if (fcBizs.length === 0) {
// return (
// <div className="!mx-[1.6rem] !p-0r flex flex-col items-center justify-center h-64 text-gray-500 border rounded-md">
// <p className="text-lg">등록된 차수별 정보가 없습니다.</p>
// <p className="text-sm mb-4">아래 버튼을 눌러 신규 차수 정보를 등록해주세요.</p>
// <Button
// onClick={addRuseFcltObj}
// type="button"
// size={commonButonSize}
// className="mt-2"
// >
// 신규 차수 등록
// </Button>
// </div>
// );
// }
const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`);
return (
@@ -499,7 +526,7 @@ export function PbnstBuseForm({
) || []
).map((field, index) => (
<div
key={`fcDemands-${indexMng}-${indexBiz}`} // 이게 핵심!
key={`fcDemands-${indexMng}-${indexBiz}=${index}`} // 이게 핵심!
className="grid grid-cols-12 gap-4 mt-3"
>
<div key={index + "keySon1"} className="col-span-4">
@@ -664,6 +691,7 @@ export function PbnstBuseForm({
)}
/>
</div>
<br></br>
</div>
))}
</div>
@@ -674,7 +702,7 @@ export function PbnstBuseForm({
<UiModal
open={isFcOrgwts}
onOpenChange={(open: boolean) => setIsFcOrgwts(open)}
bodyClass={"w-[50vw] max-w-none h-[90vh]"}
bodyClass={"w-[70vw] max-w-none max-h-[90vh] overflow-y-auto"}
body={
<FcOrgwtsRegModal
closeModal={() => {
@@ -29,6 +29,10 @@ import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { Label } from "@common/business_components/ui/form/lib/label";
import React from "react";
import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm";
import {
handleCommaNumberChange,
formatCommaNumber,
} from "@/components/lib/func/commom";
export type FilesType = {
atflCd: string;
file: File;
@@ -46,13 +50,9 @@ export function PbnstMngForm({
indexMng: number;
setIndexMng: (index: number) => void;
}) {
const {
code,
pbnst,
pbnstInsert,
fileDelete,
fileDownLoad,
} = useWrStore((state) => state);
const { code, pbnst, pbnstInsert, fileDelete, fileDownLoad } = useWrStore(
(state) => state
);
const { pbnstRes } = pbnst;
const [regFileList, setRegFileList] = useState<fcFileType[] | null>(null);
@@ -215,16 +215,14 @@ export function PbnstMngForm({
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)
console.log(" fcltNmList ", fcltNmList);
if (fcltNmList) setFcltNm(fcltNmList);
}else{
} else {
setFcltNm(null);
}
}, [pbnst.pbnstRes]);
@@ -253,16 +251,14 @@ export function PbnstMngForm({
}
}, [code]);
useEffect(() =>{
// console.log(' fcltNm ', watchedItem?.ruseUtztnFcltNm)
},[watchedItem])
useEffect(() => {
// console.log(' fcltNm ', watchedItem?.ruseUtztnFcltNm)
}, [watchedItem]);
useEffect(() => {
console.log(" form indexMng ", indexMng);
}, [indexMng]);
}, [indexMng]);
return (
<div className=" !mx-[1.6rem] !p-0r">
<div>
@@ -297,8 +293,6 @@ export function PbnstMngForm({
render={({ field }) => (
<FormUi label="">
<Input
className="!text-2xl"
placeholder=""
type="text"
@@ -379,11 +373,12 @@ export function PbnstMngForm({
<Input
key={`items.${indexMng}.fcltCpct`}
placeholder=""
type="number"
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
if (formType !== "sttn")
handleCommaNumberChange(e, field.onChange);
}}
value={field.value ?? 0}
value={formatCommaNumber(field.value)}
readOnly={formType === "sttn" ? true : false}
/>
</FormUi>
@@ -401,11 +396,12 @@ export function PbnstMngForm({
<Input
key={`items.${indexMng}.ruseCpct`}
placeholder=""
type="number"
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
if (formType !== "sttn")
handleCommaNumberChange(e, field.onChange);
}}
value={field.value ?? 0}
value={formatCommaNumber(field.value)}
readOnly={formType === "sttn" ? true : false}
/>
</FormUi>
@@ -422,11 +418,12 @@ export function PbnstMngForm({
<Input
key={`items.${indexMng}.splyExtraCpct`}
placeholder=""
type="number"
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
if (formType !== "sttn")
handleCommaNumberChange(e, field.onChange);
}}
value={field.value ?? 0}
value={formatCommaNumber(field.value)}
readOnly={formType === "sttn" ? true : false}
/>
</FormUi>
@@ -2,17 +2,14 @@
import { useWrStore } from "@/app/store";
import type { formDetailType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/headList";
import { formH1Class } from "@/components/lib/class/comClass";
import { safeDate } from "@/components/lib/func/commom";
import { formatCommaNumber, handleCommaNumberChange, safeDate } from "@/components/lib/func/commom";
import {
DatePicker,
FormField,
FormUi,
SearchInputSelect,
SelectBox,
type SelectDataType,
} from "@common/business_components/ui";
import { Input } from "@common/components/ui";
import { useState } from "react";
export default function MngRegFormDetail({
formList,
@@ -55,9 +52,7 @@ export default function MngRegFormDetail({
name={fieldId}
render={({ field }) => (
<FormUi label={formHead.lable}>
{formHead.type === "select" ? (
<Input placeholder="" type="text" {...field} />
) : formHead.type === "date" ? (
{formHead.type === "date" ? (
<DatePicker
mode={"single"}
className={"rounded-lg !w-full"}
@@ -67,35 +62,54 @@ export default function MngRegFormDetail({
field.onChange(date),
}}
selectClass={"w-full"}
disabled={formHead.option === "disabled" ? true : false}
/>
) : formHead.type === "number" ? (
<Input
placeholder=""
type="number"
value={field.value ?? 0}
type="text"
onChange={(e) => {
field.onChange(Number(e.target.value));
handleCommaNumberChange(
e,
field.onChange
);
}}
readOnly={formHead.option === "readOnly"?true:false}
/>
) : formHead.type === "selectYn" ? (
<SelectBox
label={""}
selectClass={""}
className="w-full"
selectArray={code.yn}
onSelectValue={(val: string | number) =>
field.onChange(val)
}
value={field.value ?? "Y"}
value={formatCommaNumber(field.value)}
readOnly={formHead.option === "readOnly" ? true : false}
disabled={formHead.option === "disabled" ? true : false}
/>
) : formHead.type === "select" ? (
(() => {
const potentialOptions = formHead.selectData
? code[formHead.selectData as keyof typeof code]
: [];
return (
<SelectBox
label={""}
selectClass={""}
className="w-full"
selectArray={
Array.isArray(potentialOptions)
? potentialOptions
: []
}
onSelectValue={field.onChange}
value={field.value.trim()}
readOnly={formHead.option === "readOnly"}
/>
);
})()
) : (
<Input
placeholder=""
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={formHead.option === "readOnly"?true:false}
readOnly={formHead.option === "readOnly" ? true : false}
disabled={formHead.option === "disabled" ? true : false}
/>
)}
</FormUi>
@@ -46,30 +46,40 @@ export default function RainRegForm({
]);
const cleanRainData = (raw: any): z.infer<typeof rainZod> => {
const buildings =
raw?.reuseBuilding && raw.reuseBuilding.length > 0
? raw.reuseBuilding
: [{}];
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
frstArchPrmsnDt: raw?.frstArchPrmsnDt
? new Date(raw.frstArchPrmsnDt)
: new Date(), // raw?.frstArchPrmsnDt가 유효하지 않으면 현재 날짜로 초기화
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
reuseBuilding: buildings.map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
instlCmptnDt: item?.instlCmptnDt
? new Date(item.instlCmptnDt)
: new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화
operMngCd: item?.operMngCd ?? "", // null → ""
grntchBdstCertYr:
typeof item?.grntchBdstCertYr === "string"
? item?.grntchBdstCertYr
: "", // null → ""
// Ensure enum fields are null if empty string to trigger Zod default
instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y"
grntchBdstCertYn:item?.grntchBdstCertYn??"Y",
rainyObj: {
...item?.rainyObj,
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn === "" ? null : item?.rainyObj?.filtrTrtmfctYn,
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y",
},
})),
};
};
useEffect(() => {
if (grntchBdstCertYn === "N") {
form.setValue("reuseBuilding.0.grntchBdstCertYr", "");
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
@@ -82,6 +92,9 @@ export default function RainRegForm({
}
}, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]);
// form 값이 변경될 때마다 로그 출력
useEffect(() => {
const subscription = form.watch((value) => {
@@ -146,9 +159,8 @@ export default function RainRegForm({
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(), // Provide a default Date value
onChange: (date) => field.onChange(date),
: new Date(),
onChange: (date: Date | undefined) => field.onChange(date),
}}
selectClass={"w-full"}
readOnly={readonly}
@@ -245,7 +257,7 @@ export default function RainRegForm({
label={""}
selectArray={code.yn}
onSelectValue={readonly ? () => { } : field.onChange}
value={field.value || code.yn[0]?.val}
value={field.value ?? code.yn[0]?.val}
readOnly={readonly}
/>
</FormUi>
@@ -266,7 +278,8 @@ export default function RainRegForm({
if (!readonly) handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={filtrTrtmfctYn !== "Y"}
readOnly={readonly }
disabled={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
)}
@@ -284,7 +297,8 @@ export default function RainRegForm({
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={filtrTrtmfctYn !== "Y"}
readOnly={readonly }
disabled={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
)}
@@ -28,10 +28,12 @@ import BdstReg from "../bdstReg/bdstReg";
export default function RswtrRegForm({
rswtForm,
isModal,
setIsModal,
readonly = false
}: {
rswtForm: customRswtBdst;
isModal: boolean;
setIsModal?: (isModal: boolean) => void;
readonly?: boolean;
}) {
const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore(
@@ -54,6 +56,7 @@ export default function RswtrRegForm({
const onSubmit = (values: z.infer<typeof rswtZod>) => {
if (isModal) {
rswtBdstReg(values, "41");
if(setIsModal) setIsModal(false)
} else {
rswtBdstUpdate(values, "41");
}
@@ -61,28 +64,29 @@ export default function RswtrRegForm({
const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
frstArchPrmsnDt: raw?.frstArchPrmsnDt
? new Date(raw.frstArchPrmsnDt)
: new Date(), // raw?.frstArchPrmsnDt가 유효하지 않으면 현재 날짜로 초기화
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
operMngCd: item?.operMngCd ?? "", // null → ""
grntchBdstCertYr:
typeof item?.grntchBdstCertYr === "string"
? item?.grntchBdstCertYr
: "", // null → ""
rswtrObj: {
instlCmptnDt: item?.instlCmptnDt
? new Date(item.instlCmptnDt)
: new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화
operMngCd: item?.operMngCd ?? "", // null → ""
instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y"
grntchBdstCertYn:item?.grntchBdstCertYn??"Y",
rswtrObj: {
...item?.rswtrObj,
filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y",
},
})),
};
};
useEffect(()=>{
console.log("rswtForm:", rswtForm)
}, [rswtForm])
useEffect(() => {
console.log("form errors:", form.formState.errors);
@@ -144,16 +144,16 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) {
const average = {
utztnUsgCd: "평균",
edit: false,
grssClfgUswtr: total.grssClfgUswtr / onlyData.length,
combResdch: total.combResdch / onlyData.length,
trbt: total.trbt / onlyData.length,
bod: total.bod / onlyData.length,
smll: total.smll / onlyData.length,
crmty: total.crmty / onlyData.length,
tph: total.tph / onlyData.length,
tn: total.tn / onlyData.length,
ph: total.ph / onlyData.length,
clIonQnt: total.clIonQnt / onlyData.length,
grssClfgUswtr: (total.grssClfgUswtr / onlyData.length).toFixed(1),
combResdch: (total.combResdch / onlyData.length).toFixed(1),
trbt: (total.trbt / onlyData.length).toFixed(1),
bod: (total.bod / onlyData.length).toFixed(1),
smll: (total.smll / onlyData.length).toFixed(1),
crmty: (total.crmty / onlyData.length).toFixed(1),
tph: (total.tph / onlyData.length).toFixed(1),
tn: (total.tn / onlyData.length).toFixed(1),
ph: (total.ph / onlyData.length).toFixed(1),
clIonQnt: (total.clIonQnt / onlyData.length).toFixed(1),
};
if (!resultPolution[nodeKey]) {
@@ -1,48 +1,62 @@
"use client"
import { btnClass } from "@/app/store/common/code"
import type { customDrgt } from "@/app/store/matDmd/drgtSttn"
import { drgtDetailHeadListExport } from "@/app/store/matDmd/drgtSttn/headList"
import { ContentsDiv, Title, UiTable } from "@common/business_components/ui"
import { excelDownload } from "@/components/lib/func/commom"
import { Title, UiTable } from "@common/business_components/ui"
import ListDiv from "@common/business_components/ui/list_div"
import { Button } from "@common/components/ui"
import { useCallback, useEffect } from "react"
export default function DrgtDetailModal({
data,
}: {
data: customDrgt;
}) {
const excelClick = useCallback(() => {
excelDownload(
[[]],
"우리동네가뭄",
"sample_drgtSttn.xlsx",
"A1:H1"
);
}, []);
export default function DrgtDetailModal() {
return (
<>
<ContentsDiv>
<div className="grid grid-cols-4 gap-4 ">
<div className="col-span-4">
<Title title={"우리동네가뭄 상세내용"} />
<div className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
className={`${btnClass} green`}
onClick={() => { }}
>
다운로드
</Button>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
tableData={[]}
headlist={drgtDetailHeadListExport}
pageSize={10}
total={10000}
cellClick={(index: number | undefined, headId: string) => { }}
onPageChange={() => { }}
></UiTable>
</ListDiv>
</div>
</div>
<div className="grid grid-cols-4 gap-4 h-full">
<div className="col-span-4">
<div className="flex items-center justify-between">
<Title title={`우리동네가뭄 상세내용 - ${data[0].sggNm}`} />
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
className={`${btnClass} green mt-12`}
onClick={excelClick}
>
다운로드
</Button>
</div>
</div>
<hr className="mt-2 mb-2" />
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
tableData={data}
headlist={drgtDetailHeadListExport}
pageSize={10}
total={10000}
cellClick={(index: number | undefined, headId: string) => { }}
onPageChange={() => { }}
></UiTable>
</ListDiv>
</div>
</div>
</div>
</ContentsDiv>
</div>
</>
)
}
@@ -0,0 +1,172 @@
import { coRegExcel } from "@/app/store/matDmd/coSttn/headList";
import type { ruseCaseMngExcelProps } from "@/app/store/matDmd/ruseCaseMng/index";
import { reUseCaseNames } from "@/app/store/matDmd/ruseCaseMng/excel";
import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui";
import { Form, FormField } from "@common/business_components/ui/form";
import { Button } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
import { useForm } from "react-hook-form";
import type { z } from "zod";
import { useWrStore } from "@/app/store";
export default function RuseCaseMngExcel() {
const { insertRuseCaseMng } = useWrStore((state) => state);
const form = useForm<z.infer<typeof coRegExcel>>({
resolver: zodResolver(coRegExcel),
});
const [file, setFiles] = useState<File | undefined>();
const onSubmit = () => {
if (file) {
filePicked(file, 0).then((res: unknown) => {
try {
const newArray: ruseCaseMngExcelProps[] = []
const dataArray = Array.isArray(res) ? res : [];
dataArray.forEach((item: Record<string, unknown>) => {
const object: ruseCaseMngExcelProps = {
ruseBizNm: '',
bizBgngDate: '',
bizEndDate: '',
splyFcltKnd: '',
splyFcltNm: '',
splyFcltCpct: 0,
dmdFcltNm: '',
dmdFcltAddr: '',
ruseFcltCpct: 0,
ruseFcltPplLen: '',
ruseFcltUsg: '',
ruseFcltSplyCpct: 0,
ruseFcltPrcsMsrc: '',
grnteWqUsg: '',
grnteWqGrssClfg: 0,
grnteWqCombResdch: 0,
grnteWqTrbt: '',
grnteWqBod: '',
grnteWqSmll: '',
grnteWqCrmty: '',
grnteWqTn: '',
grnteWqTp: '',
grnteWqPh: '',
grnteWqClIonQnt: '',
grnteWqCndctStat: '',
grnteWqRmrk: '',
reqBizReuseFcltCst: 0,
reqBizPplCst: 0,
reqBizArchCst: 0,
reqBizMchnCst: 0,
reqBizMtrngCst: 0,
reqBizGisCst: 0,
reqBizCctvCst: 0,
reqBizCmcnCst: 0,
reqBizWstCst: 0,
reqBizDesignCst: 0,
reqBizSpvsCst: 0,
reqBizSbsdCst: 0,
reqBizEtc: '',
reqBizGrssCst: 0,
prcmPlanNlep: 0,
prcmPlanLcep: 0,
prcmPlanBrdnCst: 0,
prcmPlanEcqrRt: 0,
prcmPlanGrssCst: 0,
clclnNlep: 0,
clclnLcep: 0,
clclnBrdnCst: 0,
clclnEcqrRt: 0,
clclnGrssCst: 0,
};
Object.keys(reUseCaseNames).forEach((key: string) => {
if (item && reUseCaseNames[key] !== undefined) {
const mappedKey = reUseCaseNames[key];
if (mappedKey && object.hasOwnProperty(mappedKey)) {
let value;
if(value === "-"){
value = '';
}
if(key === "사업시작일" || key === "사업종료일"){
if(item[key]?.toString().split(".")[1]?.length === 1){
value = `${item[key]?.toString().split(".")[0] || ""}${item[key]?.toString().split(".")[1]?.length === 1 ? "0" : ""}${item[key]?.toString().split(".")[1] || ""}`
}else{
value = `${item[key]?.toString().split(".")[0] || ""}${item[key]?.toString().split(".")[1] || ""}`
}
}else if(key === "재이용시설_관로(km)" || key?.toString().includes("보증수질_")){
value = item[key]?.toString().trim() || '';
}else{
if(item[key]?.toString().includes("%") && Number(item[key]?.toString().replaceAll("%", ""))){
value = item[key]?.toString().replaceAll("%", "");
}else{
value = item[key];
}
}
if(value === null || value?.toString().trim() === "-" || value?.toString().trim() === ""){
if (typeof object[mappedKey as keyof ruseCaseMngExcelProps] === 'number') {
(object as unknown as Record<string, unknown>)[mappedKey] = '';
} else {
(object as unknown as Record<string, unknown>)[mappedKey] = '';
}
}else if (typeof object[mappedKey as keyof ruseCaseMngExcelProps] === 'number' && Number(value?.toString().replaceAll("%", "").replaceAll(",", ""))) {
(object as unknown as Record<string, unknown>)[mappedKey] = Number(value?.toString().replaceAll("%", "").replaceAll(",", ""));
}else{
(object as unknown as Record<string, unknown>)[mappedKey] = value?.toString().trim() || '';
}
}
}
});
newArray.push(object);
});
console.log("newArray : ", newArray);
insertRuseCaseMng(newArray);
} catch (e) {
console.log(e)
}
});
};
}
return (
<>
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<Title title={"물재이용 사례 등록"}></Title>
</div>
<div className="flex justify-between">
<h3 className="text-2xl font-bold text-gray-900 mt-1 ml-7">
{"물재이용 사례 파일"}
</h3>
<Button className="" type="submit" size={"sm"}>
저장
</Button>
</div>
<div>
<FormField
control={form.control}
name="file"
render={() => (
<FormUi label="">
<FileUpload
files={file ? [file] : []}
setFiles={(files: File[] | null) => {
console.log("files", files)
if (files && files?.length === 1) setFiles(files[0]);
}}
fileTypes=""
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
</div>
</form>
</Form>
</div>
</>
);
}
@@ -1,12 +1,12 @@
import { useWrStore } from "@/app/store";
import { coRegExcel } from "@/app/store/matDmd/coSttn/headList";
import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn";
import { excelToLdrmntData, names } from "@/app/store/matDmd/ldrmntCoSttn/excel";
import { names } from "@/app/store/matDmd/ldrmntCoSttn/excel";
import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui";
import { Form, FormField } from "@common/business_components/ui/form";
import { Button, Input } from "@common/components/ui";
import { Button } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import type { z } from "zod";
@@ -26,34 +26,41 @@ export default function CoRegExcel() {
ldrmntDelete();
if (file) {
filePicked(file, 0).then((res) => {
//TODO:loading
try {
console.log("res", res)
let array: { [key: string]: string | number }[] = []
if (Array.isArray(res))
res.forEach((item, index) => {
let isError = false;
if (Array.isArray(res)){
res.some((item, index) => {
let object: { [key: string]: string | number } = {}
Object.keys(names).forEach((key) => {
Object.keys(names).some((key) => {
if (item && names[key] !== undefined) {
if (key === "공장관리번호")
if (key === "공장관리번호") {
item[key] = String(item[key])
object[names[key] as string] = item[key];
} else {
object[names[key] as string] = item[key];
}
}
return false;
})
if(isError === true)
return true;
array.push(object)
if (index != 0 && index % 5000 == 0) {
ldrmntRegData(array);
console.log("array", array)
array = []
array.length = 0;
// throw new Error("error")
}
return false;
})
}
if(isError === false)
ldrmntRegData(array);
console.log("array끝", array)
} catch (e) {
console.log(e)
alert(e)
return true;
}
});
@@ -61,47 +68,46 @@ export default function CoRegExcel() {
}
return (
<>
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<Title title={"잠재수요처 등록"}></Title>
</div>
<div className="flex justify-between">
<h3 className="text-2xl font-bold text-gray-900 mt-1 ml-7">
{"잠재수요처 파일"}
</h3>
return (
<>
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<Title title={"잠재수요처 등록"}></Title>
</div>
<div className="flex justify-between">
<h3 className="text-2xl font-bold text-gray-900 mt-1 ml-7">
{"잠재수요처 파일"}
</h3>
<Button className="" type="submit" size={"sm"}>
저장
</Button>
</div>
<Button className="" type="submit" size={"sm"}>
저장
</Button>
</div>
<div>
<div>
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormUi label="">
<FileUpload
files={file ? [file] : []}
setFiles={(files: File[] | null) => {
console.log("files", files)
if (files && files?.length === 1) setFiles(files[0]);
}}
fileTypes=""
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
</div>
</form>
</Form>
</div>
</>
);
}
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormUi label="">
<FileUpload
files={file ? [file] : []}
setFiles={(files: File[] | null) => {
if (files && files?.length === 1) setFiles(files[0]);
}}
fileTypes=""
maxFiles={1}
></FileUpload>
</FormUi>
)}
/>
</div>
</form>
</Form>
</div>
</>
);
}
@@ -14,7 +14,6 @@ const {bdst,rain} = useWrStore((state) => {
return state;
});
const { bdstChgHeadList } = bdst;
console.log("chgBuilding", chgBuilding);
return (
@@ -29,9 +29,10 @@ export type objSearchType = {
interface BdstRegProps<T extends MoType> {
moType: T;
setIsModal?: (open: boolean) => void;
}
export function BdstReg<T extends MoType>({ moType }: BdstRegProps<T>) {
export function BdstReg<T extends MoType>({ moType,setIsModal }: BdstRegProps<T>) {
const { rain, code, bdst,findBdst } = useWrStore((state) => {
return state;
});
@@ -143,7 +144,7 @@ export function BdstReg<T extends MoType>({ moType }: BdstRegProps<T>) {
{ moType === 'RNB'?
<RainRegForm rainForm={rainBdstForm as customRainBdst} isModal={true} />
:moType==='RST'
? <RswtrRegForm rswtForm={rainBdstForm as customRswtBdst} isModal={true}></RswtrRegForm>
? <RswtrRegForm rswtForm={rainBdstForm as customRswtBdst} isModal={true} setIsModal={(open) =>{setIsModal}}></RswtrRegForm>
:<HtdflRegForm htdflForm={rainBdstForm as unknown as customHtdflBdst} isModal={true}></HtdflRegForm>
}
@@ -1,6 +1,10 @@
"use client";
import { useWrStore } from "@/app/store";
import type { pubHasuType, fcBizType, fcOrgwtType,} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import type {
pubHasuType,
fcBizType,
fcOrgwtType,
} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import type { TableUpperProps } from "@/app/store/common/type/header";
import { tableData } from "@/components/lib/func/commom";
import RuseMngTable from "@/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable";
@@ -11,138 +15,161 @@ import { Input } from "@common/components/ui";
import { ArrowLeft, ArrowRight } from "lucide-react";
import { useEffect, useRef, useState } from "react";
export default function FcOrgwtsRegModal({ onRightSet, closeModal }: { onRightSet?: (rightData:fcOrgwtType[]) => void,closeModal?:(val:boolean) =>void }) {
const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore((state) => state);
export default function FcOrgwtsRegModal({
onRightSet,
closeModal,
}: {
onRightSet?: (rightData: fcOrgwtType[]) => void;
closeModal?: (val: boolean) => void;
}) {
const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore(
(state) => state
);
const { fcOrgwtsHeadList, pbnstHasuRes } = pbnst;
const [bdstTable, setbdstTable] = useState<pubHasuType[]>();
const rightRef = useRef<pubHasuType[] | null>([] as pubHasuType[]);
const leftRef = useRef<pubHasuType[] | null>([] as pubHasuType[]);
const [rightData, setRightData] = useState<pubHasuType[]>();
const [inputVal, setInputVal] = useState<string>("");
useEffect(()=>{
if(Array.isArray(searchFclts)){
useEffect(() => {
if (Array.isArray(searchFclts)) {
const filteredData = searchFclts.filter((item) => {
if(rightData){
if(rightData.find(rightItem =>rightItem.bscFctCd===item.bscFctCd))
if (rightData) {
if (
rightData.find((rightItem) => rightItem.bscFctCd === item.bscFctCd)
)
return false;
}
return true
})
setbdstTable(Array.isArray(filteredData) ? [...filteredData] : [])
return true;
});
setbdstTable(Array.isArray(filteredData) ? [...filteredData] : []);
}
}, [searchFclts])
}, [searchFclts]);
useEffect(() => {
if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) {
setbdstTable(tableData(pbnstHasuRes, { code }));
console.log('bdstTable ', bdstTable)
console.log("bdstTable ", bdstTable);
}
}, [code, pbnstHasuRes]);
useEffect(() => {
console.log('rightData ', rightData)
if(onRightSet)
onRightSet(
(rightData as pubHasuType[])?.map(item => ({
swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정
orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정
tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정
dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정
dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정
choose: false, // 임시값, 실제 데이터에 맞게 수정
// 필요한 경우 다른 속성들도 추가
})) as unknown as fcOrgwtType[]
);
}, [rightData])
console.log("rightData ", rightData);
if (onRightSet)
onRightSet(
(rightData as pubHasuType[])?.map((item) => ({
swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정
orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정
tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정
dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정
dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정
choose: false, // 임시값, 실제 데이터에 맞게 수정
// 필요한 경우 다른 속성들도 추가
})) as unknown as fcOrgwtType[]
);
}, [rightData]);
return (
<>
<div className="flex flex-col h-full">
<div className="flex items-center justify-between">
<Title title={"취수하수처리장 추가"} />
<Button className="m-2" size={"sm"}
onClick={() => {closeModal && closeModal(false) }}>저장</Button>
</div>
<div className="flex flex-col h-[80vh]">
<div className="shrink-0 p-4 border-b z-10 bg-white">
<div className="flex items-center">
<Title title="취수하수처리장 추가" />
{/* <Button size="sm" onClick={() => closeModal?.(false)}>저장</Button> */}
<div className="flex items-center gap-2 mt-8">
<div className="flex flex-row flex-grow gap-4 mt-4">
<div className="w-3/7">
<div className="flex items-center justify-between">
<Label className="w-full text-2xl text-gray-600 text-left">공공하수처리시설 목록</Label>
<div className="flex items-center">
<Label className="w-full text-xl text-gray-600 text-right">시설명</Label>
<Input className="w-1/2 !text-xl ml-2" type="text" value={inputVal} onChange={(e) => {setInputVal(e.target.value)}} />
<Button className="m-2" size={"sm"} onClick={() => getSwtrmtFcltNm(inputVal)}>조회</Button>
</div>
</div>
<div className="border-t border-gray-300 my-4"></div>
<RuseMngTable
tableData={bdstTable || []}
headList={fcOrgwtsHeadList}
upHeadList={[]} // TODO: Replace with actual upHeadList data if available
ref={leftRef}
/>
</div>
<div className="flex flex-col items-center justify-center w-1/7 gap-2">
<ArrowRight
type="button"
onClick={() => {
if (leftRef && typeof leftRef === "object" && "current" in leftRef) {
const checked = leftRef.current?.filter((item) => ((item as unknown as pubHasuType)?.choose))
const unchecked = leftRef.current?.filter((item) => !(item as unknown as pubHasuType)?.choose)
if (checked && checked.length > 0) {
checked.forEach(item=>(item as unknown as pubHasuType).choose = false)
setRightData([...(checked as unknown as pubHasuType[]),
...((rightRef.current ?? []) as unknown as pubHasuType[])
])
}
if (unchecked && unchecked.length > 0) {
setbdstTable([...(unchecked as unknown as pubHasuType[])])
}else{
setbdstTable([])
}
}
}
}
className="w-12 h-12 text-gray-500 cursor-pointer"
/>
<ArrowLeft
type="button"
onClick={() => {
if (rightRef && typeof rightRef === "object" && "current" in rightRef) {
const checked = rightRef.current?.filter((item) => ((item as unknown as pubHasuType)?.choose))
const unchecked = rightRef.current?.filter((item) => !(item as unknown as pubHasuType)?.choose)
if (checked && checked.length > 0) {
checked.forEach(item=>(item as unknown as pubHasuType).choose = false)
setbdstTable([
...(checked as unknown as pubHasuType[]),
...((leftRef.current ?? []) as unknown as pubHasuType[])
])
}
if (unchecked && unchecked.length > 0) {
setRightData([...(unchecked as unknown as pubHasuType[])])
}else{
setRightData([])
}
}
}}
className="w-12 h-12 text-gray-500 cursor-pointer mt-5" />
</div>
{/* Right Table container: Takes 2/5 of the width */}
<div className="w-3/7">
<Label className="w-full text-2xl text-gray-600 text-right">취수하수처리장 목록</Label>
<div className="border-t border-gray-300 my-4"></div>
<RuseMngTable
tableData={rightData || []}
headList={fcOrgwtsHeadList}
upHeadList={[]} // TODO: Replace with actual upHeadList data if available
ref={rightRef}
/>
<Label className="text-xl text-gray-600">시설명</Label>
<Input
className="w-48 text-xl"
type="text"
value={inputVal}
onChange={(e) => setInputVal(e.target.value)}
/>
<Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}>
조회
</Button>
</div>
</div>
</div>
</>
{/* <div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Label className="text-xl text-gray-600">시설명</Label>
<Input
className="w-48 text-xl"
type="text"
value={inputVal}
onChange={(e) => setInputVal(e.target.value)}
/>
<Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}>조회</Button>
</div>
</div>
</div> */}
{/* 테이블 영역 */}
<div className="flex flex-row gap-4 flex-grow p-4 overflow-x-auto">
{/* 왼쪽 테이블 */}
<div className="w-5/12 min-w-[400px]">
<Label className="text-2xl text-gray-600 mb-2 block text-right">
공공하수처리시설 목록
</Label>
<RuseMngTable
tableData={bdstTable || []}
headList={fcOrgwtsHeadList}
upHeadList={[]}
ref={leftRef}
/>
</div>
{/* 중간 이동 버튼 */}
<div className="flex flex-col justify-center items-center gap-4 w-[200px] min-w-[120px]">
<ArrowRight
onClick={() => {
const checked = leftRef.current?.filter((item) => item.choose);
const unchecked = leftRef.current?.filter((item) => !item.choose);
if (checked?.length) {
checked.forEach((item) => (item.choose = false));
setRightData([
...(checked as pubHasuType[]),
...(rightRef.current ?? []),
]);
}
setbdstTable(unchecked ?? []);
}}
className="w-8 h-8 text-gray-500 cursor-pointer"
/>
<ArrowLeft
onClick={() => {
const checked = rightRef.current?.filter((item) => item.choose);
const unchecked = rightRef.current?.filter(
(item) => !item.choose
);
if (checked?.length) {
checked.forEach((item) => (item.choose = false));
setbdstTable([
...(checked as pubHasuType[]),
...(leftRef.current ?? []),
]);
}
setRightData(unchecked ?? []);
}}
className="w-8 h-8 text-gray-500 cursor-pointer"
/>
</div>
{/* 오른쪽 테이블 */}
<div className="w-5/12 min-w-[400px]">
<Label className="text-2xl text-gray-600 mb-2 block text-right">
취수하수처리장 목록
</Label>
<RuseMngTable
tableData={rightData || []}
headList={fcOrgwtsHeadList}
upHeadList={[]}
ref={rightRef}
/>
</div>
</div>
</div>
);
}
@@ -20,13 +20,13 @@ import { useFieldArray, type UseFormReturn } from "react-hook-form";
import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg";
export function PbnstRegModal({
data,
form,
setPbnstInsert
}: {
data: pubHasuType;
form: UseFormReturn<{ items: PbnstRegArrrayType }>;
setPbnstInsert: (data: customPbnst) => void
}) {
const { pbnst, pbnstInsert } = useWrStore((state) => {
const { pbnstInsert } = useWrStore((state) => {
return state;
});
const [pbnstBdstForm, setPbnstBdstForm] = useState<customPbnst>(
@@ -35,40 +35,30 @@ export function PbnstRegModal({
const [currentTab, setCurrentTab] = useState<string>("mng");
const [indexMng, setIndexMng] = useState<number>(0);
const [indexBiz, setIndexBiz] = useState<number>(0);
const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => {
const item = valuse.items[indexMng];
const data = item ? writeRgtrAndMdfr([item]) : [];
const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => {
const item = valuse.items[indexMng];
const data = item ? writeRgtrAndMdfr([item]) : [];
if(data && data[0]){
pbnstInsert(data[0])
}
};
if (data && data[0]) {
// pbnstInsert(data[0]);
setPbnstInsert(data[0])
}
};
const handleTabChange = (value: string) => {
setCurrentTab(value);
};
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
};
useEffect(() => {
if (data) {
const firstForm = form.getValues("items");
setIndexMng(firstForm.length - 1) }
}, [data, form]);
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
console.log(' form.getValues() ' ,form.getValues())
}, [form.getValues()]);
const firstForm = form.getValues("items");
if (firstForm) {
setIndexMng(firstForm.length - 1);
}
}, [form]);
return (
<>
<h1 className="text-2xl font-medium text-label pl-8">
@@ -86,7 +76,7 @@ export function PbnstRegModal({
차수별정보
</TabsTrigger>
<Button
onClick={ form.handleSubmit(onSubmit)}
onClick={form.handleSubmit(onSubmit)}
size={"sm"}
className="!justify-end"
>
@@ -104,7 +94,7 @@ export function PbnstRegModal({
</TabsContent>
<TabsContent className="max-h-full" value="buse">
<PbnstBuseForm
form={form}
form={form}
indexMng={indexMng}
indexBiz={indexBiz}
setIndexBiz={setIndexBiz}
@@ -7,7 +7,7 @@ import { handleCommaNumberChange } from "@/components/lib/func/commom";
import { DatePicker, Form, FormField, FormUi, ScrollArea } from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import type { z } from "zod";
@@ -33,8 +33,24 @@ export default function SttnDetailModal({
useEffect(() => {
if (ldrmntZod) form.reset(formObj);
}, [formObj]);
useEffect(() => {
const male = Number(form.watch("maleEmpCnt") ?? 0);
const female = Number(form.watch("fmaleEmpCnt") ?? 0);
const frgnrMale = Number(form.watch("frgnrMaleEmpCnt") ?? 0);
const frgnrFemale = Number(form.watch("frgnrFmaleEmpCnt") ?? 0);
const total = male + female + frgnrMale + frgnrFemale;
form.setValue("totalEmpCnt", total);
}, [
form.watch("maleEmpCnt"),
form.watch("fmaleEmpCnt"),
form.watch("frgnrMaleEmpCnt"),
form.watch("frgnrFmaleEmpCnt"),
]);
return (
<div>
<ScrollArea className="h-[85vh]">
@@ -324,6 +340,7 @@ export default function SttnDetailModal({
handleCommaNumberChange(e, field.onChange);
}}
value={field.value ?? 0}
min={0}
/>
</FormUi>
)}
@@ -343,6 +360,7 @@ export default function SttnDetailModal({
field.onChange(e.target.value);
}}
value={field.value ?? 0}
min={0}
/>
</FormUi>
)}
@@ -362,6 +380,7 @@ export default function SttnDetailModal({
field.onChange(e.target.value);
}}
value={field.value ?? 0}
min={0}
/>
</FormUi>
)}
@@ -381,6 +400,7 @@ export default function SttnDetailModal({
field.onChange(e.target.value);
}}
value={field.value ?? 0}
min={0}
/>
</FormUi>
)}
@@ -400,6 +420,8 @@ export default function SttnDetailModal({
field.onChange(e.target.value);
}}
value={field.value ?? 0}
min={0}
readOnly={true}
/>
</FormUi>
)}
Binary file not shown.
@@ -0,0 +1,82 @@
'use client';
import { cn } from "../../lib/utils";
import {
Popover,
PopoverTrigger,
PopoverContent,
Button,
} from "@common/components/ui";
import { ko } from "date-fns/locale";
import { format } from "date-fns";
import { useState } from "react";
import { MonthPicker } from "./mothPicker";
import { CalendarIcon } from "lucide-react";
type MonthPickerProps = {
field: { value: string; onChange: (date: string) => void }; // yyyyMM 문자열
selectClass?: string;
readOnly?: boolean;
};
// yyyyMM → Date 객체
const parseYYYYMM = (value: string) => {
const year = Number(value.substring(0, 4));
const month = Number(value.substring(4, 6)) - 1;
return new Date(year, month);
};
// Date 객체 → yyyyMM 문자열
const formatDateToYYYYMM = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
return `${year}${month}`;
};
export const DateMonthPicker = ({
field,
selectClass,
readOnly = false,
}: MonthPickerProps) => {
const [open, setOpen] = useState(false);
const parsedDate = field?.value ? parseYYYYMM(field.value) : new Date();
const handleSelect = (
date: Date | undefined,
shouldClosePopover?: boolean
) => {
if (!readOnly && date) {
field.onChange(formatDateToYYYYMM(date));
if (shouldClosePopover) setOpen(false);
}
};
return (
<Popover open={open && !readOnly} onOpenChange={readOnly ? undefined : setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
color="white"
disabled={readOnly}
className={cn(
selectClass ?? "w-[240px] justify-between pl-3 text-left font-normal",
!field?.value && "text-muted-foreground",
readOnly && "opacity-60 cursor-not-allowed"
)}
>
{parsedDate
?format(parsedDate, "yyyy년 M월", { locale: ko })
: "월을 선택하세요"}
<CalendarIcon className="ml-2 h-4 w-4" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<MonthPicker
currentMonth={parsedDate}
onMonthChange={handleSelect}
/>
</PopoverContent>
</Popover>
);
};
@@ -0,0 +1,125 @@
import { cn } from "../../lib/utils";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@common/components/ui";
import {
eachMonthOfInterval,
endOfYear,
format,
isEqual,
isFuture,
parse,
startOfMonth,
startOfToday,
} from "date-fns";
import { ko } from "date-fns/locale";
function getStartOfCurrentMonth() {
return startOfMonth(startOfToday());
}
export type MonthPickerProps = {
currentMonth: Date;
onMonthChange: (
newMonth: Date | undefined,
shouldClosePopover?: boolean
) => void;
};
export function MonthPicker({ currentMonth, onMonthChange }: MonthPickerProps) {
const today = startOfToday();
const currentYear = today.getFullYear();
const yearFromProp = format(currentMonth, "yyyy");
const selectedYear = parseInt(yearFromProp);
// 현재 월의 시작 날짜
const fullYearStart = parse(yearFromProp, "yyyy", new Date());
const yearEnd =
selectedYear === currentYear ? today : endOfYear(fullYearStart);
const yearRange = Array.from({ length: 21 }, (_, i) => currentYear - 20 + i);
const months = eachMonthOfInterval({ start: fullYearStart, end: yearEnd });
const getMonthButtonClass = (month: Date) =>
cn(
"inline-flex h-9 w-16 items-center justify-center rounded-md p-0 text-sm font-normal ring-offset-white transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 aria-selected:opacity-100 dark:ring-offset-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:focus-visible:ring-slate-800",
isEqual(month, currentMonth) &&
"bg-slate-900 text-slate-50 hover:bg-slate-900 hover:text-slate-50 focus:bg-slate-900 focus:text-slate-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50 dark:hover:text-slate-900 dark:focus:bg-slate-50 dark:focus:text-slate-900",
!isEqual(month, currentMonth) &&
isEqual(month, getStartOfCurrentMonth()) &&
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
);
return (
<div className="p-3">
<div className="flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0">
<div className="space-y-4">
{/* 년도 셀렉트 */}
<div className="flex justify-center pt-1">
<Select
value={yearFromProp}
onValueChange={(selectedYearStr) => {
const selectedYearNum = parseInt(selectedYearStr);
const newDate = new Date(
selectedYearNum,
currentMonth.getMonth(),
1
);
onMonthChange(newDate, false);
}}
>
<SelectTrigger className="w-[120px] h-7">
<SelectValue placeholder="년도" />
</SelectTrigger>
<SelectContent>
{yearRange.map((year) => (
<SelectItem key={year} value={String(year)}>
{year}년
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 월 그리드 */}
<div
className="grid w-full grid-cols-3 gap-2"
role="grid"
aria-labelledby="month-picker"
>
{months.map((month) => (
<div
key={month.toString()}
className="relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-slate-100 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md dark:[&:has([aria-selected])]:bg-slate-800"
role="presentation"
>
<button
name="month"
className={getMonthButtonClass(month)}
disabled={isFuture(month)}
role="gridcell"
tabIndex={-1}
type="button"
onClick={() => onMonthChange(month, true)}
>
<time dateTime={format(month, "yyyy-MM-dd")}>
{format(month, 'MMM', { locale: ko })}
</time>
</button>
</div>
))}
</div>
</div>
</div>
</div>
);
}
@@ -1,4 +1,5 @@
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components";
import { cn } from "../../lib/utils";
type ModalType = {
bodyClass: string;
@@ -25,15 +26,20 @@ export function UiModal({
return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className={bodyClass}>
{noHeaderYn === 'N' && (
<DialogContent className={bodyClass}>
{noHeaderYn === 'N' && (
<DialogHeader>
<DialogDescription>
{headerTitle}
</DialogDescription>
</DialogHeader>
)}
{(open && body)}
{(open && body) ?
<div> {body}</div>:<></>
}
{noFooterYn === 'N' && (
<DialogFooter className="sm:justify-start">
{footerContent}
@@ -20,4 +20,5 @@ export * from "./table";
export * from "./table_div";
export * from "./title";
export * from "./sdmInsertForm";
export * from "./popover_ui"
export * from "./popover_ui";
export * from "./date_month_picker";