diff --git a/apps/wr/app/matMng/matDmd/coSttn/page.tsx b/apps/wr/app/matMng/matDmd/coSttn/page.tsx index 3900bf61f..146046bda 100644 --- a/apps/wr/app/matMng/matDmd/coSttn/page.tsx +++ b/apps/wr/app/matMng/matDmd/coSttn/page.tsx @@ -8,20 +8,25 @@ import { ContentsDiv, SearchBaseForm, Title, + UiModal, UiTable, type ReturnType } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; +import SdmInsertForm, { type DemandCompType } from "@common/business_components/ui/sdmInsertForm"; export default function coSttn() { const [bdstTable, setBdstTable] = useState(); - const [pageNation, setPageNation] = useState(1); - const { code, coSttn, coSttnInit } = useWrStore((state) => state); - const { coSttnRes } = coSttn; + + const [pageNation, setPageNation] = useState(1); + const { code, coSttn, coSttnInit, getDemandCompList,regDemandCompany,demandCompList} = useWrStore((state) => state); + const { coSttnRes } = coSttn; + const [isDmdModal, setIsDmdModal] = useState<{isMoal: boolean, data: DemandCompType}>({isMoal: false, data: {} as DemandCompType}) + const ref = useRef({} as ReturnType); const codeData = getCityOrSgg(ref); @@ -64,7 +69,7 @@ export default function coSttn() { diff --git a/apps/wr/app/store/common/code/index.ts b/apps/wr/app/store/common/code/index.ts index b02918872..3f6da6dec 100644 --- a/apps/wr/app/store/common/code/index.ts +++ b/apps/wr/app/store/common/code/index.ts @@ -91,6 +91,18 @@ const chBuCd = [ ] + +const dmdUszCd = [ + { val: "조경용수", text: "조경용수" }, + { val: "청소화장실용수", text: "청소화장실용수" }, + { val: "농업용수", text: "농업용수" }, + { val: "공업용수", text: "공업용수" }, + { val: "친수용수", text: "친수용수" }, + { val: "기타", text: "기타" }, + +] + + const pplQulity = [ { val: "도복장강관", text: "도복장강관" }, { val: "PE관", text: "PE관" }, @@ -136,6 +148,7 @@ export type comCodeState = { yn: SelectDataType[]; pplQulity: SelectDataType[]; pplLenght: SelectDataType[]; + dmdUszCd:SelectDataType[] buildingType: { RST:string, RNB:string, @@ -277,6 +290,7 @@ export const comCodeStateExport: comCodeState = { pplQulity:pplQulity, pplLenght:pplLenght, yn: yn, + dmdUszCd:dmdUszCd, buildingType:buildingType, }, }; diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts index 579c7c528..c1b46a901 100644 --- a/apps/wr/app/store/common/searchCode/index.ts +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -1,29 +1,52 @@ -import { fetchType, getFetch, postFectch } from "@/components/lib/api" -import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type" +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; +import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; +import type { DemandCompType } from "@common/business_components/ui"; export type comFcltState = { - searchFclts: pubHasuType -} + searchFclts: pubHasuType; + demandCompList: DemandCompType[]; +}; export type comFcltActions = { - getSwtrmtFcltNm: (searchNm: string) => void -} - + getSwtrmtFcltNm: (searchNm: string) => void; + getDemandCompList :(searchNm: string,cnt:number) => void + regDemandCompany:(data:DemandCompType ) => void +}; + export const searchActions: (set: any) => comFcltActions = (set: any) => { - return { - getSwtrmtFcltNm: async (searchNm: string) => { - const fcltNm = await getFetch("common", `/prcsFclt/info/1/20/${searchNm}`); - const fcltNmCd: pubHasuType[] = fcltNm.map((item: any) => ({ - bscFctCd: item.bscFctCd, - sido: item.sido, - gugun: item.gugun, - bscFctNm: item.bscFctNm, - addr: item.addr, - fctCpc: item.fctCpc - })) - return set((state: comFcltState) => ({ - ...state, - searchFclts: fcltNmCd - } - )) - }, + return { + getSwtrmtFcltNm: async (searchNm: string) => { + const fcltNm = await getFetch( + "common", + `/prcsFclt/info/1/20/${searchNm}` + ); + const fcltNmCd: pubHasuType[] = fcltNm.map((item: any) => ({ + bscFctCd: item.bscFctCd, + sido: item.sido, + gugun: item.gugun, + bscFctNm: item.bscFctNm, + addr: item.addr, + fctCpc: item.fctCpc, + })); + return set((state: comFcltState) => ({ + ...state, + searchFclts: fcltNmCd, + })); + }, + getDemandCompList: async (searchNm: string ,cnt:number) => { + const coList = await getFetch( + fetchType.WR, + `/reuseFc/request/${searchNm}/${cnt}` + ); + return set((state: comFcltState) => ({ + ...state, + demandCompList: coList, + })); + },regDemandCompany: async (data:DemandCompType)=>{ + + try{ + const response = await postFectch('/reuseFc/request',data,fetchType.WR ) + }catch(err){ + console.log(err) + } } -} \ No newline at end of file + }; +}; diff --git a/apps/wr/app/store/common/searchCode/type.tsx b/apps/wr/app/store/common/searchCode/type.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index 62a6447f7..22980aa16 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -5,6 +5,7 @@ import { fetchType, getFetch, postFectch, + putFectch, } from "@/components/lib/api"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { @@ -66,9 +67,9 @@ type pubFacActions = { pbnstMngData: (data: customPbnst[]) => void; pbnstBizData(data: fcBizType): void; getBizCycle: (pbnstObj: customPbnst) => void; - getDmdCompany: (txt: string) => void; getpbnstInsertIndex: (data: pbnstInsertIndex) => void; - searchDmdColist:(txt:string) =>void + pbnstRuseFcltUpdate: (ruseRes: customPbnst[]) => void; + pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void; }; export type pubFacType = pubFacState & pubFacActions; @@ -123,30 +124,40 @@ const initFetch = async (ruseFcltPk: string | undefined) => { return pbnstRes; }; -const writeRgtrAndMdfr = (data: customPbnst) => { - data.rgtr = TEST_UUID; - data.mdfr = TEST_UUID; - data.fcBizs = data.fcBizs.map((item) => { +export const writeRgtrAndMdfr = (data: customPbnst[]) => { + + data.forEach((pbnst) => { + pbnst.rgtr = TEST_UUID; + pbnst.mdfr = TEST_UUID; + + pbnst.fcBizs = pbnst.fcBizs?.map((item) => { item.rgtr = TEST_UUID; item.mdfr = TEST_UUID; - item.fcOrgwts = item.fcOrgwts?.map((item) => { - item.mdfr = TEST_UUID; - item.rgtr = TEST_UUID; - return item; + item.fcOrgwts = item.fcOrgwts?.map((orgwt) => { + orgwt.mdfr = TEST_UUID; + orgwt.rgtr = TEST_UUID; + return orgwt; + }); + + item.fcDemands = item.fcDemands?.map((dmd) => { + dmd.mdfr = TEST_UUID; + dmd.rgtr = TEST_UUID; + return dmd; }); - return item; }); - data.fcOpers = data.fcOpers.map((item) => { - item.rgtr = TEST_UUID; - item.mdfr = TEST_UUID; - return item; + pbnst.fcOpers = pbnst.fcOpers.map((oper) => { + oper.rgtr = TEST_UUID; + oper.mdfr = TEST_UUID; + return oper; }); - data.fcFiles = data.fcFiles.map((item) => { - item.rgtr = TEST_UUID; - item.mdfr = TEST_UUID; - return item; + pbnst.fcFiles = pbnst.fcFiles.map((file) => { + file.rgtr = TEST_UUID; + file.mdfr = TEST_UUID; + return file; }); +}); + return data; @@ -173,6 +184,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { pbnstRegClick: async (pubHasuRes: pubHasuType) => { try { const pbnstRes = await initFetch(pubHasuRes.bscFctCd); + console.log('pbnstRes ' , pbnstRes) return set((state: pubFacState) => ({ ...state, pbnst: { @@ -204,10 +216,9 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { pbnstInsert: async (pbnstRes: customPbnst) => { try { - console.log('writeRgtrAndMdfr(pbnstRes) ', writeRgtrAndMdfr(pbnstRes)) const response = await postFectch( `/reuseFc/pubFc`, - JSON.stringify( writeRgtrAndMdfr(pbnstRes)) + JSON.stringify( writeRgtrAndMdfr([pbnstRes])) ); return set((state: pubFacState) => ({ @@ -221,7 +232,39 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { } catch (error) { console.error("Error updating pbnst:", error); } - }, + },pbnstRuseFcltUpdate : async(ruseRes:customPbnst[])=>{ + try { + + const response = await putFectch( + `/reuseFc/pubFc`, + ruseRes + ); + + } catch (error) { + console.error("Error pbnstRuseFcltUpdate:", error); + } + } ,pbnstRuseBizUpdate : async(ruseRes:fcBizType[])=>{ + try { + + console.log('ruseRes ', ruseRes) + const response = await putFectch( + `/reuseFc/pubFcBiz`, + ruseRes + ); + const pbnstRes = await hasuListFetch("11", "11"); + + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstRes: pbnstRes, + isInit: false, + }, + })); + } catch (error) { + console.error("Error pbnstRuseFcltUpdate:", error); + } + } , getBizCycle: async (pbnstObj: customPbnst) => { const pbnstBizCycle = pbnstObj.fcBizs.map((item) => { return { @@ -303,23 +346,15 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { if (bizIdx >= 0) { regData.fcBizs[bizIdx] = data; + const pbnstData = state.pbnst.pbnstMngRegData + state.pbnst.pbnstMngRegData= [...pbnstData] + } } } return state; }); }, - getDmdCompany: async (txt: string) => { - const company = await getFetch(fetchType.WR, `/reuseFc/request/${txt}/3`); - - return set((state: pubFacState) => ({ - ...state, - pbnst: { - ...state.pbnst, - isInit: false, - }, - })); - }, getpbnstInsertIndex: async (data: pbnstInsertIndex) => { return set((state: pubFacState) => { state.pbnst.pbnsUpdateIndex = data; @@ -339,24 +374,6 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { } catch (e) { console.log(e); } - },searchDmdColist:async (txt:string)=>{ - - try{ - console.log(' txt ', txt) - const coList = await getFetch(fetchType.WR, `reuseFc/request/${txt}/{3}`); - return set((state: pubFacState) => ({ - ...state, - pbnst: { - ...state.pbnst, - dmdCoList: coList, - isInit: false, - }, - })); - }catch(e){ - console.log(e) - - } - - } + }, }; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx index f3f695725..01aac072a 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -44,6 +44,25 @@ export type ruseFcltType = { splyExtraCpct: number; }; +export type PbnstDemandComType = { + 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|'' + +} + + + export type fcBizType = z.infer; export type fcOrgwtType = z.infer; export type ppInstlType = z.infer; @@ -53,7 +72,7 @@ export type fcFileType = z.infer; const pplInstlSchema = z.object({ - ruseFcltBizFk: z.string(), + ruseFcltBizFk: z.number(), choose:z.boolean().default(false), pdmt: z.string().default(''), ppMqlt: z.string().default(""), @@ -72,7 +91,7 @@ const fcFileSchema = z.object({ const fcOperSchema = z.object({ pbnstRuseFcltOperInfoPk: z.number().nullish(), - pbnstRuseFcltFk: z.number().nullish(), + pbnstRuseFcltFk: z.string().nullish(), operMthd: z.string(), rprtvBzenty: z.string(), bgngDt: z.date().default(new Date()), @@ -121,13 +140,18 @@ export type pubHasuType = z.infer; const dmdColist = z.object({ + dmdInfoFk:z.string().default(''), dmdCoNm: z.string().default(''), daddr: z.string(), useUsgCd: z.string().default(""), splyBgngDay: z.date().default(new Date()), splyEndDay: z.date().default(new Date()), - splyCpct: z.string().default(""), - splyFmYn: z.number().default(0), + splyCpct: z.number().default(0), + splyFmYn: z.string().default('Y'), + rgtr: z.string().nullish(), + mdfr: z.string().nullish(), + pbnstRuseFcltBizPk: z.number(), + pbnstRuseFcltFk: z.string(), //telno:z.string().default('') }); diff --git a/apps/wr/components/lib/api/index.ts b/apps/wr/components/lib/api/index.ts index e853428f5..ad0ab4729 100644 --- a/apps/wr/components/lib/api/index.ts +++ b/apps/wr/components/lib/api/index.ts @@ -22,7 +22,7 @@ export const getFetch = async (type: string, url: string) => { }); if (!response.ok) { - throw new Error(`오류 발생: ${response.status}`); + console.log(`오류 발생: ${response.status}`); } const contentType = response.headers.get("content-type") ?? ""; diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index 608b2ccab..890d8e4df 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -13,6 +13,7 @@ import { fcBizSchema, type customPbnst, type fcBizType, + type PbnstDemandComType, type ppInstlType, } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; @@ -31,16 +32,22 @@ import { type SelectDataType, } from "@common/business_components/ui"; import { Form, FormField } from "@common/business_components/ui/form"; -import { Button, DatePicker, Input, Label } from "@common/components"; +import { + Button, + DataTable, + DatePicker, + Input, + Label, +} from "@common/components"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useRef, useState } from "react"; import { useFieldArray, useForm } from "react-hook-form"; import MngRegFormDetail from "../mngReg/mngRegFormDetail/mngRegFormDetail"; -import { - handleCommaNumberChange, - formatCommaNumber, - safeDate, -} from "@/components/lib/func/commom"; +import { safeDate } from "@/components/lib/func/commom"; +import SearchInputText from "@/components/ui/searchInput/searchInput"; +import SdmInsertForm, { + type DemandCompType, +} from "@common/business_components/ui/sdmInsertForm"; export function PbnstBuseForm({ formType, @@ -53,11 +60,13 @@ export function PbnstBuseForm({ const { code, pbnst, + demandCompList, pbnstBizData, getpbnstInsertIndex, appendRuseFclt, getBizCycle, - searchDmdColist, + getDemandCompList, + regDemandCompany, } = useWrStore((state) => state); const { pbnstBizCycle, pbnstRes } = pbnst; const [bizCycle, setBizCycle] = useState("1"); @@ -66,7 +75,10 @@ export function PbnstBuseForm({ const [ppiYn, setPpiYn] = useState("Y"); const [isFcOrgwts, setIsFcOrgwts] = useState(false); const [sumCpct, setSumCpct] = useState(0); - const [searchDmdCo, setSeachDmdCo] = useState(); + const [isDmdModal, setIsDmdModal] = useState<{ + isMoal: boolean; + data: DemandCompType; + }>({ isMoal: false, data: {} as DemandCompType }); const form = useForm({ resolver: zodResolver(fcBizSchema), @@ -143,6 +155,9 @@ export function PbnstBuseForm({ // 등록할때 신규 값을 계속 가지고 있으려고 useEffect(() => { + const formData = form.watch(); + + formData.pplInstls = ref.current ? [...ref.current] : []; // ← form 내부 상태 기준으로 복사 if (formType === "reg") { if ( firstBizData && @@ -156,15 +171,16 @@ export function PbnstBuseForm({ ); form.setValue("cycl", firstBizData[0].cycl); } - const formData = form.watch(); - formData.pplInstls = ref.current ? [...ref.current] : []; // ← form 내부 상태 기준으로 복사 pbnstBizData(formData as fcBizType); - } - }, [firstBizData, form.formState, formType]); + }else if(formType === "update"){ + + console.log('formdata ', formData) + pbnstBizData(formData as fcBizType); + + + } + }, [firstBizData, form.formState, formType, pbnst.pbnstRes]); - useEffect(() => { - console.log("searchDmdCo ", searchDmdCo); - }, [searchDmdCo]); useEffect(() => { if (ppiYn === "N") { @@ -178,7 +194,7 @@ export function PbnstBuseForm({ const plusHist = () => { const regData = { - ruseFcltBizFk: ref.current?.[0]?.ruseFcltBizFk ?? "", + ruseFcltBizFk: ref.current?.[0]?.ruseFcltBizFk ?? form.getValues('pbnstRuseFcltBizPk'), choose: false, ppMqlt: "", pdmt: "", @@ -195,17 +211,17 @@ export function PbnstBuseForm({ }; const plusArray = () => { - - console - .log('qweqwe ') appendDemand({ dmdCoNm: "", daddr: "", useUsgCd: "", splyBgngDay: new Date(), splyEndDay: new Date(), - splyCpct: "", - splyFmYn: 0, + splyCpct: 0, + splyFmYn: "Y", + pbnstRuseFcltBizPk: form.getValues('pbnstRuseFcltBizPk'), + pbnstRuseFcltFk: form.getValues('pbnstRuseFcltFk')??'', + dmdInfoFk: '', //telno: "", }); }; @@ -403,12 +419,27 @@ export function PbnstBuseForm({ moClass="grid grid-cols-12 gap-4" sonClass="col-span-4" /> -
-

{'하수처리수 재이용수 수요처'}

+
+

{"하수처리수 재이용수 수요처"}

+
{fcDemands.map((field, index) => ( -
-
+
+
( - { - searchDmdColist(val); + { + f.onChange(val); + + if (val) getDemandCompList(val, 3); }} - textValue={f.value} - setText={(val) => { - setSeachDmdCo(val); + setRtnData={(valList: PbnstDemandComType[]) => { + console.log('valList[0] ', index) + if ( + valList.length > 0 && + valList['0'] + ) { + form.setValue( + `fcDemands.${index}.pbnstRuseFcltBizPk`, + form.getValues('pbnstRuseFcltBizPk') + ); + form.setValue( + `fcDemands.${index}.pbnstRuseFcltFk`, + form.getValues('pbnstRuseFcltFk')??'' + + ); + form.setValue( + `fcDemands.${index}.dmdCoNm`, + valList['0'].dmdCoNm + ); + form.setValue( + `fcDemands.${index}.daddr`, + valList['0'].daddr + ); + form.setValue( + `fcDemands.${index}.useUsgCd`, + valList['0'].useUsgCd + ); + form.setValue( + `fcDemands.${index}.splyCpct`, + valList['0'].splyCpct + ); + form.setValue( + `fcDemands.${index}.splyFmYn`, + valList['0'].splyFmYn ?? "Y" + ); + form.setValue( + `fcDemands.${index}.dmdInfoFk`, + valList['0'].dmdInfoPk?.toString() ?? "" + ); + } + }} - selectValue={searchDmdCo} - > + > )} />
-
+
f.onChange(e.target.value)} value={f.value} className={selectClass} @@ -457,24 +530,25 @@ export function PbnstBuseForm({ )} />
-
+
( - f.onChange(e.target.value)} - value={f.value} - className={selectClass} + )} />
-
+
-
+
-
+
-
+
} > + + { + setIsDmdModal({ isMoal: false, data: {} as DemandCompType }); + }} + body={ + { + getDemandCompList(searchText, pageCnt); + }} + rtnInputData={(data) => { + console.log(" rtn data ", data); + regDemandCompany(data as DemandCompType); + setIsDmdModal({ isMoal: false, data: data as DemandCompType }); + }} + bodyClassNm={"w-full mx-auto bg-white rounded-lg shadow-md p-6"} + > + } + >
); diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx index ed78452d0..3f0776a4e 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx @@ -91,9 +91,13 @@ export function PbnstMngForm({ getpbnstInsertIndex({ ...pbnst.pbnsUpdateIndex, mng: val }); } }; + + const plusHist = () => { + const currentTableDataFromRef = ref.current ? [...ref.current] : []; const regData = { choose: false, + pbnstRuseFcltFk:form.getValues('pbnstRuseFcltPk')??"", rgtr: "", mdfr: "", operMthd: "", @@ -101,7 +105,6 @@ export function PbnstMngForm({ bgngDt: new Date(), endDt: new Date(), }; - const currentTableDataFromRef = ref.current ? [...ref.current] : []; const newDataForRHF = [...currentTableDataFromRef, regData]; form.setValue("fcOpers", newDataForRHF, { shouldValidate: true, // 필요하다면 유효성 검사 실행 @@ -246,27 +249,37 @@ export function PbnstMngForm({ } } }, [pbnst.pbnsUpdateIndex, pbnst.pbnstRes, formType, getBizCycle]); + + //신규 등록시 입력할때 데이터 넣기 useEffect(() => { + const formData = form.watch(); + formData.fcOpers = ref.current ? [...ref.current] : []; + formData.fcFiles = regFileList ? [...regFileList] : []; if (formType === "reg") { - const formData = form.watch(); - formData.fcOpers = ref.current ? [...ref.current] : []; - formData.fcFiles = regFileList ? [...regFileList] : []; pbnstMngData([formData] as customPbnst[]); + } else if (formType === "update"){ + const updateRes = pbnst.pbnstRes?.map((item)=>{ + if(item.pbnstRuseFcltPk === item.pbnstRuseFcltPk){ + item=formData as customPbnst + } + return item + }) + pbnstMngData(updateRes as customPbnst[]); } - }, [form.formState, regFileList, formType]); + }, [form.formState, regFileList, formType, pbnst.pbnstRes]); useEffect(() => { pbnstMngData(pbnst.pbnstMngRegData); }, [pbnst.pbnstMngRegData]); useEffect(() => { - if ( pbnst.pbnstRes && pbnst.pbnstRes?.length> 0) return; + + if ( pbnst.pbnstRes && pbnst.pbnstRes?.length<= 0) return; const fcltNmList = pbnst.pbnstRes?.map((item, index) => ({ text: item.ruseUtztnFcltNm, val: String(index), })); - if(fcltNmList) setFcltNm(fcltNmList); diff --git a/apps/wr/components/ui/searchInput/searchInput.tsx b/apps/wr/components/ui/searchInput/searchInput.tsx new file mode 100644 index 000000000..413c3593a --- /dev/null +++ b/apps/wr/components/ui/searchInput/searchInput.tsx @@ -0,0 +1,92 @@ + +import { useWrStore } from '@/app/store'; +import type { PbnstDemandComType } from '@/app/store/ruseFcltMng/fcltReg/pbnstReg/type'; +import type { DemandCompType } from '@common/business_components/ui'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; + +type thisProps = { + label: string | undefined; + initValue: string | undefined; + setValue : (dmdCoNm?: string) => void; + setRtnData : (rtnData: PbnstDemandComType[]) => void; +} + + +export default function SearchInputText({label=undefined, initValue=undefined, setValue=()=>{}, setRtnData=()=>{}} :thisProps) { + + const { demandCompList } = useWrStore((state) => state); + + + const [isOpen, setIsOpen] = useState(false); + const [textValue, setTextValue] = useState(initValue || ''); + const wrapperRef = useRef(null); + const handleInputChange = useCallback((e: React.ChangeEvent) => { + const value = e.target.value; + setTextValue(value); + setValue(value); + if (value) { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, [ setValue]); + + const handleSuggestionClick = useCallback((dmdCoNm: string) => { + setTextValue(dmdCoNm); + setValue(dmdCoNm); + setIsOpen(false); + setRtnData(demandCompList); + }, [demandCompList, setRtnData, setValue]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ setIsOpen(true)} */ + className="w-full h-[3.2rem] !text-xl pl-3 text-gray-600 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder={`${label}을(를) 입력하세요`} + /> + {isOpen && ( +
+
+ {demandCompList && demandCompList.length > 0 ? demandCompList.map((item : DemandCompType, index : number) => ( +
handleSuggestionClick(item.dmdCoNm)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleSuggestionClick(item.dmdCoNm); + } + }} + > + {item.dmdInfoPk.substring(0,10)} {item.dmdCoNm} +
+ )) : ( +
+ 검색 결과가 없습니다. +
+ )} +
+
+ )} +
+ ) +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/form_ui/index.tsx b/packages/common/business_components/src/ui/form_ui/index.tsx index 6bc96c8a2..96214b6cf 100644 --- a/packages/common/business_components/src/ui/form_ui/index.tsx +++ b/packages/common/business_components/src/ui/form_ui/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { FormControl, FormItem, FormLabel, FormMessage } from "../form"; import { Button, Checkbox } from "@common/components/ui"; @@ -17,7 +17,11 @@ interface FormElementProps extends React.HTMLAttributes { const FormUi = React.forwardRef( ({ children, label, onCheckboxChange, isDisabled }, ref) => { - const [isChecked, setIsChecked] = useState(false); + const [isChecked, setIsChecked] = useState(isDisabled); + + useEffect(() => { + setIsChecked(isDisabled); + }, [isDisabled]); const handleCheckboxChange = useCallback((checked: boolean) => { setIsChecked(checked); @@ -50,7 +54,7 @@ const FormUi = React.forwardRef( {React.isValidElement(children) && isDisabled ? React.cloneElement(children, { - disabled: true, + disabled: isDisabled, ...(children.props as FormElementProps) }) : children} diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index 90bf029c0..a1fa95f39 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -19,4 +19,4 @@ export * from "./tab"; export * from "./table"; export * from "./table_div"; export * from "./title"; - +export * from "./sdmInsertForm"; \ No newline at end of file diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx new file mode 100644 index 000000000..d59108c5b --- /dev/null +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -0,0 +1,287 @@ +import { FormField } from "../form"; +import { SelectBox } from "../select"; +import { Input } from "@common/components"; +import { FormUi } from "../form_ui"; +import { useCallback, useState } from "react"; +import type { ControllerRenderProps, UseFormReturn } from "react-hook-form"; +import { SdmSearchForm } from "./sdmNameBox"; +import type { DemandCompType } from "../../../../../../apps/dm/app/store/sdmMng"; + +type SelectDataType = { + text: string; + val: string; +}; + +interface FormSchema { + ctpvCd: string; + sggCd: string; + dmdCoNm: string; + dmdInfoPk: string; + daddr: string; + telno: string; + bznmTotNo?: string; + rgtr?: string; + dmdAplyPswd?: string; +} + +interface SdmRegFormProps { + form: UseFormReturn; + onCheckboxChange?: (checked: boolean) => void; + selectSidoData: SelectDataType[]; + selectSigunData: SelectDataType[]; + dataList: DemandCompType[]; + rtnValue: (searchText: string, pageCnt: number) => void; + setIsDuplicate: (value: boolean) => void; +} + +type FieldComponent = (field: ControllerRenderProps) => JSX.Element; + +export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigunData, dataList, rtnValue, setIsDuplicate }: SdmRegFormProps) { + const [sidoValue, setSidoValue] = useState(''); + const [sigunValue, setSigunValue] = useState(''); + const [filteredSigunData, setFilteredSigunData] = useState([{ text: "전체", val: '0' }]); + const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(true); + const [orgBznmNo, setOrgBznmNo] = useState(''); + + const initializeSigunData = useCallback((sidoCd: string) => { + if (sidoCd === '0') { + setSigunValue('0'); + setFilteredSigunData([{ text: "전체", val: '0' }]); + } else { + const newFilteredData = [ + { text: "전체", val: '0' }, + ...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd) + ]; + setSigunValue('0'); + setFilteredSigunData(newFilteredData); + } + }, [selectSigunData]); + + const onSelectValue = useCallback(( + val: string, + type: string, + field: ControllerRenderProps | undefined + ) => { + switch (type) { + case "ctpvCd": + initializeSigunData(val); + setSidoValue(val); + field?.onChange(val); + form.setValue('dmdInfoPk', ''); + form.setValue('bznmTotNo', ''); + break; + case "sggCd": + setSigunValue(val); + field?.onChange(val); + form.setValue('dmdInfoPk', ''); + form.setValue('bznmTotNo', ''); + break; + } + }, [initializeSigunData, form]); + + const handleCheckboxChange = useCallback((checked: boolean) => { + setIsBznmTotNoDisabled(checked); + onCheckboxChange?.(checked); + }, [onCheckboxChange]); + + const setRtnValue = useCallback((searchText: string, pageCnt: number) => { + rtnValue(searchText, pageCnt); + }, [rtnValue]); + + const setRtnData = useCallback((item: DemandCompType) => { + const rtnCompData = item; + form.setValue('dmdCoNm', rtnCompData?.dmdCoNm || ''); + form.setValue('dmdInfoPk', rtnCompData?.dmdInfoPk.substring(0,10) || ''); + form.setValue('bznmTotNo', rtnCompData?.dmdInfoPk.substring(10,14)==='0000' ? '' : rtnCompData?.dmdInfoPk.substring(10,14)); + + // 수요처 불러온 후 사업자번호를 바꿀경우를 대비함 + setOrgBznmNo(rtnCompData?.dmdInfoPk); + const sidoCd = rtnCompData?.ctpvCd.trim() || ''; + setSidoValue(sidoCd); + form.setValue('ctpvCd', sidoCd); + initializeSigunData(sidoCd); + + setTimeout(() => { + const sigunCd = rtnCompData?.sggCd.trim() || ''; + setSigunValue(sigunCd); + form.setValue('sggCd', sigunCd); + }, 100); + + if(rtnCompData?.dmdInfoPk.substring(10,14) === '0000') { + handleCheckboxChange(true); + } else { + handleCheckboxChange(false); + } + + form.setValue('daddr', rtnCompData?.daddr || ''); + form.setValue('telno', rtnCompData?.telno || ''); + + // 선택을 했을 때 중복 상태를 true로 설정 + setIsDuplicate(true); + }, [form, handleCheckboxChange, initializeSigunData, setIsDuplicate]); + + const setRtnText = useCallback((text: string) => { + form.setValue('dmdCoNm', text); + }, [form]); + + const bznNoChange = useCallback((e: React.ChangeEvent, noType: string) => { + const value = e.target.value; + console.log('form value : '+form.getValues('dmdInfoPk')); + console.log('org value : '+orgBznmNo); + + if(noType === 'dmdInfoPk') { + if(orgBznmNo.substring(0,10) === value) { + setIsDuplicate(true); + } else { + setIsDuplicate(false); + } + form.setValue('dmdInfoPk', value); + } else { + if(orgBznmNo.substring(10,14) === value) { + setIsDuplicate(true); + } else { + setIsDuplicate(false); + } + form.setValue('bznmTotNo', value); + } + + }, [form, orgBznmNo, setIsDuplicate]); + + const formFields: Array<{ + name: keyof FormSchema; + label: string; + component: FieldComponent; + }> = [ + { + name: "ctpvCd", + label: "시도", + component: (field) => ( + onSelectValue(val.toString(), 'ctpvCd', field)} + className="" + value={sidoValue} + /> + ) + }, + { + name: "sggCd", + label: "시군구", + component: (field) => ( + onSelectValue(val.toString(), 'sggCd', field)} + className="" + value={sigunValue} + /> + ) + }, + { + name: "dmdCoNm", + label: "회사명", + component: (field) => ( + + ) + }, + { + name: "dmdInfoPk", + label: "사업자등록번호", + component: (field) => ( + bznNoChange(e, 'dmdInfoPk')} + /> + ) + }, + { + name: "bznmTotNo", + label: "종사업자등록번호", + component: (field) => ( + + bznNoChange(e, 'bznmTotNo')} + /> + + ) + }, + { + name: "daddr", + label: "사업자주소", + component: (field) => ( + + ) + }, + { + name: "telno", + label: "전화번호", + component: (field) => ( + + ) + }, + { + name: "dmdAplyPswd", + label: "패스워드", + component: (field) => ( + + ) + } + ]; + + return ( + <> + {formFields.map(({ name, label, component }) => ( + ( + name === 'bznmTotNo' ? component(field) : ( + + {component(field)} + + ) + )} + /> + ))} + + ); +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx new file mode 100644 index 000000000..4b4a94648 --- /dev/null +++ b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx @@ -0,0 +1,221 @@ +"use client"; + +/* import { useDmStore } from "@/app/store"; */ +import { SdmRegForm } from "./form"; +import { Button } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +/* import { useRouter } from "next/navigation"; */ +import { useCallback, useEffect, useState } from "react"; +import { FormProvider, useForm, type SubmitHandler } from "react-hook-form"; +import { toast, Toaster } from "sonner"; +/* import { Toaster, toast } from "react-hot-toast"; */ +import * as z from "zod"; +import type { SelectDataType } from "../search_input_select"; + + +export type DemandCompType = { + dmdInfoPk : string | ''; + bzmnNo : string | ''; + dmdCoNm : string | ''; + daddr : string | ''; + telno : string | ''; + rgtr : string | ''; + useUsgCd : string | ''; + ctpvCd : string | ''; + sggCd : string | ''; + dmdAplyPswd: string | ''; +} + + +type thisProps = { + selectSidoData: SelectDataType[]; + selectSigunData: SelectDataType[]; + dataList: DemandCompType[]; + rtnValue: (searchText: string, pageCnt: number) => void; + rtnInputData: (data: DemandCompType) => void; + bodyClassNm?: string; +} + +const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean) => z.object({ + ctpvCd: z.string().min(1, "시/도를 선택해주세요"), + sggCd: z.string().min(1, "시/군/구를 선택해주세요"), + dmdCoNm: z.string() + .min(1, "회사명을 입력해주세요") + .max(20, "회사명은 20자 이내로 입력해주세요"), + dmdInfoPk: z.string() + .min(1, "사업자등록번호를 입력해주세요") + .regex(/^\d{10}$/, "사업자등록번호는 10자리 숫자만 입력 가능합니다") + .refine(() => isBznmTotNoDisabled ? !isDuplicate : true, "이미 등록된 사업자등록번호입니다"), + bznmTotNo: isBznmTotNoDisabled + ? z.string().optional() + : z.string() + .length(4, "종사업자등록번호는 4자리 숫자만 입력 가능합니다") + .refine(() => !isDuplicate, "이미 등록된 사업자등록번호입니다"), + daddr: z.string() + .min(1, "주소를 입력해주세요") + .min(6, "주소는 6자 이상 입력해주세요") + .max(40, "주소는 40자 이내로 입력해주세요"), + telno: z.string() + .min(8, "전화번호는 8자 이상 입력해주세요") + .max(12, "전화번호는 12자 이내로 입력해주세요") + .regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"), + rgtr: z.string().optional(), + dmdAplyPswd : z.string() + .min(1, "패스워드를 입력해주세요") + .min(8, "패스워드는 8자 이상 입력해주세요") + .max(16, "패스워드는 16자 이내로 입력해주세요"), +}); + +type FormSchema = ReturnType; +export type FormData = z.infer; + +const defaultValues: FormData = { + ctpvCd: '', + sggCd: '', + dmdCoNm: '', + dmdInfoPk: '', + bznmTotNo: '', + daddr: '', + telno: '', + rgtr: "3fa85f64-5717-4562-b3fc-2c963f66afa6", + dmdAplyPswd : '', +}; + +/** + * 수요처 등록 폼 + * @param selectSidoData 시/도 데이터 + * @param selectSigunData 시/군/구 데이터 + * @param dataList 수요처 목록 + * @param rtnValue 검색 결과(텍스트) 콜백 + * @param rtnInputData 수요처 리스트 콜백 + * @returns + */ + +/** + * 페이지에서 작업 + const {Common, initializeCommonData, getDemandCompList, insertSdmData, Sdm} = useDmStore((state) => state); + const { selectSidoData, selectSigunData } = Common; + const { demandCompList } = Sdm; + + // 공통데이터 (시도, 시군구 데이터 호출) + useEffect(() => { + initializeCommonData(); + }, [initializeCommonData]); + + // 컴포넌트에서 텍스트 검색 호출 + const callback = useCallback((searchText: string, pageCnt: number) => { + searchText = searchText.trim(); + if(searchText.length > 0) { + getDemandCompList(searchText, pageCnt); + } + }, [getDemandCompList]); + + // 컴포넌트에서 수요처 등록 호출 + const handleInsertSdmData = useCallback((data: DemandCompType) => { + insertSdmData(data); + + }, [insertSdmData]); + */ + +export default function SdmInsertForm({selectSidoData, selectSigunData, dataList, rtnValue, rtnInputData,bodyClassNm}: thisProps) { + /* const { insertSdmData } = useDmStore((state) => state); + const router = useRouter(); */ + + const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(true); + const [isDuplicate, setIsDuplicate] = useState(false); + const formSchema = createFormSchema(isBznmTotNoDisabled, isDuplicate); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues, + mode: "onChange", + criteriaMode: "all", + shouldFocusError: false, + shouldUnregister: false + }); + + // 체크박스 상태가 변경될 때마다 bznmTotNo 필드의 에러를 초기화 + useEffect(() => { + if (isBznmTotNoDisabled) { + form.clearErrors('bznmTotNo'); + form.setValue('bznmTotNo', ''); + } + }, [isBznmTotNoDisabled, form]); + + const setRtnValue = useCallback((searchText: string, pageCnt: number) => { + rtnValue(searchText, pageCnt); + }, [rtnValue]); + + const onSubmit: SubmitHandler> = async (data) => { + try { + // form 에러 체크 + const formErrors = form.formState.errors; + if (Object.keys(formErrors).length > 0) { + const errorMessage = Object.values(formErrors)[0]?.message || '입력값을 확인해주세요.'; + toast.error(errorMessage); + return; + } + + const dmdInfoPk = data.dmdInfoPk + (data.bznmTotNo || '0000'); + const formData = { + ...data, + dmdInfoPk, + }; + + console.log(formData); + /* await insertSdmData(formData); */ + rtnInputData(formData as DemandCompType); + + const queryParams = new URLSearchParams({ + dmdCoNm: data.dmdCoNm, + ctpvCd: data.ctpvCd, + sggCd: data.sggCd, + dmdInfoPk, + bznmTotNo: data.bznmTotNo || '', + daddr: data.daddr, + telno: data.telno, + }); + + //router.push(`/pages/sdmMng/sdmInfoInsert?${queryParams.toString()}`); + toast.success('수요처가 성공적으로 등록되었습니다.'); + } catch (error) { + console.error('Error submitting form:', error); + toast.error('수요처 등록에 실패했습니다.'); + } + }; + + return ( + <> +
+

+ 수요처등록 +

+
+ +
+ +
+ +
+ +
+ +
+
+ + + ); +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/sdmInsertForm/sdmNameBox.tsx b/packages/common/business_components/src/ui/sdmInsertForm/sdmNameBox.tsx new file mode 100644 index 000000000..83571b6ff --- /dev/null +++ b/packages/common/business_components/src/ui/sdmInsertForm/sdmNameBox.tsx @@ -0,0 +1,105 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import type { DemandCompType } from "../../../../../../apps/dm/app/store/sdmMng"; + +export type thisProps = { + label: string | undefined; + initValue: string | undefined; + dataList : DemandCompType[]; + rtnValue : (searchText: string, pageCnt: number) => void; + rtnData : (item: DemandCompType) => void; + rtnText : (text: string) => void; +} + +/** + * 수요처 검색 컴포넌트 + * @param label 라벨 + * @param initValue 초기값 + * @param rtnValue 검색 결과 반환 함수 + * @param dataList 검색할 때 넘기는 데이터 리스트 + * @param rtnData 검색 결과 반환 함수 + */ +export const SdmSearchForm = ({label=undefined, initValue=undefined, rtnValue=()=>{}, dataList=[], rtnData=()=>{}, rtnText=()=>{} } :thisProps) => { + + const [isOpen, setIsOpen] = useState(false); + const [textValue, setTextValue] = useState(initValue || ''); + const wrapperRef = useRef(null); + const handleInputChange = useCallback((e: React.ChangeEvent) => { + const value = e.target.value; + setTextValue(value); + rtnValue(value, 10); + rtnText(value); + if (value) { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, [rtnValue, rtnText]); + + const handleSuggestionClick = useCallback((item: DemandCompType) => { + setTextValue(item.dmdCoNm); + rtnData(item); + setIsOpen(false); + }, [rtnData]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ setIsOpen(true)} */ + className="w-full h-[3.2rem] !text-xl pl-3 text-gray-600 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder={`${label}을(를) 입력하세요`} + /> + {isOpen && ( +
+
+ {dataList && dataList.length > 0 ? dataList.map((item : DemandCompType, index : number) => ( +
handleSuggestionClick(item)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleSuggestionClick(item); + } + }} + > + {item.dmdInfoPk.substring(10,14) !== '0000' ? ( + <> + 사업자번호 : {item.dmdInfoPk.substring(0,10)}  + 종사업자번호 : {item.dmdInfoPk.substring(10,14)}  + + ) : ( + <> + 사업자번호 : {item.dmdInfoPk.substring(0,10)}  + + )} + {item.dmdCoNm} +
+ )) : ( +
+ 검색 결과가 없습니다. +
+ )} +
+
+ )} +
+ ) +} \ No newline at end of file