diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index e8146b37a..5e4bddaad 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -1,30 +1,83 @@ "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, + 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() + const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); + const [selectSgg, setSelectSgg] = useState([{val: "0", text: "전체"}]); const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); - const [selectUtz, setSelectUtz] = useState() - const [searchTxt, setSearchTxt] = useState() + + const [sidoCd, setSidoCd] = useState(""); + const [sigunguCd, setSigunguCd] = useState(""); + const [selectUtz, setSelectUtz] = useState(["조경용수"]); + const [name, setName] = useState(""); + // 물재이용사례 조회 + const searchRuseCaseMng = async () => { + console.log(selectUtz); + const res = await selectRuseCaseMng(sidoCd, sigunguCd, selectUtz, name); + console.log('res : ', res); + } + + // 시도 선택 const getSelCity = (val: string | number) => { const cityValue = String(val); + const cityText = code.cityCd.find((item: any) => item.cityCd === 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: any) => item.sggCd === sggValue)?.sggNm; + setSigunguCd(sggText || ""); + setObjSearch({ ...objSearch, sgg: String(val) }); }; + + // 엑셀 등록 모달 + const [isExcelOpen, setIsExcelOpen] = useState<{ + isModal: boolean; + }>({ isModal: false }); + + // 다운로드 + const download = () => { + console.log("다운로드"); + } + + // 양식받기 + const sampleDownLoad = () => { + excelDownload( + [[]], + "물재이용사례 관리", + "sample_ruseCaseMng.xlsx", + "A1:AX1", + 2 + ); + } + return ( <> @@ -54,47 +107,32 @@ export default function RuseCaseMng() { selectClass={""} selectArray={ruseUtzType} onSelectValue={(val) => { - if(val) - setSelectUtz([String(val)])}} + if (val) setSelectUtz([String(val)]); + }} /> - { - setSearchTxt(e) + setName(e); }} /> - - +
- - - -
@@ -103,24 +141,35 @@ export default function RuseCaseMng() {
-
+
- { - }} - onPageChange={() => { }} - > +
+ {}} + onPageChange={() => {}} + /> +
+ + {/* 엑셀 등록 모달 */} + { + setIsExcelOpen({ isModal: !isExcelOpen.isModal }) + }} + bodyClass={"w-[40vw] max-w-none"} + body={} + /> - ) + ); } \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx new file mode 100644 index 000000000..ebbaa8b7a --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx @@ -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; +}; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx index c54b0db1a..0b40c1826 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -1,24 +1,119 @@ +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:TableUpperProps[]; }; -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; + insertRuseCaseMng: (data: any) => Promise; +}; + +export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { + const tailUrl = (sidoCd && !sigunguCd ? `/${sigunguCd}` : sigunguCd ? `/${sigunguCd}` : '/0') + (usageCd ? `/${usageCd}` : '') + (name ? `/${name}` : ''); + console.log('tailUrl : ', tailUrl); + const resp = await getFetch(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`); + console.log('resp : ', resp); + return resp; +}; + +export const fetchInsertRuseCaseMng = async (data: any) => { + console.log('data : ', data) + 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: data, + }, + })); + }, + + insertRuseCaseMng: async (data: any) => { + const response = await fetchInsertRuseCaseMng(data); + if(response.status === 200){ + ToastFormUi({ + body: "잠재수요처 등록이 완료되었습니다. ", + title: "잠재수요처 등록", + }); + } + }, }; }; diff --git a/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx new file mode 100644 index 000000000..57424996e --- /dev/null +++ b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx @@ -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>({ + resolver: zodResolver(coRegExcel), + }); + + const [file, setFiles] = useState(); + + const onSubmit = () => { + if (file) { + filePicked(file, 0).then((res: unknown) => { + try { + const newArray: ruseCaseMngExcelProps[] = [] + const dataArray = Array.isArray(res) ? res : []; + dataArray.forEach((item: Record) => { + 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)[mappedKey] = ''; + } else { + (object as unknown as Record)[mappedKey] = ''; + } + }else if (typeof object[mappedKey as keyof ruseCaseMngExcelProps] === 'number' && Number(value?.toString().replaceAll("%", "").replaceAll(",", ""))) { + (object as unknown as Record)[mappedKey] = Number(value?.toString().replaceAll("%", "").replaceAll(",", "")); + }else{ + (object as unknown as Record)[mappedKey] = value?.toString().trim() || ''; + } + } + } + }); + newArray.push(object); + }); + console.log("newArray : ", newArray); + insertRuseCaseMng(newArray); + } catch (e) { + console.log(e) + } + }); + }; + } + + return ( + <> +
+
+ +
+ +
+
+

+ {"물재이용 사례 파일"} +

+ +
+ +
+ ( + + { + console.log("files", files) + if (files && files?.length === 1) setFiles(files[0]); + }} + fileTypes="" + maxFiles={1} + > + + )} + /> +
+
+ +
+ + ); +} \ No newline at end of file diff --git a/apps/wr/public/sample_ruseCaseMng.xlsx b/apps/wr/public/sample_ruseCaseMng.xlsx new file mode 100644 index 000000000..3a88e0623 Binary files /dev/null and b/apps/wr/public/sample_ruseCaseMng.xlsx differ