Merge commit 'a38a1315f2f4284d2618ea7f83d70412a30b233c' into iipbs/wr/form_fuction

This commit is contained in:
jyha00
2025-06-05 10:20:30 +09:00
16 changed files with 1099 additions and 148 deletions
+32 -5
View File
@@ -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<customCoSttnBdst[]>();
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<ReturnType>({} as ReturnType);
const codeData = getCityOrSgg(ref);
@@ -64,7 +69,7 @@ export default function coSttn() {
<Button
className={btnClass}
onClick={() => {
/* 등록 로직 */
setIsDmdModal({ ...isDmdModal, isMoal:true });
}}
>
등록
@@ -112,7 +117,29 @@ export default function coSttn() {
</ListDiv>
</div>
</div>
<UiModal
bodyClass={"w-[40vw] max-w-none h-[90vh]"}
open={isDmdModal.isMoal}
onOpenChange={() => {
setIsDmdModal({ isMoal: false, data: {} as DemandCompType });
}}
body={
<SdmInsertForm
selectSidoData={code.cityCd}
selectSigunData={code.sggCd}
dataList={demandCompList}
rtnValue={(searchText: string, pageCnt: number) => {
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"}
></SdmInsertForm>
}
></UiModal>
</ContentsDiv>
</>
@@ -28,6 +28,7 @@ import { z } from "zod";
import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal";
import { Toaster } from "react-hot-toast";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg";
export default function PublicReuseFaciltyInsert() {
const {
@@ -38,6 +39,8 @@ export default function PublicReuseFaciltyInsert() {
pbnstRegClick,
getSwtrmtFcltNm,
pbnstMngData,
pbnstRuseFcltUpdate,
pbnstRuseBizUpdate,
} = useWrStore((state) => state);
const { pubFacHeadList, pbnstHasuRes, pbnstRes } = pbnst;
const [currentTab, setCurrentTab] = useState<string>("mng");
@@ -51,12 +54,35 @@ export default function PublicReuseFaciltyInsert() {
const [bdstTable, setbdstTable] = useState<pubHasuType[]>();
const [selectBdst, setSelectBdst] = useState<customPbnst | null>(null);
useEffect(() => {
if (!pbnst.isInit) {
pubHasuInit("1", "11");
}
}, [pbnst.isInit, pubHasuInit]);
const saveRuseData = async ()=>{
const data = writeRgtrAndMdfr(pbnst.pbnstMngRegData)
let bizs =[] as fcBizType[]
data.forEach((fclt) =>{
bizs = [
...bizs,
...fclt.fcBizs
]
})
await pbnstRuseFcltUpdate(data)
console.log('bizs ', bizs)
await pbnstRuseBizUpdate(bizs)
console.log(' data ', data)
}
useEffect(() => {
if(code.sggCd.length){
if(searchFclts){
@@ -156,7 +182,7 @@ export default function PublicReuseFaciltyInsert() {
<TabsTrigger className="!text-xl" value="buse">
차수별정보
</TabsTrigger>
<Button size={"sm"} className="!justify-end">
<Button size={"sm"} onClick={ saveRuseData}className="!justify-end">
저장
</Button>
</TabsList>
+14
View File
@@ -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,
},
};
+48 -25
View File
@@ -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)
}
}
}
};
};
@@ -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)
}
}
},
};
};
@@ -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<typeof fcBizSchema>;
export type fcOrgwtType = z.infer<typeof fcOrgwtSchema>;
export type ppInstlType = z.infer<typeof pplInstlSchema>;
@@ -53,7 +72,7 @@ export type fcFileType = z.infer<typeof fcFileSchema>;
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<typeof pubHasuZod>;
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('')
});
+1 -1
View File
@@ -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") ?? "";
@@ -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<boolean>(false);
const [sumCpct, setSumCpct] = useState<number>(0);
const [searchDmdCo, setSeachDmdCo] = useState<String>();
const [isDmdModal, setIsDmdModal] = useState<{
isMoal: boolean;
data: DemandCompType;
}>({ isMoal: false, data: {} as DemandCompType });
const form = useForm<fcBizType>({
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"
/>
<div>
<h1 className={formH1Class}>{'하수처리수 재이용수 수요처'}</h1>
<div key={"hasu"}>
<h1 className={formH1Class}>{"하수처리수 재이용수 수요처"}</h1>
<div className="text-right mb-3">
<Button
type="button"
onClick={plusArray}
onClick={() => {
plusArray();
}}
size={commonButonSize}
className="mt-6 mr-3"
>
수요항목 추가
</Button>
<Button
type="button"
onClick={() => {
setIsDmdModal({
isMoal: true,
data: {} as DemandCompType,
});
}}
size={commonButonSize}
className="mt-6"
>
@@ -416,31 +447,72 @@ export function PbnstBuseForm({
</Button>
</div>
{fcDemands.map((field, index) => (
<div className="grid grid-cols-12 gap-4 mt-3">
<div className="col-span-4">
<div
key={index + "loot"}
className="grid grid-cols-12 gap-4 mt-3"
>
<div key={index + "keySon1"} className="col-span-4">
<FormField
control={form.control}
name={`fcDemands.${index}.dmdCoNm`}
render={({ field: f }) => (
<FormUi label="수요처명">
<SearchInputSelect
className={""}
label={""}
selectData={[]}
onSelectValue={(val) => {
searchDmdColist(val);
<SearchInputText
label={undefined}
initValue={f.value}
setValue={(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}
></SearchInputSelect>
></SearchInputText>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<div key={index + "keySon2"} className="col-span-4">
<FormField
control={form.control}
name={`fcDemands.${index}.daddr`}
@@ -449,6 +521,7 @@ export function PbnstBuseForm({
<Input
placeholder="-"
type="text"
readOnly
onChange={(e) => f.onChange(e.target.value)}
value={f.value}
className={selectClass}
@@ -457,24 +530,25 @@ export function PbnstBuseForm({
)}
/>
</div>
<div className="col-span-4">
<div key={index + "keySon3"} className="col-span-4">
<FormField
control={form.control}
name={`fcDemands.${index}.useUsgCd`}
render={({ field: f }) => (
<FormUi label="사용용도">
<Input
placeholder="-"
type="text"
onChange={(e) => f.onChange(e.target.value)}
value={f.value}
className={selectClass}
<SelectBox
label={""}
selectClass={""}
className="w-full"
selectArray={code.dmdUszCd}
onSelectValue={f.onChange}
value={f.value?? code.dmdUszCd[0]?.val}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<div key={index + "keySon4"} className="col-span-3">
<FormField
control={form.control}
name={`fcDemands.${index}.splyBgngDay`}
@@ -492,7 +566,7 @@ export function PbnstBuseForm({
)}
/>
</div>
<div className="col-span-3">
<div key={index + "keySon5"} className="col-span-3">
<FormField
control={form.control}
name={`fcDemands.${index}.splyEndDay`}
@@ -510,7 +584,7 @@ export function PbnstBuseForm({
)}
/>
</div>
<div className="col-span-3">
<div key={index + "keySon6"} className="col-span-3">
<FormField
control={form.control}
name={`fcDemands.${index}.splyCpct`}
@@ -528,7 +602,7 @@ export function PbnstBuseForm({
)}
/>
</div>
<div className="col-span-3">
<div key={index + "keySon7"} className="col-span-3">
<FormField
control={form.control}
name={`fcDemands.${index}.splyFmYn`}
@@ -570,6 +644,30 @@ export function PbnstBuseForm({
></FcOrgwtsRegModal>
}
></UiModal>
<UiModal
bodyClass={"w-[40vw] max-w-none h-[90vh]"}
open={isDmdModal.isMoal}
onOpenChange={() => {
setIsDmdModal({ isMoal: false, data: {} as DemandCompType });
}}
body={
<SdmInsertForm
selectSidoData={code.cityCd}
selectSigunData={code.sggCd}
dataList={demandCompList}
rtnValue={(searchText: string, pageCnt: number) => {
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"}
></SdmInsertForm>
}
></UiModal>
</div>
</>
);
@@ -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);
@@ -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<HTMLDivElement>(null);
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
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 (
<div ref={wrapperRef}>
<input
type="text"
value={textValue}
onChange={handleInputChange}
/* onFocus={() => 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 && (
<div className=" z-1 w-full mt-2 bg-white border border-gray-300 rounded-md shadow-lg h-90 overflow-auto">
<div className="max-h-72">
{demandCompList && demandCompList.length > 0 ? demandCompList.map((item : DemandCompType, index : number) => (
<div
key={index}
className="!text-xl w-full text-left px-5 py-3 hover:bg-gray-100 cursor-pointer overflow-auto"
onClick={() => handleSuggestionClick(item.dmdCoNm)}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleSuggestionClick(item.dmdCoNm);
}
}}
>
{item.dmdInfoPk.substring(0,10)}&nbsp;<span className="float-right">{item.dmdCoNm}</span>
</div>
)) : (
<div className="!text-xl w-full px-5 py-3 hover:bg-gray-100 cursor-pointer overflow-auto">
검색 결과가 없습니다.
</div>
)}
</div>
</div>
)}
</div>
)
}
@@ -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<HTMLElement> {
const FormUi = React.forwardRef<HTMLDivElement, FormUiProps>(
({ 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<HTMLDivElement, FormUiProps>(
<FormControl>
{React.isValidElement(children) && isDisabled
? React.cloneElement(children, {
disabled: true,
disabled: isDisabled,
...(children.props as FormElementProps)
})
: children}
@@ -19,4 +19,4 @@ export * from "./tab";
export * from "./table";
export * from "./table_div";
export * from "./title";
export * from "./sdmInsertForm";
@@ -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<FormSchema>;
onCheckboxChange?: (checked: boolean) => void;
selectSidoData: SelectDataType[];
selectSigunData: SelectDataType[];
dataList: DemandCompType[];
rtnValue: (searchText: string, pageCnt: number) => void;
setIsDuplicate: (value: boolean) => void;
}
type FieldComponent = (field: ControllerRenderProps<FormSchema, keyof FormSchema>) => JSX.Element;
export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigunData, dataList, rtnValue, setIsDuplicate }: SdmRegFormProps) {
const [sidoValue, setSidoValue] = useState<string>('');
const [sigunValue, setSigunValue] = useState<string>('');
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(true);
const [orgBznmNo, setOrgBznmNo] = useState<string>('');
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<FormSchema, keyof FormSchema> | 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<HTMLInputElement>, 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) => (
<SelectBox
selectClass={undefined}
label=""
selectArray={selectSidoData}
onSelectValue={(val) => onSelectValue(val.toString(), 'ctpvCd', field)}
className=""
value={sidoValue}
/>
)
},
{
name: "sggCd",
label: "시군구",
component: (field) => (
<SelectBox
selectClass={undefined}
label=""
selectArray={filteredSigunData}
onSelectValue={(val) => onSelectValue(val.toString(), 'sggCd', field)}
className=""
value={sigunValue}
/>
)
},
{
name: "dmdCoNm",
label: "회사명",
component: (field) => (
<SdmSearchForm
rtnData={setRtnData}
label="수요처명"
initValue={''}
dataList={dataList}
rtnValue={setRtnValue}
rtnText={setRtnText}
{...field}
/>
)
},
{
name: "dmdInfoPk",
label: "사업자등록번호",
component: (field) => (
<Input
placeholder="사업자등록번호를 입력하세요"
type="text"
className="!text-xl text-gray-600"
{...field}
onChange={(e) => bznNoChange(e, 'dmdInfoPk')}
/>
)
},
{
name: "bznmTotNo",
label: "종사업자등록번호",
component: (field) => (
<FormUi
label="종사업자등록번호"
onCheckboxChange={handleCheckboxChange}
isDisabled={isBznmTotNoDisabled}
>
<Input
placeholder="여러사업장이 있을 경우 입력해주세요"
type="text"
className="!mt-5 !text-xl text-gray-600"
disabled={isBznmTotNoDisabled}
{...field}
onChange={(e) => bznNoChange(e, 'bznmTotNo')}
/>
</FormUi>
)
},
{
name: "daddr",
label: "사업자주소",
component: (field) => (
<Input
placeholder="주소를 입력하세요"
type="text"
className="!text-xl text-gray-600"
{...field}
/>
)
},
{
name: "telno",
label: "전화번호",
component: (field) => (
<Input
placeholder="전화번호를 입력하세요"
type="text"
className="!text-xl text-gray-600"
{...field}
/>
)
},
{
name: "dmdAplyPswd",
label: "패스워드",
component: (field) => (
<Input
placeholder="패스워드를 입력하세요"
type="password"
className="!text-xl text-gray-600"
{...field}
/>
)
}
];
return (
<>
{formFields.map(({ name, label, component }) => (
<FormField
key={String(name)}
control={form.control}
name={name}
render={({ field }) => (
name === 'bznmTotNo' ? component(field) : (
<FormUi label={label}>
{component(field)}
</FormUi>
)
)}
/>
))}
</>
);
}
@@ -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<typeof createFormSchema>;
export type FormData = z.infer<FormSchema>;
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<z.infer<FormSchema>>({
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<z.infer<FormSchema>> = 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 (
<>
<div className="text-center mb-8">
<h1 className="text-4xl font-bold text-gray-900 underline">
수요처등록
</h1>
</div>
<div className= {bodyClassNm?bodyClassNm:"!max-w-3xl mx-auto bg-white rounded-lg shadow-md p-6"}>
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<SdmRegForm
form={form}
onCheckboxChange={setIsBznmTotNoDisabled}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
dataList={dataList}
rtnValue={setRtnValue}
setIsDuplicate={setIsDuplicate}
/>
<div className="flex justify-center pt-4">
<Button
type="submit"
className="w-32 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-md transition-colors duration-200"
>
수요처등록
</Button>
</div>
</form>
</FormProvider>
</div>
<Toaster position="top-center" />
</>
);
}
@@ -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<HTMLDivElement>(null);
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
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 (
<div ref={wrapperRef}>
<input
type="text"
value={textValue}
onChange={handleInputChange}
/* onFocus={() => 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 && (
<div className=" z-1 w-full mt-2 bg-white border border-gray-300 rounded-md shadow-lg h-90 overflow-auto">
<div className="max-h-72">
{dataList && dataList.length > 0 ? dataList.map((item : DemandCompType, index : number) => (
<div
key={index}
className="!text-xl w-full text-left px-5 py-3 hover:bg-gray-100 cursor-pointer overflow-auto"
onClick={() => handleSuggestionClick(item)}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleSuggestionClick(item);
}
}}
>
{item.dmdInfoPk.substring(10,14) !== '0000' ? (
<>
<span className="text-gray-500">사업자번호 : </span> {item.dmdInfoPk.substring(0,10)}&nbsp;
<span className="text-gray-500">종사업자번호 : </span> {item.dmdInfoPk.substring(10,14)}&nbsp;
</>
) : (
<>
<span className="text-gray-500">사업자번호 : </span> {item.dmdInfoPk.substring(0,10)}&nbsp;
</>
)}
<span className="float-right">{item.dmdCoNm}</span>
</div>
)) : (
<div className="!text-xl w-full px-5 py-3 hover:bg-gray-100 cursor-pointer overflow-auto">
검색 결과가 없습니다.
</div>
)}
</div>
</div>
)}
</div>
)
}