diff --git a/apps/wr/app/matMng/matDmd/coMatRvw/page.tsx b/apps/wr/app/matMng/matDmd/coMatRvw/page.tsx new file mode 100644 index 000000000..b7bafc693 --- /dev/null +++ b/apps/wr/app/matMng/matDmd/coMatRvw/page.tsx @@ -0,0 +1,409 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { formTableName } from "@/components/lib/class/comClass"; +import { getCity } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ContentsDiv, Form, FormField, FormUi, ScrollArea, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, DatePicker, Input, Label } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { z } from "zod"; + + +export default function coMatRvw() { + const { code, coRvw, } = useWrStore((state) => state); + const { coRvwHeadList, } = coRvw; + const [fromDate, setFromDate] = useState(new Date()); + const [toDate, setToDate] = useState(new Date()); + const [selectSgg, setSelectSgg] = useState() + const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); + + + const schema = z.object({ + fcltCpct: z.number().optional(), + }); + + const form = useForm>({ + resolver: zodResolver(schema), + defaultValues: { + fcltCpct: 0, + } + }); + + const getSelCity = (val: string | number) => { + const cityValue = String(val); // Ensure val is treated as a string + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: '' }); + }; + + const getSgg = (val: string | number) => { + console.log(val); + setObjSearch({ ...objSearch, sgg: String(val) }) + }; + + const { + fields: [], + append, + remove, + } = useFieldArray({ + control: form.control, + name: "", + }); + + useEffect(() => { + if (fromDate > toDate) { + const newFromDate = new Date(toDate); + newFromDate.setDate(toDate.getDate() - 1); + setFromDate(newFromDate); + } + }, [toDate]); + + return ( + <> + +
+
+ + <SearchDiv> + <SearchFormLeft> + <div className="col-span-8 flex flex-row"> + <SelectBox + className="col-span-2 w-1/3 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/3 h-full text-2xl" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + /> + <div className="col-span-5 w-full"> + <div className="flex flex-row items-center"> + <Label className="text-xl text-gray-600">수요신청년도</Label> + <div className="flex flex-row"> + <DatePicker mode={'single'} + selected={fromDate} + onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} /> + <span>~</span> + <DatePicker mode={'single'} + selected={toDate} + onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} /> + </div> + </div> + </div> + <SearchInput + className={"text-2xl"} + label="회사명" + textValue={""} + setText={(e) => { + }} + /> + </div> + </SearchFormLeft> + <SearchFormRight + className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <div className="space-y"> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + onClick={(e) => { }} + > + 조회 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={"수요매칭검토현황"} + tableData={[]} + children={undefined} + headlist={coRvwHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + }} + onPageChange={() => { }} + ></UiTable> + <hr /> + <Form {...form}> + <h3 className="mt-4 ml-2 text-2xl font-medium text-label">표준공사비 산정</h3> + <div className="flex items-center mx-3 mb-3"> + <h1 className="">처리시설 표준공사비 산정</h1> + + <Button + size={"sm"} + onClick={(e) => { }} + > + 계산 + </Button> + <Button + size={"sm"} + onClick={(e) => { }} + > + 저장 + </Button> + + </div> + <div className="grid grid-cols-12 gap-4 ml-4 mr-4"> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="용도"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="요구수량(M3/d)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <Label className="">용도에 따른 처리공정<i className="diveicon di-search" /></Label> + + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value || ""} + className="mt-2" + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="처리시설 용량(요구수량 * 365일)"> + <Input + placeholder="" + type="number" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <Label>처리시설용량에 따른 공사비(백만원)</Label> + <Button + size={"sm"} + onClick={(e) => { }} + className="bg-none" + > + 공사비 산정표 보기 + </Button> + </div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="number" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <Label>시설부대경비 요율(%)</Label> + <Button + size={"sm"} + onClick={(e) => { }} + > + 요율표 보기 + </Button> + </div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="number" + step="0.01" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-3 mt-4"> + <Label>총사업비 (백만원)<i className="diveicon di-search" /></Label> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + className="mt-2" + type="number" + step="0.01" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <Label>공급처 선정</Label> + <Button + size={"sm"} + onClick={(e) => { }} + className="bg-none" + > + 후보지 선택 + </Button> + </div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-4 mt-4"> + <div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="공급관로 표준공사비(백만원)"> + <Input + placeholder="" + type="text" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="mt-4"> + <Label>관로 표준공사비 산정 프로그램</Label> + <Button + size={"sm"} + onClick={(e) => { }} + className="bg-none" + > + 다운로드 + </Button> + </div> + <div className="col-span-4 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <Label className="text-xl">처리시설 + 공급관로 총사업비(백만원) + <FormUi label=""> + <Input + placeholder="" + type="text" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + readOnly={true} + value={field.value ?? 0} + /> + </FormUi> + </Label> + )} + /> + </div> + </div> + </div> + </Form> + </ListDiv> + </div> + </div> + </ContentsDiv > + </> + ) +} \ No newline at end of file diff --git a/apps/wr/app/matMng/matDmd/ruseRvwHstry/page.tsx b/apps/wr/app/matMng/matDmd/ruseRvwHstry/page.tsx new file mode 100644 index 000000000..9b3d2262f --- /dev/null +++ b/apps/wr/app/matMng/matDmd/ruseRvwHstry/page.tsx @@ -0,0 +1,367 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { formTableName } from "@/components/lib/class/comClass"; +import { getCity } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ContentsDiv, Form, FormField, FormUi, ScrollArea, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, DatePicker, Input, Label } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { z } from "zod"; + + +export default function ruseRvwHstry() { + const { code, ruseHstry, } = useWrStore((state) => state); + const { ruseHstryHeadList, } = ruseHstry; + const [fromDate, setFromDate] = useState<Date>(new Date()); + const [toDate, setToDate] = useState<Date>(new Date()); + const schema = z.object({}); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); + + const getSelCity = (val: string | number) => { + const cityValue = String(val); // Ensure val is treated as a string + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: '' }); + }; + + const getSgg = (val: string | number) => { + console.log(val); + setObjSearch({ ...objSearch, sgg: String(val) }) + }; + + const form = useForm<z.infer<typeof schema>>({ + resolver: zodResolver(schema), + defaultValues: { + fcltCpct: 0, + } + }); + + const { + fields: [], + append, + remove, + } = useFieldArray({ + control: form.control, + name: "", + }); + +return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"재이용검토이력"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-8 flex flex-row"> + <SelectBox + className="col-span-2 w-1/3 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={getSelCity} + > + {""} + </SelectBox> + <SelectBox + className="col-span-2 w-1/3 h-full text-2xl" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + > + {""} + </SelectBox> + <div className="col-span-5 w-full"> + <div className="flex flex-row items-center"> + <Label className="text-xl text-gray-600">수요신청년도</Label> + <div className="flex flex-row"> + <DatePicker mode={'single'} + className="w-full" + selected={fromDate} + onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} /> + <span>~</span> + <DatePicker mode={'single'} + selected={toDate} + onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} /> + </div> + </div> + </div> + <SearchInput + className={"text-2xl"} + label="시설명" + textValue={""} + setText={(e) => { + }} + /> + <SearchInput + className={"text-2xl"} + label="회사명" + textValue={""} + setText={(e) => { + }} + /> + </div> + </SearchFormLeft> + <SearchFormRight + className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <div className="space-y"> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + onClick={(e) => { }} + > + 조회 + </Button> + <Button + size={"sm"} + onClick={(e) => { }} + > + 검토완료 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={"재이용검토현황"} + tableData={[]} + children={undefined} + headlist={ruseHstryHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + }} + onPageChange={() => { }} + ></UiTable> + <hr /> + <Form {...form}> + <h3 className="mt-4 ml-2 text-2xl font-medium text-label">수요신청 상세현황</h3> + <h1 className="mt-1 ml-3">표준공사비 산정</h1> + <div className="flex items-center mx-3 mb-3"> + <h1 className="">처리시설 표준공사비 산정</h1> + <Button + size={"sm"} + onClick={(e) => { }} + > + 재계산 + </Button> + <Button + size={"sm"} + onClick={(e) => { }} + > + 저장 + </Button> + + </div> + <div className="grid grid-cols-12 gap-4 ml-4 mr-4"> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="용도"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="요구수량(M3/d)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <Label className="">용도에 따른 처리공정<i className="diveicon di-search" /></Label> + + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value || ""} + className="mt-2" + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label="처리시설 용량(요구수량 * 365일)"> + <Input + placeholder="" + type="number" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <Label>처리시설용량에 따른 공사비(백만원)</Label> + <Button + size={"sm"} + onClick={(e) => { }} + className="bg-none" + > + 공사비 산정표 보기 + </Button> + </div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="number" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <Label>시설부대경비 요율(%)</Label> + <Button + size={"sm"} + onClick={(e) => { }} + > + 요율표 보기 + </Button> + </div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="number" + step="0.01" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-3 mt-4"> + <Label>총사업비 (백만원)<i className="diveicon di-search" /></Label> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + className="mt-2" + type="number" + step="0.01" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <Label>공급처 선정</Label> + <Button + size={"sm"} + onClick={(e) => { }} + className="bg-none" + > + 후보지 선택 + </Button> + </div> + <FormField + control={form.control} + name="" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-4 mt-4"> + <div className="mt-4"> + <Label>관로 표준공사비 산정</Label> + </div> + </div> + </div> + </Form> + </ListDiv> + </div> + </div> + </ContentsDiv > + </> +) +} \ No newline at end of file diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx index 086e76f22..ff6ef558d 100644 --- a/apps/wr/app/page.tsx +++ b/apps/wr/app/page.tsx @@ -41,6 +41,13 @@ export default function Home() { <div> <h2 className="text-xl font-semibold mb-4">지도점검 결과</h2> <div className="grid grid-cols-12 gap-4"> + <div className="col-span-2"> + <Button className="w-full"> + <Link href="ruseFcltMng/prfmncReg/pbnstPrfmncReg"> + 하수재이용실적 등록 + </Link> + </Button> + </div> <div className="col-span-2"> <Button className="w-full"> <Link href="ruseFcltMng/prfmncReg/chkgdnReg"> @@ -112,6 +119,23 @@ export default function Home() { </div> </div> </div> + + {/* 수요처 매칭 */} + <div> + <h2 className="text-xl font-semibold mb-4">수요처 매칭</h2> + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-2"> + <Button className="w-full"> + <Link href="matMng/matDmd/coMatRvw">수요처 매칭 검토</Link> + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full"> + <Link href="matMng/matDmd/ruseRvwHstry">재이용검토이력</Link> + </Button> + </div> + </div> + </div> </div> </> ); diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 8994e46c1..86dce20d6 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -6,6 +6,7 @@ import { SearchFormLeft, SearchInput, Title, + UiModal, UiTable, } from "@common/business_components"; import { Button, Input } from "@common/components"; @@ -14,46 +15,91 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components"; import { useEffect, useState } from "react"; import ListDiv from "@common/business_components/ui/list_div"; import { tableData } from "@/components/lib/func/commom"; -import type { customPbnst, ruseFcltType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import type { + customPbnst, + fcBizSchema, + fcBizType, + pubHasuType, + ruseFcltType, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm"; +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 { code, pbnst, pubHasuInit } = useWrStore((state) => state); - const { pubFacHeadList,pbnstHasuRes } = pbnst; - - - const {pbnstRes} = pbnst + const { + code, + pbnst, + searchFclts, + pubHasuInit, + pbnstRegClick, + getSwtrmtFcltNm, + pbnstMngData, + pbnstRuseFcltUpdate, + pbnstRuseBizUpdate, + } = useWrStore((state) => state); + const { pubFacHeadList, pbnstHasuRes, pbnstRes } = pbnst; const [currentTab, setCurrentTab] = useState<string>("mng"); - const [fcltMng, setFcltMng] = useState<ruseFcltType[]>(); + const [pbnstBiz, setPbnstBiz] = useState<fcBizType[]>(); + const [isRuseReg, setIsRuseReg] = useState<{ + isModal: boolean; + data: pubHasuType; + }>({ isModal: false, data: {} as pubHasuType }); + const [searchTxt, setSearchTxt] = useState<string>(); - const [bdstTable, setbdstTable] = useState<customPbnst[]>(); + const [bdstTable, setbdstTable] = useState<pubHasuType[]>(); + const [selectBdst, setSelectBdst] = useState<customPbnst | null>(null); -useEffect(() => { + useEffect(() => { if (!pbnst.isInit) { - pubHasuInit(undefined); + pubHasuInit("1", "11"); } }, [pbnst.isInit, pubHasuInit]); -useEffect(() => { - if (code.sggCd.length > 0) { - setbdstTable(tableData(pbnstHasuRes, { code })); + + 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){ + setbdstTable(tableData(searchFclts, { code })); + }else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { + setbdstTable(tableData(pbnstHasuRes, { code })); + } } - }, [code, pbnstHasuRes]); + }, [code, pbnstHasuRes,searchFclts]); - - const onSelectValue = (val: string) => { - console.log(val); - }; const cellClick = (val: number | undefined) => { if (val === undefined) return; - - setFcltMng([]); - }; - const resetContents = () => { - setFcltMng([]); // 빈 배열로 설정하여 신규 모드로 전환 + const selectRow = pbnstHasuRes?.[val]; + if (selectRow) { + setIsRuseReg({ ...isRuseReg, data: selectRow }); + pbnstRegClick(selectRow); + } }; const handleTabChange = (value: string) => { @@ -63,7 +109,7 @@ useEffect(() => { return ( <> <ContentsDiv> - <div className="grid grid-cols-4 gap-4"> + <div className="grid grid-cols-4 gap-4"> <div className="col-span-4"> <Title title={"하수재이용 시설 등록"} /> <SearchDiv> @@ -81,6 +127,9 @@ useEffect(() => { className="" size={"sm"} onClick={(e) => { + searchTxt && searchTxt?.length > 0 + ? getSwtrmtFcltNm(searchTxt) + : pubHasuInit("1", "11") }} > 조회 @@ -88,7 +137,15 @@ useEffect(() => { <Button size={"sm"} className="" - onClick={() =>{}} + onClick={() => { + pbnstMngData([] as customPbnst[]); + isRuseReg.data.bscFctCd + ? setIsRuseReg({ ...isRuseReg, isModal: true }) + : ToastFormUi({ + body: "하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요", + title: "재이용시설 등록", + }); + }} > 신규등록 </Button> @@ -102,37 +159,57 @@ useEffect(() => { <div className="col-span-4 h-full min-h-0"> <ListDiv className="min-h-0"> <ScrollArea className="h-[60vh]"> - <UiTable + <UiTable tableData={bdstTable || []} headlist={pubFacHeadList} pageSize={100} total={10} headName={"공공 하수처리시설 등록"} cellClick={cellClick} - onPageChange={(page: number) => { } } children={undefined} /> - </ScrollArea> + onPageChange={(page: number) => {}} + children={undefined} + /> + </ScrollArea> </ListDiv> </div> <ListDiv className="col-span-2 pr-6 "> - <ScrollArea className="h-[60vh]"> - <Tabs defaultValue="mng" onValueChange={handleTabChange}> - <TabsList className=""> - <TabsTrigger className="!text-xl" value="mng">시설정보</TabsTrigger> - <TabsTrigger className="!text-xl" value="buse">차수별정보</TabsTrigger> + <ScrollArea className="h-[60vh]"> + <Tabs defaultValue="mng" onValueChange={handleTabChange}> + <TabsList className=""> + <TabsTrigger className="!text-xl" value="mng"> + 시설정보 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="buse"> + 차수별정보 + </TabsTrigger> + <Button size={"sm"} onClick={ saveRuseData}className="!justify-end"> + 저장 + </Button> </TabsList> - <TabsContent className="max-h-full" value="mng"> - <PbnstMngForm data={fcltMng || []} /> - </TabsContent> - <TabsContent className="max-h-full" value="buse"> - <PbnstBuseForm /> - </TabsContent> - </Tabs> - </ScrollArea> + <TabsContent className="max-h-full" value="mng"> + <PbnstMngForm formType="update" /> + </TabsContent> + <TabsContent className="max-h-full" value="buse"> + <PbnstBuseForm formType="update" /> + </TabsContent> + </Tabs> + </ScrollArea> </ListDiv> + <UiModal + body={ + <PbnstRegModal + data={isRuseReg.data || ({} as customPbnst)} + ></PbnstRegModal> + } + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={isRuseReg.isModal} + onOpenChange={(open) => { + setIsRuseReg({ data: {} as pubHasuType, isModal: open }); + }} + /> </div> + <Toaster position="top-center" /> </ContentsDiv> </> ); } - - diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx index c294bdf93..837cf23f1 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx @@ -14,23 +14,28 @@ import { Button } from "@common/components/ui"; import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; import { useCallback, useEffect, useRef, useState } from "react"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; -import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; +import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { excelDownload, getCityOrSgg } from "@/components/lib/func/commom"; export default function PbnstSttn() { - const { code, pbnstSttn } = useWrStore( - (state) => state - ); - + const { code, pbnstSttn, pbnstSttnSearch } = useWrStore((state) => state); + const ref = useRef<ReturnType>({} as ReturnType); + const codeData = getCityOrSgg(ref); const [pageNation, setPageNation] = useState(1); const [isChHead, setIsChHead] = useState(false); const [bdstTable, setBdstTable] = useState<customPbnst[]>(); - // const nodeList = headMakeprfmnc(pbnstSttn.pbnSttHeadList, pbnstSttn.pbnSttHeadNm); const excelClick = useCallback(() => { // if (codeData) pbnstSttnSearch(1, "10000", codeData); - }, []); + excelDownload( + [[]], + "하수재이용시설 현황", + "sample_pbnstSttn.xlsx", + "A1:U2" + ); + }, [pbnstSttnSearch, codeData]); return ( <> @@ -67,7 +72,7 @@ export default function PbnstSttn() { // pbnstSttnSearch(1, "20", con); }} excelClick={excelClick} - initClick={function (): void {}} + initClick={function (): void { }} btnClass={btnClass} ref={ref} /> @@ -85,7 +90,7 @@ export default function PbnstSttn() { headlist={pbnstSttn.pbnstSttHeadList} pageSize={20} total={100000} - cellClick={(index: number | undefined,headId:string)=>{} } + cellClick={(index: number | undefined, headId: string) => { }} onPageChange={setPageNation} ></UiTable> </ListDiv> diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx new file mode 100644 index 000000000..c5dce841b --- /dev/null +++ b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx @@ -0,0 +1,155 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { excelDownload, yearOptions } from "@/components/lib/func/commom"; +import PbnPrfExcelModal from "@/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal"; +import { + ContentsDiv, + Title, + SearchDiv, + SearchFormLeft, + SelectBox, + SearchInput, + SearchFormRight, + ScrollArea, + UiTable, + UiModal, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, Label } from "@common/components/ui"; +import { useState, useEffect, useCallback } from "react"; +import { set } from "react-hook-form"; +import { Toaster } from "react-hot-toast"; + +export default function prfmncPrfmncReg() { + const { code, prfmnc, prfmncHasuInit, } = useWrStore( + (state) => state + ); + const { pbnstPrfmncHeadList, monthHeadList, prfmncRes } = prfmnc; + const [bdstTable, setbdstTable] = useState<pubHasuType[]>(); + const [searchTxt, setSearchTxt] = useState<string>(); + const [isExcelOpen, setIsExcelOPen] = useState<boolean>(false); + + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + + + const excelClick = useCallback(() => { + console.log("dd") + excelDownload( + [[]], + "하수재이용실적", + "sample_pbnstPrfmncReg.xlsx", + "A1:O1" + ); + }, [prfmnc.excelList]) + + useEffect(() => { + if (!prfmnc.isInit) { + prfmncHasuInit("1", "11"); + } + }, [prfmnc.isInit, prfmncHasuInit]); + useEffect(() => { + setbdstTable(prfmncRes) + console.log("prfmnc.prfmncRes", prfmncRes) + }, [prfmncRes]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"하수재이용 시설 등록"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-48 mt-2 text-xl text-gray-600"> + {"실적등록년도"} + </Label> + <SelectBox + className="w-full h-full text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={searchYear} + onSelectValue={(val: string | number) => + setSearchYear(String(val)) + } + /> + </div> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full text-2xl"} + label="" + textValue={searchTxt || ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </div> + </SearchFormLeft> + <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <div className="space-y"> + <div className="flex flex-row space-x-2"> + <Button size={"sm"} onClick={(e) => {}}> + 조회 + </Button> + <Button size={"sm"} onClick={() => {}}> + 저장 + </Button> + <Button size={"sm"} onClick={() => {setIsExcelOPen(true)}}> + 파일등록 + </Button> + <Button color="green" size={"sm"} onClick={excelClick}> + 양식받기 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-12 gap-4 h-full"> + <div className="col-span-4 h-full min-h-0"> + <ListDiv className="min-h-0"> + <ScrollArea className="h-[60vh]"> + <UiTable + tableData={bdstTable || []} + headlist={pbnstPrfmncHeadList} + pageSize={100} + total={10} + headName={"하수재이용시설 목록"} + cellClick={() => {}} + onPageChange={(page: number) => {}} + children={undefined} + /> + </ScrollArea> + </ListDiv> + </div> + <ListDiv className="col-span-8 pr-6 "> + <ScrollArea className="h-[60vh]"> + <UiTable + tableData={[]} + headlist={monthHeadList} + pageSize={100} + total={10} + headName={"하수처리수재이용 실적(m3) 등록결과"} + cellClick={() => {}} + onPageChange={() => {}} + children={undefined} + /> + </ScrollArea> + </ListDiv> + </div> + </ContentsDiv> + <UiModal bodyClass={""} body={<PbnPrfExcelModal></PbnPrfExcelModal>} open={isExcelOpen} onOpenChange={() => {setIsExcelOPen(!isExcelOpen)}}></UiModal> + <Toaster position="top-center" /> + + </> + ); +} \ No newline at end of file diff --git a/apps/wr/app/store/common/code/index.ts b/apps/wr/app/store/common/code/index.ts index 86a03a120..3f6da6dec 100644 --- a/apps/wr/app/store/common/code/index.ts +++ b/apps/wr/app/store/common/code/index.ts @@ -89,6 +89,43 @@ const chBuCd = [ { val: "개축", text: "개축" }, { val: "재축", text: "재축" }, ] + + + +const dmdUszCd = [ + { val: "조경용수", text: "조경용수" }, + { val: "청소화장실용수", text: "청소화장실용수" }, + { val: "농업용수", text: "농업용수" }, + { val: "공업용수", text: "공업용수" }, + { val: "친수용수", text: "친수용수" }, + { val: "기타", text: "기타" }, + +] + + +const pplQulity = [ + { val: "도복장강관", text: "도복장강관" }, + { val: "PE관", text: "PE관" }, + { val: "스테인레스관", text: "스테인레스관" }, + { val: "덕타일주철관", text: "덕타일주철관" }, + { val: "유리섬유복합관", text: "유리섬유복합관" }, + { val: "기타", text: "기타" }, + +] +const pplLenght = [ + { val: "D100", text: "D100" }, + { val: "D200", text: "D200" }, + { val: "D250", text: "D250" }, + { val: "D300", text: "D300" }, + { val: "D350", text: "D350" }, + { val: "D400", text: "D400" }, + { val: "D450", text: "D450" }, + { val: "D500", text: "D500" }, + { val: "D700", text: "D700" }, + { val: "D900", text: "D900" }, + { val: "D1000​", text: "D1000​" }, + +] export const buildingType = { RST:"RST", RNB:"RNB", @@ -109,6 +146,9 @@ export type comCodeState = { lclgvrnmntCd: EnvPartSelectDataType[]; orgwtKndCd: SelectDataType[]; yn: SelectDataType[]; + pplQulity: SelectDataType[]; + pplLenght: SelectDataType[]; + dmdUszCd:SelectDataType[] buildingType: { RST:string, RNB:string, @@ -247,7 +287,10 @@ export const comCodeStateExport: comCodeState = { mdmbsnCd: [], lgbsnCd: [], lclgvrnmntCd: [], + 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 e69de29bb..c1b46a901 100644 --- a/apps/wr/app/store/common/searchCode/index.ts +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -0,0 +1,52 @@ +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; + demandCompList: DemandCompType[]; +}; +export type comFcltActions = { + 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, + })); + }, + 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) + } + } + }; +}; diff --git a/apps/wr/components/ui/table/chkgdTable/rainChjgdDetail/rainChkgdDetailTable.tsx b/apps/wr/app/store/common/searchCode/type.tsx similarity index 100% rename from apps/wr/components/ui/table/chkgdTable/rainChjgdDetail/rainChkgdDetailTable.tsx rename to apps/wr/app/store/common/searchCode/type.tsx diff --git a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx new file mode 100644 index 000000000..906c97047 --- /dev/null +++ b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx @@ -0,0 +1,109 @@ +import { z } from "zod"; +import type { CommonTableProps } from "../../common/type/headder"; + +export const sttnHeadListType = z.array( + z.object({ + id: z.string(), + title: z.string(), + visible: z.boolean(), + }) +); + +export const coRvwHeadListExport: z.infer<typeof sttnHeadListType> = [ + { + id: "ctpv", + title: "시도", + visible: true, + }, + { + id: "sgg", + title: "시군구", + visible: true, + }, + { + id: "coNm", + title: "회사명", + visible: true, + }, + { + id: "addr", + title: "공장주소", + visible: true, + }, + { + id: "telNo", + title: "전화번호", + visible: true, + }, + { + id: "regDt", //바꾸기 + title: "신청일자", + visible: true, + }, + { + id: "state", //바꾸기 + title: "진행상태", + visible: true, + }, +]; + + +// id는 다확인해야함 +export const coRvwNodeListExport: CommonTableProps[] = [ + { + id: "ctpv", + title: "시도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sgg", + title: "시군구", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "coNm", + title: "회사명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "addr", + title: "공장주소", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + },{ + id: "telNo", + title: "전화번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "regDt", //바꾸기 + title: "신청일자", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "state", //바꾸기 + title: "진행상태", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + +]; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx new file mode 100644 index 000000000..6e62da829 --- /dev/null +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -0,0 +1,34 @@ +import type { z } from "zod"; +import type { sttnHeadListType } from "../coSttn/headList"; +import { coRvwHeadListExport, coRvwNodeListExport } from "./headList"; +import type { CommonTableProps } from "../../common/type/headder"; + +const coRvwHeadList = coRvwHeadListExport; +const coRvwNodeList = coRvwNodeListExport; + +export type coRvwState = { + coRvw: { + isInit: boolean; + coRvwHeadList: z.infer<typeof sttnHeadListType>; + coRvwNodeList: CommonTableProps[]; + + }; +}; + +export const coRvwStateExport: coRvwState = { + coRvw: { + isInit: false, + coRvwHeadList: coRvwHeadList, + coRvwNodeList: coRvwNodeList, + } +} + +export type coRvwType = coRvwState & coRvwActions; + +type coRvwActions = { +}; + +export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { + return { + } +} \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx b/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx new file mode 100644 index 000000000..f394229cf --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx @@ -0,0 +1,185 @@ +import { z } from "zod"; +import type { CommonTableProps } from "../../common/type/headder"; + +export const sttnHeadListType = z.array( + z.object({ + id: z.string(), + title: z.string(), + visible: z.boolean(), + }) +); + +export const ruseHstryHeadListExport: z.infer<typeof sttnHeadListType> = [ + { + id: "ctpv", + title: "시도", + visible: true, + }, + { + id: "sgg", + title: "시군구", + visible: true, + }, + { + id: "coNm", + title: "회사명", + visible: true, + }, + { + id: "addr", + title: "공장주소", + visible: true, + }, + { + id: "telNo", + title: "전화번호", + visible: true, + }, + { + id: "regDt", //바꾸기 + title: "신청일자", + visible: true, + }, + { + id: "rvwDt", //바꾸기 + title: "검토일자", + visible: true, + }, + { + id: "matDt", //바꾸기 + title: "매칭 완료일자", + visible: true, + }, + { + id: "gong", //바꾸기 + title: "공급처", + visible: true, + }, + { + id: "dmndQty", //바꾸기 + title: "요구수량(M3/d)", + visible: true, + }, + { + id: "useUsgCd", //바꾸기 + title: "사용용도", + visible: true, + }, + { + id: "state", //바꾸기 + title: "진행상태", + visible: true, + }, + { + id: "matYn", //바꾸기 + title: "매칭완료 여부", + visible: true, + }, +]; + + +// id는 다확인해야함 +export const ruseHstryNodeListExport: CommonTableProps[] = [ + { + id: "ctpv", + title: "시도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sgg", + title: "시군구", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "coNm", + title: "회사명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "addr", + title: "공장주소", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + },{ + id: "telNo", + title: "전화번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "regDt", //바꾸기 + title: "신청일자", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "rvwDt", //바꾸기 + title: "검토일자", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + },{ + id: "matDt", //바꾸기 + title: "매칭 완료일자", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + },{ + id: "gong", //바꾸기 + title: "공급처", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "dmndQty", //바꾸기 + title: "요구수량(M3/d)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "useUsgCd", //바꾸기 + title: "사용용도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "state", //바꾸기 + title: "진행상태", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "matYn", //바꾸기 + title: "매칭완료 여부", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + +]; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx b/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx new file mode 100644 index 000000000..e41f792b6 --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx @@ -0,0 +1,34 @@ +import type { z } from "zod"; +import type { sttnHeadListType } from "../coSttn/headList"; +import type { CommonTableProps } from "../../common/type/headder"; +import { ruseHstryHeadListExport, ruseHstryNodeListExport } from "./headList"; + +const ruseHstryHeadList = ruseHstryHeadListExport; +const ruseHstryNodeList = ruseHstryNodeListExport; + +export type ruseHstryState = { + ruseHstry: { + isInit: boolean; + ruseHstryHeadList: z.infer<typeof sttnHeadListType>; + ruseHstryNodeList: CommonTableProps[]; + + }; +}; + +export const ruseHstryStateExport: ruseHstryState = { + ruseHstry: { + isInit: false, + ruseHstryHeadList: ruseHstryHeadList, + ruseHstryNodeList: ruseHstryNodeList, + } +} + +export type ruseHstryType = ruseHstryState & ruseHstryActions; + +type ruseHstryActions = { +}; + +export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (set: any) => { + return { + } +} \ No newline at end of file diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 110c684bf..c6737b31e 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -66,7 +66,11 @@ export const rswtChkgdnStateExport: rswtChkgdnState = { rswtChkHeadList: rswtChkHeadList, rswtDetailHeadList: rswtDetailHeadList, rswtHeadNm: rswtHeadNm, - excelList: [{ data: [[]], headerNum: 0, headerRange: "A1:V3" }], + excelList: [{ + data: [[]], headerNum: 0, headerRange: "A1:V3", + sheetName: "", + startCol: 0 + }], rswtChkRes: [], rswtChkPolGuide: [], isInit: false, diff --git a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/excel.tsx b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/excel.tsx new file mode 100644 index 000000000..b0cbb7724 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/excel.tsx @@ -0,0 +1,43 @@ +import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; + + + const nameMap = { + "세척수": "wshwtr", + "냉각수": "coolUswtr", + "청소수": "cleanUswtr", + "식수대": "drwtUswtr", + "희석용수": "dltnUswtr", + "중수도": "ithlRswtr", + "기타": "ithlEtcUswtr", + "청소화장실용수": "cleanToilUswtr", + "세척살수용수": "resn", + "조경용수": "lndwtr", + "친수용수": "hydUswtr", + "지하수충전": "ugwtChr", + "농업용수": "farmUswtr", + "하천등유지용수": "rvrKeepUswtr", + "공업용수": "indstUswtr" + }; + + +export const excelToPbnPrfData = (excelData: any[], year: string) => { + // 월별 데이터 하나로 병합 + const merged = {} as any; + excelData.forEach((item: any) => { + const key = nameMap[item["용수명"]]; + if (!key) return; + + for (let i = 1; i <= 12; i++) { + const month = i.toString().padStart(2, "0"); + const ym = year + month; + + if (!merged[ym]) merged[ym] = { ym }; + merged[ym][key] = Number(item[`${i}월`] ?? 0); + } + }); + + const pbnPrfData = Object.values(merged); + console.log(pbnPrfData); + + return pbnPrfData; +}; \ No newline at end of file diff --git a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/headList.tsx b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/headList.tsx new file mode 100644 index 000000000..a41eeba52 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/headList.tsx @@ -0,0 +1,34 @@ +import { z } from "zod"; +import type { TableUpperProps } from "../../ruseFcltMng/fcltReg/rainReg"; + +export const pbnPrfRegExcelZod = z.object({ + ym: z.string().default(""), + bscFctNm: z.string().default(""), + pbnstRuseFcltNm: z.string().default(""), + file: z.instanceof(File).optional(), +}); + +export const pbnstPrfmncHeadListExport: TableUpperProps[] = [ + { id: "sido", title: "시도" }, + { id: "gugun", title: "시군구" }, + { id: "bscFctNm", title: "시설명" }, + { id: "sisul", title: "재이용시설명" }, + { id: "sisulCd", title: "재이용시설코드" }, +]; + +export const monthHeadListExport: TableUpperProps[] = [ + { id: "gubun", title: "구분" }, + { id: "total", title: "계" }, + { id: "mon1", title: "1월" }, + { id: "mon2", title: "2월" }, + { id: "mon3", title: "3월" }, + { id: "mon4", title: "4월" }, + { id: "mon5", title: "5월" }, + { id: "mon6", title: "6월" }, + { id: "mon7", title: "7월" }, + { id: "mon8", title: "8월" }, + { id: "mon9", title: "9월" }, + { id: "mon10", title: "10월" }, + { id: "mon11", title: "11월" }, + { id: "mon12", title: "12월" }, +]; diff --git a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx new file mode 100644 index 000000000..282c277ec --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx @@ -0,0 +1,79 @@ +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; +import type { TableUpperProps } from "../../ruseFcltMng/fcltReg/rainReg"; +import { + pbnstPrfmncHeadListExport, + monthHeadListExport, +} from "./headList"; +import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; + +const pbnstPrfmncHeadList = pbnstPrfmncHeadListExport; +const monthHeadList = monthHeadListExport; + +export type pbnstPrfmncState = { + prfmnc: { + isInit: boolean; + pbnstPrfmncHeadList: TableUpperProps[]; + monthHeadList: TableUpperProps[]; + prfmncRes: pubHasuType[]; + excelList: Number[]; + } +} + +export const pbnstPrfmncStateExport: pbnstPrfmncState = { + prfmnc: { + isInit: false, + pbnstPrfmncHeadList: pbnstPrfmncHeadList, + monthHeadList: monthHeadList, + prfmncRes: [], + excelList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + } +} + +export type pbnstPrfmncType = pbnstPrfmncState & pbnstPrfmncActions; + +type pbnstPrfmncActions = { + prfmncHasuInit: (page: string, sggOrCity: string) => void; +} + +const checkWrRegList = async () => { + const wrRegList = await getFetch("wr", `/reuseFc/pubFc`); + return wrRegList; +}; +const hasuListFetch = async (page: string, sggOrCity: string) => { + const checkWrRegListRes = await checkWrRegList(); + + const rainRes = await postFectch( + `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity ? sggOrCity : "11"}/0/1/SW`, + checkWrRegListRes, + fetchType.COMMON + ); + + const hasuList = rainRes.data.map((item: any) => { + return { + ...item, + ruse: item.ruse === false ? "N" : "Y", + }; + }); + + return hasuList; +}; +export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (set: any) => { + return { + prfmncHasuInit: async (page: string, sggOrCity: string) => { + try { + const prfmncRes = await hasuListFetch(page, sggOrCity); + console.log("prfmncRes", prfmncRes) + return set((state: pbnstPrfmncState) => ({ + ...state, + prfmnc: { + ...state.prfmnc, + prfmncRes: prfmncRes, + isInit: false, + }, + })); + } catch (errorHasu) { + console.log(errorHasu); + } + }, + } +} \ No newline at end of file diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx index f24d33ad3..186bfd3c0 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx @@ -1,10 +1,6 @@ import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { TableUpperProps } from "../rainReg"; - - - - export const pubFacHeadListExport: TableUpperProps[] = [ { id: "sido", title: "시도" }, { id: "gugun", title: "시군구" }, @@ -12,65 +8,128 @@ export const pubFacHeadListExport: TableUpperProps[] = [ { id: "addr", title: "위치" }, { id: "efflWqltStndrdNm", title: "지역구분" }, { id: "fctCpc", title: "시설용량" }, - { id: "yn", title: "재이용여부" }, - { id: "authoDay", title: "등록여부" }, + { id: "ruse", title: "재이용여부" }, + { id: "rswtrYn", title: "등록여부" }, ]; -export const pubMngUpHeadListExport: TableUpperProps[] = [ - { id: "mng", title: "운영방식" }, - { id: "subCo", title: "대행업체" }, - { id: "startDay", title: "시작일", upName: "upChangeRe", upSequnce: 1 }, - { id: "endDay", title: "종료일", upName: "upChangeRe", upSequnce: 2 }, +export const pbnstMngUpHeadListExport: TableUpperProps[] = [ + { id: "choose", title: "선택", type: "checkbox" }, + { id: "operMthd", title: "운영방식", edit: true }, + { id: "rprtvBzenty", title: "대행업체", edit: true }, + { + id: "bgngDt", + title: "시작일", + upName: "upChangeRe", + upSequnce: 1, + type: "date", + }, + { + id: "endDt", + title: "종료일", + upName: "upChangeRe", + upSequnce: 2, + type: "date", + }, ]; -const date = new Date(); -export const pubMngNmExport: chHeadType[] = [ +export const pplInstlsHeadListExport: TableUpperProps[] = [ + { id: "choose", title: "선택", type: "checkbox" }, + { id: "ppMqlt", title: "관재질", type: "select", selectData: "pplQulity" }, + { id: "pdmt", title: "관경", type: "select", selectData: "pplLenght" }, + { id: "cpct", title: "연장", edit: true, type: "number" }, +]; + +export const pbnstDemandHeadListExport: TableUpperProps[] = [ + { id: "dmdCoNm", title: "수요처명" }, + { id: "daddr", title: "위치(주소)" }, + { id: "useUsgCd", title: "사용용도" }, + { id: "splyBgngDay", title: "공급시작일" }, + { id: "splyEndDay", title: "공급종료일" }, + { id: "splyCpct", title: "공급량(m³/일)" }, + { id: "splyFmYon", title: "공급유량계 유무" }, +]; + +export const pbnstOrgwtHeadListRxport: TableUpperProps[] = [ + { id: "ruseUtztnFcltNm", title: "운영방식" }, + { id: "orgwtWtritQnt", title: "원수취수량" }, + { id: "tmsYn", title: "하수처리량TMS 전후" }, + { id: "dschgFmYn", title: "하수처리장 방류 유량계 전·후​" }, + { id: "dtlPstnExpln", title: "상세위치 설명" }, +]; +export const fcOrgwtsHeadListExport: TableUpperProps[] = [ + { id: "choose", title: "선택", type: "checkbox" }, + { id: "sido", title: "시도" }, + { id: "gugun", title: "시군구" }, + { id: "bscFctNm", title: "시설명" }, + { id: "addr", title: "위치" }, +]; + + +export const pbnstMngNmExport: chHeadType[] = [ { headNm: "upChangeRe", text: "운영대행기간", length: 2 }, ]; -export type formDetailType ={ - id:string, - lable:string - type?:string -} +export type formDetailType = { + id: string; + lable: string; + type?: string; +}; +export const wasteWaterFcltINfo: formDetailType[] = [ + { id: "wwtEmsnFcltPrmsnYn", lable: "폐수배출물신고 허가여부", type: "selectYn" }, + { id: "tmsInstlYn", lable: "Tms설치여부", type: "selectYn" }, + { id: "pbnstDrngInflwExclYn", lable: "공공하수도유입제외 허가여부", type: "selectYn" }, +]; -export const wasteWaterFcltINfo:formDetailType[]=[ - {id:'wwtEmsnFcltPrmsnYn' , lable:'폐수배출물신고 허가여부'}, - {id:'tmsInstlYn' , lable:'Tms설치여부'}, - {id:'pbnstDrngInflwExclYn' , lable:'공공하수도유입제외 허가여부'}, -] +export const concentratedWaterInfo: formDetailType[] = [ + { id: "erctWtrOcrnCpct", lable: "눙축수 발생량(m³/일)", type: "number" }, + { id: "erctUswtrLinkSpt", lable: "눙축수연계지점" }, + { id: "erctUswtrFcltCpct", lable: "시설용량(m³/일)", type: "number" }, + { id: "erctUswtrPrcsMeth", lable: "눙축수처리방식" }, + { id: "erctUswtrPrcsMsrcCd", lable: "눙축수처리공법" }, +]; -export const concentratedWaterInfo:formDetailType[]=[ -{id:'erctWtrOcrnCpct' , lable:'눙축수 발생량(m³/일)'}, -{id:'erctUswtrLinkSpt' , lable:'눙축수연계지점'}, -{id:'erctUswtrFcltCpct' , lable:'시설용량(m³/일)'}, -{id:'erctUswtrPrcsMeth' , lable:'눙축수처리방식'}, -{id:'erctUswtrPrcsMsrcCd' , lable:'눙축수처리공법'}, -] +export const ruseFcltinfo: formDetailType[] = [ + { id: "rprcsFcltCpct", lable: "시설용량(m³/일)", type: "number" }, + { id: "majorPrcsMsrcCd", lable: "주처리공법" }, + { id: "addr", lable: "위치(주소)" }, + { id: "rprcsMthdCd", lable: "재처리방식" }, + { id: "inoutYn", lable: "위치(처리장 내외)" ,type: "selectYn" }, // 머가 내? 외?, + { id: "mainSplyPumpCpct", lable: "메인공급펌프 용량", type: "number" }, +]; -export const ruseFcltinfo:formDetailType[]=[ - {id:'rprcsFcltCpct' , lable:'시설용량(m³/일)'}, - {id:'majorPrcsMsrcCd' , lable:'주처리공법'}, - {id:'addr' , lable:'위치(주소)'}, - {id:'rprcsMthdCd' , lable:'재처리방식'}, - {id:'inoutYn' , lable:'위치(처리장 내외)'},// 머가 내? 외?, - {id:'mainSplyPumpCpct' , lable:'메인공급펌프 용량'}, - ] - +export const businessMoneyInfo: formDetailType[] = [ + { id: "gvsbs", lable: "국고", type: "number" }, + { id: "lcep", lable: "지방비", type: "number" }, + { id: "cusrAlotm", lable: "민간투자비", type: "number" }, + { id: "etc", lable: "기타" }, + { id: "totalMoney", lable: "합계", type: "number" }, +]; -export const businessMoneyInfo:formDetailType[]=[ - {id:'gvsbs' , lable:'국고'}, - {id:'lcep' , lable:'지방비'}, - {id:'username' , lable:'민간투자비'}, - {id:'cusr_alotm' , lable:'원인자부담금'}, - {id:'etc' , lable:'기타'}, - {id:'totalMoney' , lable:'합계'}, -] +export const businessSttnInfo: formDetailType[] = [ + { id: "seCd", lable: "구분" }, + { id: "bizMthdCd", lable: "사업방식" }, + { id: "bizCmcnDay", lable: "사업준공일", type: "date" }, + { id: "instlAprvSncnDt", lable: "설치승인인가일", type: "date" }, +]; -export const businessSttnInfo:formDetailType[]=[ - {id:'seCd' , lable:'구분'}, - {id:'bizMthdCd' , lable:'사업방식'}, - {id:'bizCmcnDay' , lable:'사업준공일' ,type:'date'}, - {id:'instlAprvSncnDt' , lable:'설치승인인가일' ,type:'date'} , -] \ No newline at end of file +export const ruseFcltIntakeList: formDetailType[] = [ + { id: "seCd", lable: "구분" }, + { id: "bizMthdCd", lable: "사업방식" }, + { id: "bizCmcnDay", lable: "사업준공일", type: "date" }, + { id: "instlAprvSncnDt", lable: "설치승인인가일", type: "date" }, +]; + +export const ruseWaterQulityList: formDetailType[] = [ + { id: "bod", lable: "BOD(mg/L)", type: "number" }, + { id: "grssClfg", lable: "총대장균군수(개/mg)", type: "number" }, + { id: "combResdch", lable: "결합잔류염소(mh/L)", type: "number" }, + { id: "trbt", lable: "탁도(NTU)", type: "number" }, + { id: "smll", lable: "냄새", type: "number" }, + { id: "crmty", lable: "색도", type: "number" }, + { id: "tn", lable: "총질소(mg/L)", type: "number" }, + { id: "tph", lable: "총인(mg/L)", type: "number" }, + { id: "ph", lable: "수소이온농도", type: "number" }, + { id: "clIonQnt", lable: "염화물(mg/L)", type: "number" }, + { id: "cdtnv", lable: "전기전도도(μs/cm)​", type: "number" }, +]; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index b8e3a00ea..22980aa16 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -1,35 +1,75 @@ import { z } from "zod"; -import type { TableUpperProps } from "../rainReg"; -import { getFetch } from "@/components/lib/api"; +import { TEST_UUID, type TableUpperProps } from "../rainReg"; +import { + deleteFectch, + fetchType, + getFetch, + postFectch, + putFectch, +} from "@/components/lib/api"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; -import { pubFacHeadListExport, pubMngNmExport, pubMngUpHeadListExport } from "./headList"; -import { exampleData, fcBizSchema, type customPbnst, type pubHasuType } from "./type"; +import { + exampleData, + fcBizSchema, + type customPbnst, + type fcBizType, + type pbnstInsertIndex, + type pubHasuType, +} from "./type"; import type { SelectDataType } from "@common/business_components/ui"; - +import { + pbnstMngUpHeadListExport, + pbnstOrgwtHeadListRxport, + pbnstMngNmExport, + pubFacHeadListExport, + pplInstlsHeadListExport, + pbnstDemandHeadListExport, + fcOrgwtsHeadListExport, +} from "./headList"; const selectLabel: SelectDataType[] = [{ text: "test", val: "1" }]; -const pubFacHeadList= pubFacHeadListExport; -const pubMngUpHeadList = pubMngUpHeadListExport -const pubMngNm= pubMngNmExport; - +const pubFacHeadList = pubFacHeadListExport; +const pbnstMngUpHeadList = pbnstMngUpHeadListExport; +const pbnstOrgwtHeadList = pbnstOrgwtHeadListRxport; +const pbnstMngNm = pbnstMngNmExport; +const pplInstlsHeadLisat = pplInstlsHeadListExport; +const pbnstDemandHeadList = pbnstDemandHeadListExport; +const fcOrgwtsHeadList = fcOrgwtsHeadListExport; export type pubFacState = { pbnst: { isInit: boolean; pubFacHeadList: TableUpperProps[]; - pubMngUpHeadList: TableUpperProps[]; - pubMngNm: chHeadType[]; - pbnstHasuRes: pubHasuType[] - selectLabel: SelectDataType[]; - pbnstRes: customPbnst[]; + pbnstMngUpHeadList: TableUpperProps[]; + pbnstMngNm: chHeadType[]; + pbnstHasuRes: pubHasuType[]; + pbnstRes: customPbnst[] | null; // 배열에서 단일 객체 또는 null로 변경 + pbnstResObj: customPbnst | null; // 배열에서 단일 객체 또는 null로 변경 pbnstBizCycle: SelectDataType[]; + selectLabel: SelectDataType[]; + pbnstOrgwtHeadList: TableUpperProps[]; + pplInstlsHeadLisat: TableUpperProps[]; + pbnstDemandHeadList: TableUpperProps[]; + fcOrgwtsHeadList: TableUpperProps[]; + pbnstMngRegData: customPbnst[]; + pbnsUpdateIndex: pbnstInsertIndex; + dmdComList: any[]; }; }; type pubFacActions = { - pbnstRegInit: (sgg: string) => void; - pbnstRegSearch: (txt: string | undefined, sgg: string) => void; + pubHasuInit: (page: string, sggOrCity: string) => void; + pbnstRegClick: (pubHasuRes: pubHasuType) => void; pbnstReg: (txt: string | undefined) => void; - pubHasuInit: (txt: string | undefined) => void; + pbnstInsert(pbnstRes: customPbnst): Promise<void>; + appendRuseFclt(array: customPbnst[]): void; + fileDelete: (atflCd: string) => void; + fileDownLoad: (atflCd: string) => void; + pbnstMngData: (data: customPbnst[]) => void; + pbnstBizData(data: fcBizType): void; + getBizCycle: (pbnstObj: customPbnst) => void; + getpbnstInsertIndex: (data: pbnstInsertIndex) => void; + pbnstRuseFcltUpdate: (ruseRes: customPbnst[]) => void; + pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void; }; export type pubFacType = pubFacState & pubFacActions; @@ -38,45 +78,101 @@ export const pubFacStateExport: pubFacState = { pbnst: { isInit: false, pubFacHeadList: pubFacHeadList, - pubMngUpHeadList: pubMngUpHeadList, - selectLabel: selectLabel, - pubMngNm: pubMngNm, + pbnstMngUpHeadList: pbnstMngUpHeadList, + pbnstMngNm: pbnstMngNm, + pbnstOrgwtHeadList: pbnstOrgwtHeadList, pbnstHasuRes: [], - pbnstRes: [], - pbnstBizCycle:[] + pbnstResObj: null, // 초기값을 null로 변경 + pbnstRes: [], // 초기값을 null로 변경 + pbnstBizCycle: [{ text: "없음", val: "1" }], + selectLabel: selectLabel, + pplInstlsHeadLisat: pplInstlsHeadLisat, + pbnstDemandHeadList: pbnstDemandHeadList, + fcOrgwtsHeadList: fcOrgwtsHeadList, + pbnstMngRegData: [], + pbnsUpdateIndex: {} as pbnstInsertIndex, + dmdComList: [], }, }; -const initFetch = async (txt: string | undefined) => { - const rainRes = await getFetch( - "common", - `/prcsFclt/info/1/10/A/41220` + (txt ? `/${txt}` : "") + "/500" + "/1" - ); - return rainRes; +const checkWrRegList = async () => { + const wrRegList = await getFetch("wr", `/reuseFc/pubFc`); + return wrRegList; }; -const getBizCycle = async (fcBizs: z.infer<typeof fcBizSchema>[] ) => { - const pbnstBizCycle =fcBizs.map((item: z.infer<typeof fcBizSchema>) => { +const hasuListFetch = async (page: string, sggOrCity: string) => { + const checkWrRegListRes = await checkWrRegList(); + + const rainRes = await postFectch( + `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity ? sggOrCity : "11"}/0/1/SW`, + checkWrRegListRes, + fetchType.COMMON + ); + + const hasuList = rainRes.data.map((item: any) => { return { - text: `${item.cycl}차수`, - val: item.cycl, + ...item, + ruse: item.ruse === false ? "N" : "Y", }; }); - return pbnstBizCycle; -} + + return hasuList; +}; + +const initFetch = async (ruseFcltPk: string | undefined) => { + const pbnstRes = await getFetch("wr", `/reuseFc/pubFc/${ruseFcltPk}`); + return pbnstRes; +}; + +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((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; + }); + pbnst.fcOpers = pbnst.fcOpers.map((oper) => { + oper.rgtr = TEST_UUID; + oper.mdfr = TEST_UUID; + return oper; + }); + pbnst.fcFiles = pbnst.fcFiles.map((file) => { + file.rgtr = TEST_UUID; + file.mdfr = TEST_UUID; + return file; + }); +}); + + + + return data; +}; export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { return { - - pubHasuInit: async (txt: string | undefined) => { + pubHasuInit: async (page: string, sggOrCity: string) => { try { - const pbnstHasuRes = await initFetch(txt); - + const pbnstHasuRes = await hasuListFetch(page, sggOrCity); return set((state: pubFacState) => ({ ...state, pbnst: { ...state.pbnst, - pbnstHasuRes:pbnstHasuRes, + pbnstHasuRes: pbnstHasuRes, isInit: false, }, })); @@ -85,39 +181,15 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { } }, - pbnstRegInit: async (txt: string | undefined) => { + pbnstRegClick: async (pubHasuRes: pubHasuType) => { try { - //const pbnstRes = await initFetch(txt); - - const pbnstRes = exampleData; - - const pbnstBizCycle = getBizCycle(pbnstRes?.fcBizs); + const pbnstRes = await initFetch(pubHasuRes.bscFctCd); + console.log('pbnstRes ' , pbnstRes) return set((state: pubFacState) => ({ ...state, pbnst: { ...state.pbnst, pbnstRes: pbnstRes, - pbnstBizCycle:pbnstBizCycle, - isInit: false, - }, - })); - } catch (errorHasu) { - console.log(errorHasu); - } - }, - pbnstRegSearch: async (txt: string | undefined, sgg: string) => { - try { - const pbnstRes = await getFetch( - "wr", - `/api/v1/reuseFc/pubFc` + (txt ? `/${txt}` : "") + `/${sgg}` - ); - const pbnstBizCycle = getBizCycle(pbnstRes.fcBizs); - return set((state: pubFacState) => ({ - ...state, - pbnst: { - ...state.pbnst, - pbnstReg: pbnstRes, - pbnstBizCycle:pbnstBizCycle, isInit: false, }, })); @@ -125,9 +197,10 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { console.log(errorHasu); } }, + pbnstReg: async (txt: string | undefined) => { try { - const pbnstRes = await initFetch(txt); + const pbnstRes = await hasuListFetch("11", "11"); return set((state: pubFacState) => ({ ...state, pbnst: { @@ -140,5 +213,167 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { console.log(errorHasu); } }, + pbnstInsert: async (pbnstRes: customPbnst) => { + try { + + const response = await postFectch( + `/reuseFc/pubFc`, + JSON.stringify( writeRgtrAndMdfr([pbnstRes])) + ); + + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstRes: response.data, + isInit: false, + }, + })); + } 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 { + text: `${item.cycl}차`, + val: item.cycl, + }; + }); + + console.log(" pbnstBizCycle ", pbnstBizCycle); + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstBizCycle: pbnstBizCycle, + pbnstResObj: pbnstObj, + isInit: false, + }, + })); + }, + fileDelete: async (atflCd: string) => { + try { + const response = await deleteFectch( + `/attach-service/api/v1/atfl/attachments/atflCd//${atflCd}`, + undefined, + fetchType.CS + ); + + console.log("File deleted successfully:", response); + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + isInit: false, + }, + })); + } catch (error) { + console.error("Error deleting file:", error); + } + }, + fileDownLoad: async (atflCd: string) => { + try { + const response = await getFetch( + fetchType.CS, + `/attach-service/api/v1/atflcd/download/${atflCd}` + ); + + if (!response.ok) { + throw new Error(`Error downloading file: ${response.statusText}`); + } + } catch (error) { + console.error("Error downloading file:", error); + } + }, + + pbnstMngData: (data: customPbnst[]) => { + if (data.length > 0) { + } + + return set((state: pubFacState) => { + state.pbnst.pbnstMngRegData = data; + state.pbnst.pbnstBizCycle = + data.length <= 0 + ? [{ text: "없음", val: "1" }] + : state.pbnst.pbnstBizCycle; + return state; + }); + }, + + pbnstBizData: (data: fcBizType) => { + return set((state: pubFacState) => { + if (state.pbnst.pbnstMngRegData) { + const regData = state.pbnst.pbnstMngRegData.find( + (item) => data.pbnstRuseFcltFk === item.pbnstRuseFcltPk + ); + if (regData) { + const bizIdx = regData.fcBizs.findIndex( + (item) => item.pbnstRuseFcltBizPk == data.pbnstRuseFcltBizPk + ); + + if (bizIdx >= 0) { + regData.fcBizs[bizIdx] = data; + const pbnstData = state.pbnst.pbnstMngRegData + state.pbnst.pbnstMngRegData= [...pbnstData] + + } + } + } + return state; + }); + }, + getpbnstInsertIndex: async (data: pbnstInsertIndex) => { + return set((state: pubFacState) => { + state.pbnst.pbnsUpdateIndex = data; + return state; + }); + }, + appendRuseFclt: async (array: customPbnst[]) => { + try { + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstRes: array, + 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 1a8f1479f..01aac072a 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -1,4 +1,5 @@ import { z } from "zod"; +import { TEST_UUID } from "../rainReg"; export type customPbnst = z.infer<typeof pbnstRegZod>; @@ -13,6 +14,11 @@ export type selectLabel = { val: string; }; +export type pbnstInsertIndex ={ + mng:number, + biz:number +} + export type publicReuseFacilityType = { sido: string; sigungo: string; @@ -27,13 +33,6 @@ export type publicReuseFacilityType = { register?: string; swtrmtFcltFk: string; }; -export type fcltOperInfoType = { - mng: string; - subCo: string; - startDay: string; - endDay: string; - pbnstRuseFcltPk: string; -}; export type ruseFcltType = { swtrmtFcltFk: string; @@ -45,398 +44,639 @@ 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>; + +export type operType = z.infer<typeof fcOperSchema>; +export type fcFileType = z.infer<typeof fcFileSchema>; + + const pplInstlSchema = z.object({ - ruseFcltBizFk: z.string(), - pdmt: z.string(), - ppMqlt: z.string(), - cpct: z.string(), + ruseFcltBizFk: z.number(), + choose:z.boolean().default(false), + pdmt: z.string().default(''), + ppMqlt: z.string().default(""), + cpct: z.number().default(0), }); const fcFileSchema = z.object({ - ruseFcltFk: z.number().default(0), - fileTkn: z.string().default("test"), - filePath: z.string().default("test"), - fileKnd: z.string().default("test"), - regDt: z.date().default(new Date()), - rgtr: z.string().default("test"), - mdfcnDt: z.date().default(new Date()), - mdfr: z.string().default("test"), + ruseFcltFk: z.number().nullish(), + fileTkn: z.string(), + filePath: z.string(), + fileKnd: z.string(), + regDt: z.date().default(new Date()).nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.date().default(new Date()).nullish(), + mdfr: z.string().nullish(), }); const fcOperSchema = z.object({ - pbnstRuseFcltOperInfoPk: z.number().default(0), - pbnstRuseFcltFk: z.number().default(0), + pbnstRuseFcltOperInfoPk: z.number().nullish(), + pbnstRuseFcltFk: z.string().nullish(), operMthd: z.string(), rprtvBzenty: z.string(), bgngDt: z.date().default(new Date()), endDt: z.date().default(new Date()), - regDt: z.date().default(new Date()), - rgtr: z.string().default("test"), - mdfcnDt: z.date().default(new Date()), - mdfr: z.string().default("test"), + regDt: z.date().nullish(), + rgtr: z.string().default(""), + mdfcnDt: z.date().nullish(), + mdfr: z.string().default(""), + choose: z.boolean().default(false), }); const fcOrgwtSchema = z.object({ - pbnstRuseFcltBizFk: z.number().default(0), + pbnstRuseFcltBizFk: z.string().nullish(), swtrmtFcltFk: z.number().default(0), - orgwtWtritQnt: z.string(), - tmsYn: z.string(), - dschgFmYn: z.string(), - dtlPstnExpln: z.string(), - regDt: z.date().default(new Date()), - rgtr: z.string().default("test"), - mdfcnDt: z.date().default(new Date()), - mdfr: z.string().default("test"), + orgwtWtritQnt: z.number(), + tmsYn: z.enum(["Y", "N"] ).default("N"), + dschgFmYn: z.enum(["Y", "N"] ).default("N"), + dtlPstnExpln: z.string().default(''), + regDt: z.date().nullish(), + rgtr: z.string().default(''), + mdfcnDt: z.date().nullish(), + mdfr: z.string().default(''), + choose: z.boolean().default(false), }); -const fcPrfmncSchema = z.object({ - pbnstRuseFcltFk: z.number().default(0), - ym: z.string(), - wshwtr: z.string(), - coolUswtr: z.string(), - cleanUswtr: z.string(), - drwtUswtr: z.string(), - dltnUswtr: z.string(), - ithlRswtr: z.string(), - ithlEtcUswtr: z.string(), - cleanToilUswtr: z.string(), - resn: z.string(), - lndwtr: z.string(), - hydUswtr: z.string(), - rvrKeepUswtr: z.string(), - farmUswtr: z.string(), - ugwtChr: z.string(), - indstUswtr: z.string(), - regDt: z.date().default(new Date()), - rgtr: z.string().default("test"), - mdfcnDt: z.date().default(new Date()), - mdfr: z.string().default("test"), -}); -export const fcBizSchema = z.object({ - pbnstRuseFcltBizPk: z.number().default(0), - pbnstRuseFcltFk: z.number().default(0), - cycl: z.string(), - seCd: z.string(), - bizMthdCd: z.string(), - instlAprvSncnDt: z.string(), - bizCmcnDay: z.string(), - gvsbs: z.string(), - lcep: z.string(), - cusrAlotm: z.string(), - etc: z.string(), - totalMoney: z.string(), - rprcsFcltCpct: z.string(), - majorPrcsMsrcCd: z.string(), - addr: z.string(), - rprcsMthdCd: z.string(), - inoutYn: z.string(), - mainSplyPumpCpct: z.string(), - erctWtrOcrnCpct: z.string(), - erctUswtrLinkSpt: z.string(), - erctUswtrFcltCpct: z.string(), - erctUswtrPrcsMeth: z.string(), - erctUswtrPrcsMsrcCd: z.string(), - wwtEmsnFcltPrmsnYn: z.string(), - tmsInstlYn: z.string(), - pbnstDrngInflwExclYn: z.string(), - bod: z.string(), - grssClfg: z.string(), - combResdch: z.string(), - trbt: z.string(), - clIonQnt: z.string(), - smll: z.string(), - crmty: z.string(), - tph: z.string(), - tn: z.string(), - ph: z.string(), - cdtnv: z.string(), - designCst: z.string(), - cstrnSpvsCst: z.string(), - fcltSbsdCst: z.string(), - wstCst: z.string(), - cnstCmcnCst: z.string(), - mntrgEqpmntInstlCst: z.string(), - gisDbCst: z.string(), - elpwrMtrngCst: z.string(), - mchnCst: z.string(), - archCst: z.string(), - pplCst: z.string(), - ruseFcltCst: z.string(), - designCstClcln: z.string(), - cstrnSpvsCstClcln: z.string(), - fcltSbsdCstClcln: z.string(), - wstCstClcln: z.string(), - cnstCmcnCstClcln: z.string(), - mntrgEqpmntInstlCstClcln: z.string(), - gisDbCstClcln: z.string(), - elpwrMtrngCstClcln: z.string(), - mchnCstClcln: z.string(), - archCstClcln: z.string(), - pplCstClcln: z.string(), - ruseFcltCstClcln: z.string(), - fcOrgwts: z.array(fcOrgwtSchema), - pplInstls: z.array(pplInstlSchema), - flg: z.string(), - regDt: z.string(), - rgtr: z.string(), - mdfcnDt: z.string(), - mdfr: z.string(), -}); - -export const pbnstRegZod = z.object({ - pbnstRuseFcltPk: z.number().default(0), - swtrmtFcltFk: z.string(), - ruseUtztnFcltNm: z.string(), - fcltCpct: z.string(), - ruseCpct: z.string(), - splyExtraCpct: z.string(), - regDt: z.date().default(new Date()), - rgtr: z.string().default("test"), - mdfcnDt: z.date().default(new Date()), - mdfr: z.string().default("test"), - fcFiles: z.array(fcFileSchema), - fcBizs: z.array(fcBizSchema), - fcOpers: z.array(fcOperSchema), -}); const pubHasuZod = z.object({ + choose: z.boolean().default(false), bscFctCd: z.string(), sido: z.string().default(""), gugun: z.string().default(""), bscFctNm: z.string().default(""), efflWqltStndrdNm: z.string().default(""), fctCpc: z.number().default(0), + addr:z.string().nullish() }); +const demandInfoList = z.object({ + +}) + + 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.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('') +}); + + + + + +export const fcBizSchema = z.object({ + pbnstRuseFcltBizPk: z.number().default(1), + pbnstRuseFcltFk: z.string().nullish(), + superkey: z.string().default("").nullish(), + cycl: z.number().default(1), + seCd: z.string().default(""), + bizMthdCd: z.string().default(""), + instlAprvSncnDt: z.date().nullish(), + bizCmcnDay: z.date().nullish(), + gvsbs: z.number().default(0), + lcep: z.number().default(0), + cusrAlotm: z.number().default(0), + etc: z.string().default(''), + totalMoney: z.number().default(0).nullish(), + rprcsFcltCpct: z.number().default(0), + majorPrcsMsrcCd: z.string().default(""), + addr: z.string().default(''), + rprcsMthdCd: z.string().default(""), + inoutYn: z.enum(["Y", "N"]).default("Y"), + mainSplyPumpCpct: z.number().nullish(), + erctWtrOcrnCpct: z.number().nullish(), + erctUswtrLinkSpt: z.string().nullish(), + erctUswtrFcltCpct: z.number().nullish(), + erctUswtrPrcsMeth: z.string().nullish(), + erctUswtrPrcsMsrcCd: z.string().nullish(), + wwtEmsnFcltPrmsnYn: z.enum(["Y", "N"]).default("Y"), + tmsInstlYn: z.enum(["Y", "N"]).default("Y"), + pbnstDrngInflwExclYn: z.enum(["Y", "N"]).default("Y"), + bod: z.number().default(0), + grssClfg: z.number().default(0), + combResdch: z.number().default(0), + trbt: z.number().default(0), + clIonQnt: z.number().default(0), + smll: z.number().default(0), + crmty: z.number().default(0), + tph: z.number().default(0), + tn: z.number().default(0), + ph: z.number().default(0), + cdtnv: z.number().default(0), + designCst: z.number().nullish(), + cstrnSpvsCst: z.number().nullish(), + fcltSbsdCst: z.number().nullish(), + wstCst: z.number().nullish(), + cnstCmcnCst: z.number().nullish(), + mntrgEqpmntInstlCst: z.number().nullish(), + gisDbCst: z.number().nullish(), + elpwrMtrngCst: z.number().nullish(), + mchnCst: z.number().nullish(), + archCst: z.number().nullish(), + pplCst: z.number().nullish(), + ruseFcltCst: z.number().nullish(), + designCstClcln: z.number().nullish(), + cstrnSpvsCstClcln: z.number().nullish(), + fcltSbsdCstClcln: z.number().nullish(), + wstCstClcln: z.number().nullish(), + cnstCmcnCstClcln: z.number().nullish(), + mntrgEqpmntInstlCstClcln: z.number().default(0), + gisDbCstClcln: z.number().nullish(), + elpwrMtrngCstClcln: z.number().nullish(), + mchnCstClcln: z.number().nullish(), + archCstClcln: z.number().nullish(), + pplCstClcln: z.number().nullish(), + ruseFcltCstClcln: z.number().nullish(), + fcOrgwts: z.array(fcOrgwtSchema).nullish(), + pplInstls: z.array(pplInstlSchema).nullish(), + fcDemands: z.array(dmdColist).default([]).nullish(), + flg: z.string().nullish(), + regDt: z.string().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.string().nullish(), + mdfr: z.string().nullish(), +}); +export const pbnstRegZod = z.object({ + pbnstRuseFcltPk: z.string().default(''), + swtrmtFcltFk: z.string().default(""), + ruseUtztnFcltNm: z.string().default(""), + fcltCpct: z.number().default(0), + ruseCpct: z.number().default(0), + splyExtraCpct: z.number().default(0), + regDt: z.date().default(new Date()), + rgtr: z.string().default("test"), + mdfcnDt: z.date().default(new Date()), + mdfr: z.string().default("test"), + fcFiles: z.array(fcFileSchema).default([]), + fcBizs: z.array(fcBizSchema).default([]), + fcOpers: z.array(fcOperSchema).default([]), + hasuhasu: z.number().default(0), +}); + //관경 제외 -export const exampleData = { - pbnstRuseFcltPk: 1, - swtrmtFcltFk: "SWTRMT-001", - ruseUtztnFcltNm: "재이용시설 A", - fcltCpct: "100", - ruseCpct: "90", - splyExtraCpct: "10", - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - fcFiles: [], - fcBizs: [ +export const exampleData = [ + { + "swtrmtFcltFk": "11000SW001R", + "pbnstRuseFcltPk": "11000SW001R1", + "ruseUtztnFcltNm":'난지', + "fcltCpct": 123, + "ruseCpct": 123, + "splyExtraCpct": 123, + "hasuhasu": 123, + "fcFiles": [], + "fcBizs": [ { - pbnstRuseFcltBizPk: 10, - pbnstRuseFcltFk: 1, - cycl: "1", - seCd: "A010", - bizMthdCd: "M02", - instlAprvSncnDt: "2022-01-01", - bizCmcnDay: "2022-02-01", - gvsbs: "정부지원", - lcep: "지자체", - cusrAlotm: "고객", - etc: "비고 없음", - totalMoney: "100000000", - rprcsFcltCpct: "50", - majorPrcsMsrcCd: "MP01", - addr: "서울시 종로구", - rprcsMthdCd: "RP01", - inoutYn: "Y", - mainSplyPumpCpct: "30", - erctWtrOcrnCpct: "5", - erctUswtrLinkSpt: "있음", - erctUswtrFcltCpct: "20", - erctUswtrPrcsMeth: "생물학적", - erctUswtrPrcsMsrcCd: "EM01", - wwtEmsnFcltPrmsnYn: "Y", - tmsInstlYn: "N", - pbnstDrngInflwExclYn: "Y", - bod: "30", - grssClfg: "양호", - combResdch: "적정", - trbt: "10", - clIonQnt: "5", - smll: "없음", - crmty: "정상", - tph: "0.2", - tn: "2", - ph: "7.2", - cdtnv: "100", - designCst: "1000", - cstrnSpvsCst: "500", - fcltSbsdCst: "200", - wstCst: "300", - cnstCmcnCst: "1000", - mntrgEqpmntInstlCst: "150", - gisDbCst: "200", - elpwrMtrngCst: "180", - mchnCst: "700", - archCst: "400", - pplCst: "300", - ruseFcltCst: "5000", - designCstClcln: "990", - cstrnSpvsCstClcln: "480", - fcltSbsdCstClcln: "190", - wstCstClcln: "280", - cnstCmcnCstClcln: "980", - mntrgEqpmntInstlCstClcln: "140", - gisDbCstClcln: "190", - elpwrMtrngCstClcln: "170", - mchnCstClcln: "680", - archCstClcln: "380", - pplCstClcln: "290", - ruseFcltCstClcln: "4800", - fcOrgwts: [ + "pbnstRuseFcltFk": "11000SW001R1", + "cycl": 1, + "seCd": "123", + "bizMthdCd": "123", + "gvsbs": 123, + "lcep": 123, + "cusrAlotm": 123, + "etc": "123", + "rprcsFcltCpct": 123, + "majorPrcsMsrcCd": "123", + "addr": "123", + "rprcsMthdCd": "123", + "inoutYn": "123", + "mainSplyPumpCpct": 123, + "erctWtrOcrnCpct": 123, + "erctUswtrLinkSpt": "123", + "erctUswtrFcltCpct": 123, + "erctUswtrPrcsMeth": "123", + "erctUswtrPrcsMsrcCd": "123", + "bod": 123, + "grssClfg": 123, + "combResdch": 123, + "trbt": 123, + "smll": 1230, + "crmty": 123, + "tn": 123, + "tph": 1230, + "ph": 123, + "clIonQnt": 123, + "cdtnv": 123, + "pplInstls": [ { - pbnstRuseFcltBizFk: 10, - swtrmtFcltFk: 1, - orgwtWtritQnt: "100", - tmsYn: "Y", - dschgFmYn: "N", - dtlPstnExpln: "배출 위치", - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - mdfr: "admin", - }, - { - pbnstRuseFcltBizFk: 10, - swtrmtFcltFk: 2, - orgwtWtritQnt: "153", - tmsYn: "Y", - dschgFmYn: "N", - dtlPstnExpln: "배출 위치22", - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - mdfr: "admin", - }, - ], - flg: "N", - regDt: "2022-01-01", - rgtr: "admin", - mdfcnDt: "2022-02-01", - mdfr: "admin", + "ruseFcltBizFk": "", + "choose": false, + "ppMqlt": "PE관", + "pdmt": "D250", + "cpct": "123", + "id": "e1472f7c-7033-44ae-a953-f69e6b90fbc1" + } + ] + + }, { - pbnstRuseFcltBizPk: 11, - pbnstRuseFcltFk: 1, - cycl: "2", - seCd: "A011", - bizMthdCd: "M02", - instlAprvSncnDt: "2022-01-01", - bizCmcnDay: "2022-02-01", - gvsbs: "민간", - lcep: "지자체", - cusrAlotm: "고객", - etc: "비고 없음", - totalMoney: "100000000", - rprcsFcltCpct: "50", - majorPrcsMsrcCd: "MP01", - addr: "부산시 해운대구", - rprcsMthdCd: "RP01", - inoutYn: "Y", - mainSplyPumpCpct: "30", - erctWtrOcrnCpct: "5", - erctUswtrLinkSpt: "있음", - erctUswtrFcltCpct: "20", - erctUswtrPrcsMeth: "생물학적", - erctUswtrPrcsMsrcCd: "EM01", - wwtEmsnFcltPrmsnYn: "Y", - tmsInstlYn: "N", - pbnstDrngInflwExclYn: "Y", - bod: "30", - grssClfg: "양호", - combResdch: "적정", - trbt: "10", - clIonQnt: "5", - smll: "없음", - crmty: "정상", - tph: "0.2", - tn: "2", - ph: "7.2", - cdtnv: "100", - designCst: "1000", - cstrnSpvsCst: "500", - fcltSbsdCst: "200", - wstCst: "300", - cnstCmcnCst: "1000", - mntrgEqpmntInstlCst: "150", - gisDbCst: "200", - elpwrMtrngCst: "180", - mchnCst: "700", - archCst: "400", - pplCst: "300", - ruseFcltCst: "5000", - designCstClcln: "990", - cstrnSpvsCstClcln: "480", - fcltSbsdCstClcln: "190", - wstCstClcln: "280", - cnstCmcnCstClcln: "980", - mntrgEqpmntInstlCstClcln: "140", - gisDbCstClcln: "190", - elpwrMtrngCstClcln: "170", - mchnCstClcln: "680", - archCstClcln: "380", - pplCstClcln: "290", - ruseFcltCstClcln: "4800", - fcOrgwts: [ + "pbnstRuseFcltFk": "11000SW001R1", + "cycl": 2, + "seCd": "하하", + "bizMthdCd": "호호", + "gvsbs": 12, + "lcep": 12, + "cusrAlotm": 12, + "etc": "1", + "rprcsFcltCpct": 12, + "majorPrcsMsrcCd": "12", + "addr": "12", + "rprcsMthdCd": "12", + "inoutYn": "12", + "mainSplyPumpCpct": 12, + "erctWtrOcrnCpct": 13, + "erctUswtrLinkSpt": "12", + "erctUswtrFcltCpct": 13, + "erctUswtrPrcsMeth": "12", + "erctUswtrPrcsMsrcCd": "12", + "bod": 1, + "grssClfg": 1, + "combResdch": 1, + "trbt": 1, + "smll": 1, + "crmty": 1, + "tn": 1, + "tph": 1, + "ph": 1, + "clIonQnt": 1, + "cdtnv": 1, + "pplInstls": [ { - pbnstRuseFcltBizFk: 11, - swtrmtFcltFk: 1, - orgwtWtritQnt: "152", - tmsYn: "Y", - dschgFmYn: "N", - dtlPstnExpln: "배출 위치1", - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - mdfr: "admin", - }, - { - pbnstRuseFcltBizFk: 11, - swtrmtFcltFk: 1, - orgwtWtritQnt: "150", - tmsYn: "Y", - dschgFmYn: "N", - dtlPstnExpln: "배출 위치", - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - mdfr: "admin", - }, - ], - flg: "N", - regDt: "2022-01-01", - rgtr: "manager", - mdfcnDt: "2022-02-01", - mdfr: "manager", - }, + "ruseFcltBizFk": "", + "choose": false, + "ppMqlt": "PE관", + "pdmt": "D300", + "cpct": "1", + "id": "e1472f7c-7033-44ae-a953-f69e6b90fbc1" + } + ] + } ], - fcOpers: [ + "fcOpers": [ { - pbnstRuseFcltOperInfoPk: 100, - pbnstRuseFcltFk: 1, - operMthd: "위탁", - rprtvBzenty: "기관명", - bgngDt: new Date("2025-05-21T11:03:26.434810"), - endDt: new Date("2025-05-21T11:03:26.434810"), - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - mdfr: "admin", - }, + "choose": false, + "rgtr": "", + "mdfr": "", + "operMthd": "1", + "rprtvBzenty": "1", + "bgngDt": "20250513", + "endDt": "20250529", + "id": "cbef8f84-33df-406b-a85f-edd2a5d3618e" + } + ] +}, +{ + "swtrmtFcltFk": "11000SW001R", + "ruseUtztnFcltNm":'난지', + "pbnstRuseFcltPk": "11000SW001R2", + "fcltCpct": 123, + "ruseCpct": 123, + "splyExtraCpct": 123, + "hasuhasu": 123, + "fcFiles": [], + "fcBizs": [ { - pbnstRuseFcltOperInfoPk: 101, - pbnstRuseFcltFk: 1, - operMthd: "직영", - rprtvBzenty: "기관명", - bgngDt: new Date("2025-05-21T11:03:26.434810"), - endDt: new Date("2025-05-21T11:03:26.434810"), - regDt: new Date("2025-05-21T11:03:26.434810"), - rgtr: "admin", - mdfcnDt: new Date("2025-05-21T11:03:26.434810"), - mdfr: "admin", - }, + "pbnstRuseFcltFk": "11000SW001R2", + "cycl": 1, + "seCd": "123", + "bizMthdCd": "123", + "gvsbs": 123, + "lcep": 123, + "cusrAlotm": 123, + "etc": "123", + "rprcsFcltCpct": 123, + "majorPrcsMsrcCd": "123", + "addr": "123", + "rprcsMthdCd": "123", + "inoutYn": "123", + "mainSplyPumpCpct": 123, + "erctWtrOcrnCpct": 123, + "erctUswtrLinkSpt": "123", + "erctUswtrFcltCpct": 123, + "erctUswtrPrcsMeth": "123", + "erctUswtrPrcsMsrcCd": "123", + "bod": 123, + "grssClfg": 123, + "combResdch": 123, + "trbt": 123, + "smll": 1230, + "crmty": 123, + "tn": 123, + "tph": 1230, + "ph": 123, + "clIonQnt": 123, + "cdtnv": 123, + "pplInstls": [ + { + "ruseFcltBizFk": "", + "choose": false, + "ppMqlt": "PE관", + "pdmt": "D250", + "cpct": "123", + "id": "e1472f7c-7033-44ae-a953-f69e6b90fbc1" + } + ] + } ], -}; + "fcOpers": [ + { + "choose": false, + "rgtr": "", + "mdfr": "", + "operMthd": "123", + "rprtvBzenty": "123", + "bgngDt": "20250513", + "endDt": "20250529", + "id": "cbef8f84-33df-406b-a85f-edd2a5d3618e" + } + ] +} +] + + + +// [{ +// pbnstRuseFcltPk: 1, +// swtrmtFcltFk: "11000SW001R", +// ruseUtztnFcltNm: "재이용시설 A", +// hasuhasu: 0, +// fcltCpct: "100", +// ruseCpct: "90", +// splyExtraCpct: "10", +// rgtr: TEST_UUID, +// mdfr: TEST_UUID, +// fcFiles: [], +// fcBizs: [ +// { +// pbnstRuseFcltBizPk: 1, +// pbnstRuseFcltFk: 1, +// cycl: "1", +// seCd: "A010", +// bizMthdCd: "M02", +// instlAprvSncnDt: "2022-01-01", +// bizCmcnDay: "2022-02-01", +// gvsbs: "정부지원", +// lcep: "지자체", +// cusrAlotm: "고객", +// etc: "비고 없음", +// totalMoney: "100000000", +// rprcsFcltCpct: "50", +// majorPrcsMsrcCd: "MP01", +// addr: "서울시 종로구", +// rprcsMthdCd: "RP01", +// inoutYn: "Y", +// mainSplyPumpCpct: "30", +// erctWtrOcrnCpct: "5", +// erctUswtrLinkSpt: "있음", +// erctUswtrFcltCpct: "20", +// erctUswtrPrcsMeth: "생물학적", +// erctUswtrPrcsMsrcCd: "EM01", +// wwtEmsnFcltPrmsnYn: "Y", +// tmsInstlYn: "N", +// pbnstDrngInflwExclYn: "Y", +// bod: "30", +// grssClfg: "양호", +// combResdch: "적정", +// trbt: "10", +// clIonQnt: "5", +// smll: "없음", +// crmty: "정상", +// tph: "0.2", +// tn: "2", +// ph: "7.2", +// cdtnv: "100", +// designCst: "1000", +// cstrnSpvsCst: "500", +// fcltSbsdCst: "200", +// wstCst: "300", +// cnstCmcnCst: "1000", +// mntrgEqpmntInstlCst: "150", +// gisDbCst: "200", +// elpwrMtrngCst: "180", +// mchnCst: "700", +// archCst: "400", +// pplCst: "300", +// ruseFcltCst: "5000", +// designCstClcln: "990", +// cstrnSpvsCstClcln: "480", +// fcltSbsdCstClcln: "190", +// wstCstClcln: "280", +// cnstCmcnCstClcln: "980", +// mntrgEqpmntInstlCstClcln: "140", +// gisDbCstClcln: "190", +// elpwrMtrngCstClcln: "170", +// mchnCstClcln: "680", +// archCstClcln: "380", +// pplCstClcln: "290", +// ruseFcltCstClcln: "4800", +// pplInstls: [ +// { +// ruseFcltBizFk: "10", +// pdmt: "D200", +// ppMqlt: "도복장강관", +// cpct: "1", +// }, +// { +// ruseFcltBizFk: "10", +// pdmt: "D100", +// ppMqlt: "PE관", +// cpct: "2", +// }, +// ], +// fcOrgwts: [ +// { +// pbnstRuseFcltBizFk: 1, +// swtrmtFcltFk: "11000SW001R", +// orgwtWtritQnt: "100", +// tmsYn: "Y", +// dschgFmYn: "N", +// dtlPstnExpln: "배출 위치", +// regDt: "20250221", +// rgtr: "admin", +// mdfcnDt: "20250321", +// mdfr: "admin", +// }, +// { +// pbnstRuseFcltBizFk: 1, +// swtrmtFcltFk: "11200PB001R", +// orgwtWtritQnt: "153", +// tmsYn: "Y", +// dschgFmYn: "N", +// dtlPstnExpln: "배출 위치22", +// regDt: "20250221", +// rgtr: "admin", +// mdfcnDt: "20250421", +// mdfr: "admin", +// }, +// ], +// flg: "N", +// rgtr: TEST_UUID, +// mdfr: TEST_UUID, +// }, +// { +// pbnstRuseFcltBizPk: 11, +// pbnstRuseFcltFk: 1, +// cycl: "2", +// seCd: "A011", +// bizMthdCd: "M02", +// instlAprvSncnDt: "2022-01-01", +// bizCmcnDay: "2022-02-01", +// gvsbs: "민간", +// lcep: "지자체", +// cusrAlotm: "고객", +// etc: "비고 없음", +// totalMoney: "100000000", +// rprcsFcltCpct: "50", +// majorPrcsMsrcCd: "MP01", +// addr: "부산시 해운대구", +// rprcsMthdCd: "RP01", +// inoutYn: "Y", +// mainSplyPumpCpct: "30", +// erctWtrOcrnCpct: "5", +// erctUswtrLinkSpt: "있음", +// erctUswtrFcltCpct: "20", +// erctUswtrPrcsMeth: "생물학적", +// erctUswtrPrcsMsrcCd: "EM01", +// wwtEmsnFcltPrmsnYn: "Y", +// tmsInstlYn: "N", +// pbnstDrngInflwExclYn: "Y", +// bod: "30", +// grssClfg: "양호", +// combResdch: "적정", +// trbt: "10", +// clIonQnt: "5", +// smll: "없음", +// crmty: "정상", +// tph: "0.2", +// tn: "2", +// ph: "7.2", +// cdtnv: "100", +// designCst: "1000", +// cstrnSpvsCst: "500", +// fcltSbsdCst: "200", +// wstCst: "300", +// cnstCmcnCst: "1000", +// mntrgEqpmntInstlCst: "150", +// gisDbCst: "200", +// elpwrMtrngCst: "180", +// mchnCst: "700", +// archCst: "400", +// pplCst: "300", +// ruseFcltCst: "5000", +// designCstClcln: "990", +// cstrnSpvsCstClcln: "480", +// fcltSbsdCstClcln: "190", +// wstCstClcln: "280", +// cnstCmcnCstClcln: "980", +// mntrgEqpmntInstlCstClcln: "140", +// gisDbCstClcln: "190", +// elpwrMtrngCstClcln: "170", +// mchnCstClcln: "680", +// archCstClcln: "380", +// pplCstClcln: "290", +// ruseFcltCstClcln: "4800", +// pplInstls: [ +// { +// ruseFcltBizFk: "11", +// pdmt: "관경", +// ppMqlt: "1", +// cpct: "100", +// }, +// { +// ruseFcltBizFk: "11", +// pdmt: "관경", +// ppMqlt: "2", +// cpct: "200", +// }, +// ], +// fcOrgwts: [ +// { +// pbnstRuseFcltBizFk: 11, +// swtrmtFcltFk: "11000SW001R", +// orgwtWtritQnt: "152", +// tmsYn: "Y", +// dschgFmYn: "N", +// dtlPstnExpln: "배출 위치1", +// rgtr: TEST_UUID, +// mdfr: TEST_UUID, +// }, +// { +// pbnstRuseFcltBizFk: 11, +// swtrmtFcltFk: "11200PB001R", +// orgwtWtritQnt: "150", +// tmsYn: "Y", +// dschgFmYn: "N", +// dtlPstnExpln: "배출 위치", +// rgtr: TEST_UUID, +// mdfr: TEST_UUID, +// }, +// ], +// flg: "N", +// rgtr: TEST_UUID, +// mdfr: TEST_UUID, +// }, +// ], +// fcOpers: [ +// { +// pbnstRuseFcltOperInfoPk: 100, +// pbnstRuseFcltFk: 1, +// operMthd: "위탁", +// rprtvBzenty: "기관명", +// bgngDt: new Date("2023-09-30"), +// endDt: new Date("2023-12-31"), +// rgtr: TEST_UUID, +// mdfr: TEST_UUID, +// }, +// { +// pbnstRuseFcltOperInfoPk: 101, +// pbnstRuseFcltFk: 1, +// operMthd: "직영", +// rprtvBzenty: "기관명", +// bgngDt: new Date("2024-11-30"), +// endDt: new Date("2024-12-31"), +// rgtr:TEST_UUID , +// mdfr: TEST_UUID, +// }, +// ], +// } + + + + + +// ] diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index 4f6bd576c..e7e0a3653 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -1,6 +1,7 @@ import { getFetch, postFectch, putFectch } from "@/components/lib/api"; import { z } from "zod"; import { makeMdfr, makeRstr } from "../bdstReg"; +import type { SelectDataType } from "@common/business_components/ui"; export const TEST_UUID= '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e' export const rainZod = z.object({ @@ -90,6 +91,7 @@ export type TableUpperProps = { visible?:boolean; rowSpan?: true; type?: string; + selectData ?:string; }; export const upHeadList: TableUpperProps[] = [ diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/excel.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/excel.tsx new file mode 100644 index 000000000..a85a009a4 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/excel.tsx @@ -0,0 +1,28 @@ +import type { customPbnst } from "../../fcltReg/pbnstReg/type"; + +export const excelPbnstSttnParsing = (data: any[]): string[][] => { + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + item.a, + String(item.aaa), + item.aaaa ?? "", + item.operMngCd ?? "", + String(item.cityText ?? ""), + String(item.sggText ?? ""), + String(item.bdnstNm ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + String(item.archArea ?? ""), + ]) + }) + return excelExportData; +} \ No newline at end of file diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx index 09b5a0cad..cb6b8e392 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx @@ -1,13 +1,14 @@ import type { CommonTableProps } from "@/app/store/common/type/headder"; -import type { customPbnst } from "../../fcltReg/pbnstReg"; import type { z } from "zod"; import { pbnSttHeadListExport, pbnstSttNodeListExport - } from "./headList"; +} from "./headList"; import { fetchType, getFetch } from "@/components/lib/api"; import { controlHeadView } from "../rainSttn"; import type { sttnHeadListType } from "../rainSttn/headList"; +import type { customPbnst } from "../../fcltReg/pbnstReg/type"; +import { excelPbnstSttnParsing } from "./excel"; const pbnstSttHeadList = pbnSttHeadListExport; const pbnstSttNodeList = pbnstSttNodeListExport; @@ -26,6 +27,7 @@ type pbnstSttnActions = { pbnstChSttnHead: ( headList: z.infer<typeof sttnHeadListType>, ) => void; + pbnstSttnSearch: (page: number, total: string, con: string) => void; }; export type pbnstSttnType = pbnstSttnState & pbnstSttnActions; @@ -34,8 +36,8 @@ export const pbnstSttnStateExport: pbnstSttnState = { pbnstSttn: { pbnstSttHeadList: pbnstSttHeadList, pbnstSttNodeList: pbnstSttNodeList, - pbnstSttnRes:[], - excelList:[], + pbnstSttnRes: [], + excelList: [], isInit: false, }, }; @@ -45,19 +47,52 @@ export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = ( ) => { return { pbnstChSttnHead: async ( - headList: z.infer<typeof sttnHeadListType> - ): Promise<void> => { - - console.log('head ', headList) - const changeNodeList= controlHeadView(headList,pbnstSttNodeList) - - return set((state: pbnstSttnState) => ({ - pbnstSttn: { - ...state.pbnstSttn, - pbnstSttHeadList: headList, - pbnstSttNodeList: changeNodeList, - }, - })); + headList: z.infer<typeof sttnHeadListType> + ): Promise<void> => { + + console.log('head ', headList) + const changeNodeList = controlHeadView(headList, pbnstSttNodeList) + + return set((state: pbnstSttnState) => ({ + pbnstSttn: { + ...state.pbnstSttn, + pbnstSttHeadList: headList, + pbnstSttNodeList: changeNodeList, }, + })); + }, + pbnstSttnSearch: async (page: number, total: string, con: string) => { + try { + const pbnstSttnRes = await getFetch( + fetchType.WR, + `/reuseFc/waterreuseFc/${page}/${total}/${con}` + ); + if (Number(total) > 9000) { + const pbnstExcel = excelPbnstSttnParsing(pbnstSttnRes); + set((state: pbnstSttnState) => { + return { + ...state, + pbnstSttn: { + ...state.pbnstSttn, + excelList: pbnstExcel, + }, + }; + }); + } else + set((state: pbnstSttnState) => { + + return { + ...state, + pbnstSttn: { + ...state.pbnstSttn, + pbnstSttnRes: pbnstSttnRes, + isInit: false, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, } } diff --git a/apps/wr/app/store/store.tsx b/apps/wr/app/store/store.tsx index 5f7172c60..297983882 100644 --- a/apps/wr/app/store/store.tsx +++ b/apps/wr/app/store/store.tsx @@ -87,6 +87,10 @@ import { rswtChkStateExport, rswtChkSttnActionsExport, type rswtChkSttnState, ty import { pbnstSttnActionsExport, pbnstSttnStateExport, type pbnstSttnState, type pbnstSttnType } from "./ruseFcltMng/fcltSttn/pbnstSttn"; import { coSttnActionsExport, coSttnStateExport, type coSttnState, type coSttnType } from "./matDmd/coSttn"; // 옮겨야함 import { ldrmntCoSttnActionsExport, ldrmntCoSttnStateExport, type ldrmntCoSttnState, type ldrmntCoSttnType } from "./matDmd/ldrmntCoSttn"; // 옮겨야함 +import { searchActions, type comFcltActions, type comFcltState } from "./common/searchCode"; +import { pbnstPrfmncActionsExport, pbnstPrfmncStateExport, type pbnstPrfmncState, type pbnstPrfmncType } from "./prfmncReg/pbnstPrfmncReg"; +import { coRvwActionsExport, coRvwStateExport, type coRvwState, type coRvwType } from "./matDmd/coMatRvw"; +import { ruseHstryActionsExport, ruseHstryStateExport, type ruseHstryState, type ruseHstryType } from "./matDmd/ruseRvwHstry"; export type WrStore = comCodeType & pubFacType & @@ -103,8 +107,12 @@ export type WrStore = comCodeType & rainChkSttnType& rswtChkSttnType& pbnstSttnType& + comFcltActions&comFcltState& coSttnType& // 옮겨야함 - ldrmntCoSttnType // 옮겨야함 + ldrmntCoSttnType& // 옮겨야함 + pbnstPrfmncType& + coRvwType& + ruseHstryType ; export const StoreZus = ( @@ -124,8 +132,10 @@ export const StoreZus = ( rswtChkSttnState & pbnstSttnState& coSttnState& // 옮겨야함 - ldrmntCoSttnState // 옮겨야함 - + ldrmntCoSttnState& // 옮겨야함 + pbnstPrfmncState& + coRvwState& + ruseHstryState = { ...comCodeStateExport, ...pubFacStateExport, @@ -144,6 +154,9 @@ export const StoreZus = ( ...pbnstSttnStateExport, ...coSttnStateExport, // 옮겨야함 ...ldrmntCoSttnStateExport, // 옮겨야함 + ...pbnstPrfmncStateExport, + ...coRvwStateExport, + ...ruseHstryStateExport, } ) => { return createStore<WrStore>()((set) => { @@ -155,6 +168,7 @@ export const StoreZus = ( getEnvPartCd(set); return { ...initState, + ...searchActions(set), ...pubFacActionsExport(set), ...rainFacActionsExport(set), ...rswtFacActionsExport(set), @@ -172,6 +186,9 @@ export const StoreZus = ( ...pbnstSttnActionsExport(set), ...coSttnActionsExport(set), // 옮겨야함 ...ldrmntCoSttnActionsExport(set), // 옮겨야함 + ...pbnstPrfmncActionsExport(set), + ...coRvwActionsExport(set), + ...ruseHstryActionsExport(set), }; }); }; diff --git a/apps/wr/components/lib/api/index.ts b/apps/wr/components/lib/api/index.ts index 0ffa0d53c..ad0ab4729 100644 --- a/apps/wr/components/lib/api/index.ts +++ b/apps/wr/components/lib/api/index.ts @@ -1,67 +1,102 @@ //8880 -import { DEV_URL_FO, DEV_URL_WR } from "../config/"; +import { CS_URL, DEV_URL_FO, DEV_URL_WR, LOCAL_URL } from "../config/"; -export const fetchType={COMMON: "common", WR: "wr"} +export const fetchType = { COMMON: "common", WR: "wr", LOCAL: "LOCAL" , CS: "CS" }; -export const getFetch = async( type:string , url: string) => { - const div = type === fetchType.COMMON ? DEV_URL_FO : DEV_URL_WR - const response = await fetch( div+url , { +export const getFetch = async (type: string, url: string) => { + const div = + type === fetchType.COMMON + ? DEV_URL_FO + : type === fetchType.LOCAL + ? LOCAL_URL + : type === fetchType.CS + ? '' + : DEV_URL_WR; + + const response = await fetch(div + url, { method: "GET", headers: { - accept: "application/json", + accept: "application/json", }, }); - if(!response.ok) { + if (!response.ok) { + console.log(`오류 발생: ${response.status}`); + } + const contentType = response.headers.get("content-type") ?? ""; + + if (contentType.includes("application/json")) { + // JSON 응답 처리 + const res = await response.json(); + return res.data; + } else { + // 파일 응답 처리 (ex. PDF, Excel 등) + const blob = await response.blob(); + const blobUrl = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + const filename = "downloaded_file.PDF"; + + a.href = blobUrl; + a.download = filename; + a.click(); + window.URL.revokeObjectURL(blobUrl); + + return null; // 다운로드이므로 반환값 없음 + } +}; + +export const postFectch = async (url: string, params: any, type?: string) => { + const div = type === fetchType.COMMON ? DEV_URL_FO : DEV_URL_WR; + + const response = await fetch(div + url, { + method: "POST", + headers: { + accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }); + if (!response.ok) { throw new Error(`오류 발생: ${response.status}`); } - const res = await response.json() - return res.data; -} + return await response.json(); +}; -export const postFectch = async (url: string, params: any) => { - - const response = await fetch(DEV_URL_WR+url, { - method: "POST", +export const putFectch = async (url: string, params: any) => { + const response = await fetch(DEV_URL_WR + url, { + method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify(params), }); - if(!response.ok) { + if (!response.ok) { throw new Error(`오류 발생: ${response.status}`); } return response; -} +}; +export const deleteFectch = async (url: string, params?: any, type?: string) => { + const div = + type === fetchType.COMMON + ? DEV_URL_FO + : type === fetchType.LOCAL + ? LOCAL_URL + : type === fetchType.CS + ? '' + : DEV_URL_WR; -export const putFectch = async (url: string, params: any) => { - - const response = await fetch(DEV_URL_WR+url, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(params), - }); - if(!response.ok) { - throw new Error(`오류 발생: ${response.status}`); - } - return response; - } - - -export const deleteFectch = async (url: string, params: any) => { - const response = await fetch(DEV_URL_WR+url, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(params), - }); - if(!response.ok) { - throw new Error(`오류 발생: ${response.status}`); - } - return response; - } \ No newline at end of file + console.log("deleteFectch url", div + url); + const response = await fetch(div + url, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: params?JSON.stringify(params):undefined, + }); + if (!response.ok) { + throw new Error(`오류 발생: ${response.status}`); + } + return await response.text(); +}; \ No newline at end of file diff --git a/apps/wr/components/lib/config/index.ts b/apps/wr/components/lib/config/index.ts index 0ed7b82dd..d0e076d81 100644 --- a/apps/wr/components/lib/config/index.ts +++ b/apps/wr/components/lib/config/index.ts @@ -1,16 +1,17 @@ -const LOCAL_PORT = "3000"; +const LOCAL_PORT = "3001"; const DEV_PORT = "11080"; const DEV_PORT_NEW="11082" export const LOCAL_URL_WR = `http://localhost:${LOCAL_PORT}/api/v1`; export const DEV_URL_WR = `http://con.mind-one.co.kr:${DEV_PORT}/api/v1`; +// export const DEV_URL_WR = `http://192.168.0.91:8880/api/v1`; -//export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`; +// export const DEV_URL_FO = `http://192.168.0.91:8882/api/v1`; export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_NEW}/api/v1`; - +export const LOCAL_URL = `http://localhost:${LOCAL_PORT}`; export const CS_URL= 'http://dev.onasys.co.kr:8000' export const CS_TUS_UPLOAD= "/attach-service/api/v1/tus/upload"; \ No newline at end of file diff --git a/apps/wr/components/lib/useTus_wr/useTusWr.tsx b/apps/wr/components/lib/useTus_wr/useTusWr.tsx index 4aedb71d3..b9441e6a4 100644 --- a/apps/wr/components/lib/useTus_wr/useTusWr.tsx +++ b/apps/wr/components/lib/useTus_wr/useTusWr.tsx @@ -1,53 +1,78 @@ -import { Upload, type HttpRequest, type HttpResponse, type OnSuccessPayload } from "tus-js-client"; +import { + Upload, + type HttpRequest, + type HttpResponse, + type OnSuccessPayload, + type DetailedError, +} from "tus-js-client"; import { CS_TUS_UPLOAD, CS_URL } from "../config"; +import type { FilesType } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; type UploadOptions = { handleUploadStart?: (upload: Upload) => void; handleProgress?: (percentage: string) => void; - handleSuccess?: (payload :OnSuccessPayload) => void; - handleResponse?: (req:HttpRequest, res: HttpResponse) => void; + handleSuccess?: (payload: OnSuccessPayload) => void; + handleResponse?: (req: HttpRequest, res: ReturnFileType) => void; }; +export type ReturnFileType = { + orgnlFileNm: string; + atflFlieSz: number; + atflType: string; + dwnldNocs: number; + delYn: string; + lnkgDmnId: string | null; + lnkgDmnNm: string | null; + regDt: Date | null; + atflCd: String; + atflSn: number; + atflId: String; + physFileNm: String; +}; +export const TusClientUpload = (oFile: FilesType, options?: UploadOptions) => + new Promise<ReturnFileType>((resolve, reject) => { + try { + const Authorization = "Authorization"; + const AUTH_USER_ID = "token-id"; + let fileInfo: string | undefined; + //CS_URL + const uploader = new Upload(oFile.file, { + endpoint: CS_TUS_UPLOAD, + chunkSize: 10 * 1024 * 1024, + headers: { + Authorization: "", + AUTH_USER_ID: "", + }, + metadata: { + filename: oFile.file.name, + filetype: oFile.file.type, + }, + onError(error) { + reject(error); + }, + onProgress(bytesUploaded, bytesTotal) { + const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2); + options?.handleProgress?.(percentage); + }, + onAfterResponse(req, res: HttpResponse) { + fileInfo = res.getHeader("X-File-Info"); + if (fileInfo === undefined || fileInfo === null) return; + }, + onSuccess(payload: OnSuccessPayload) { + options?.handleSuccess?.(payload); + if (fileInfo) { + console.log('fileInfo', fileInfo); + resolve(JSON.parse(fileInfo) as ReturnFileType); + } else reject({} as DetailedError); + }, + }); -export function TusClientUpload(oFile: File, setUpload: (upload: Upload) => void, options?: UploadOptions) { - const Authorization = "Authorization"; - const AUTH_USER_ID = "token-id"; - - //CS_URL - const uploader = new Upload(oFile, { - endpoint: 'http://localhost:3001' + CS_TUS_UPLOAD, - chunkSize: 10* 1024 * 1024, - headers: { - Authorization: "", - AUTH_USER_ID: "", - }, - metadata: { - filename: oFile.name, - filetype: oFile.type, - }, - onError(error) { - console.error(`Failed because: ${error}`); - }, - onProgress(bytesUploaded, bytesTotal) { - const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2); - options?.handleProgress?.(percentage); - }, - onAfterResponse(req, res) { - const fileInfo = res.getHeader("X-File-Info"); - if (fileInfo === undefined || fileInfo === null) return; - - options?.handleResponse?.(res); - }, - onSuccess(payload :OnSuccessPayload) { - options?.handleSuccess?.(payload); - }, + options?.handleUploadStart?.(uploader); + uploader.start(); + } catch (e) { + console.log(e); + reject(e as DetailedError); + } + // setUpload(uploader); }); - uploader.start(); - - options?.handleUploadStart?.(uploader); - - console.log("upload start >>>>>", uploader); - - - // setUpload(uploader); -} +// // diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index 638f45915..5ab5edf98 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -15,7 +15,8 @@ import { SelectBox, type SelectDataType, } from "@common/business_components"; -import { Button, Input } from "@common/components"; +import { Button, Checkbox, Input } from "@common/components"; +import { Check } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { useFieldArray } from "react-hook-form"; @@ -42,18 +43,15 @@ export default function BdstReg({ form }: { form: any }) { // remove(fields.length - 1); // } // }; -// 이건 삭제해도되는거 const removeSelected = () => { - // fields 배열에서 selectedIds 에 포함된 field.id 의 인덱스만 골라내기 const idxsToRemove = fields - .map((f, i) => selectedIds.includes(f.id) ? i : -1) - .filter(i => i > -1) - // 내림차순 정렬: 인덱스 뒤쪽부터 제거해야 안전합니다. - .sort((a,b) => b - a); + .map((item, idx) => selectedIds.includes(item.id) ? idx : -1) + .filter(index => index > -1) + .sort((prevIndex, nextIndex) => nextIndex - prevIndex); - remove(idxsToRemove); - setSelectedIds([]); -}; + remove(idxsToRemove); + setSelectedIds([]); + }; const [selectSgg, setSelectSgg] = useState<SelectDataType[] | undefined>(); @@ -89,9 +87,6 @@ export default function BdstReg({ form }: { form: any }) { return () => subscription?.unsubscribe(); }, [form.form]); - - - // 여기서부터는 자유롭게 삭제 가능 const [selectedIds, setSelectedIds] = useState<string[]>([]); const toggleSelect = (id: string) => { @@ -280,19 +275,13 @@ export default function BdstReg({ form }: { form: any }) { </Button> </div> {fields.map((field, index) => ( - //여기서부터 자유롭게 삭제 가능 <div key={field.id} className="grid grid-cols-[auto,1fr,1fr,1fr] gap-2 mt-4 items-center"> - {/* 1) 체크박스 */} <div className="col-span-1 flex justify-center mr-4"> - <input - type="checkbox" + <Checkbox checked={selectedIds.includes(field.id)} - onChange={() => toggleSelect(field.id)} - className="h-6 w-6" + onCheckedChange={() => toggleSelect(field.id)} /> </div> - - {/* 2) 구분 */} <div className=""> <FormField control={form.control} @@ -308,8 +297,6 @@ export default function BdstReg({ form }: { form: any }) { )} /> </div> - - {/* 3) 허가일 */} <div className=""> <FormField control={form.control} @@ -328,8 +315,6 @@ export default function BdstReg({ form }: { form: any }) { )} /> </div> - - {/* 4) 면적 */} <div className=""> <FormField control={form.control} 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 912d9dc77..04e17867c 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -1,135 +1,676 @@ "use client"; -import { SelectBox } from "@common/business_components"; -import { - FormUi, -} from "@common/business_components/ui"; -import { Form, FormField } from "@common/business_components/ui/form"; -import { Button, Input, TableHead, TableRow } from "@common/components"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { use, useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; import { useWrStore } from "@/app/store"; -import { - selectClass, - type TableUpperProps, -} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import { - commonButonSize, - formH1Class, - formTableName, -} from "@/components/lib/class/comClass"; -import MngRegFormDetail from "../mngReg/mngRegFormDetail/mngRegFormDetail"; import { businessMoneyInfo, businessSttnInfo, concentratedWaterInfo, ruseFcltinfo, + ruseWaterQulityList, wasteWaterFcltINfo, } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/headList"; -import { pbnstRegZod } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +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"; +import { + commonButonSize, + formH1Class, + formTableName, +} from "@/components/lib/class/comClass"; +import FcOrgwtsRegModal from "@/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal"; +import RuseMngTable from "@/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable"; +import { SelectBox } from "@common/business_components"; +import { + FormUi, + SearchInputSelect, + UiModal, + type SelectDataType, +} from "@common/business_components/ui"; +import { Form, FormField } from "@common/business_components/ui/form"; +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 { 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, + firstBizData, +}: { + formType: string; + firstBizData?: fcBizType[]; +}) { + const ref = useRef<ppInstlType[] | null>(null); + const { + code, + pbnst, + demandCompList, + pbnstBizData, + getpbnstInsertIndex, + appendRuseFclt, + getBizCycle, + getDemandCompList, + regDemandCompany, + } = useWrStore((state) => state); + const { pbnstBizCycle, pbnstRes } = pbnst; + const [bizCycle, setBizCycle] = useState("1"); + const [ppiSelectList, setPpiSelectList] = + useState<[{ name: string; val: SelectDataType[] }]>(); + const [ppiYn, setPpiYn] = useState("Y"); + const [isFcOrgwts, setIsFcOrgwts] = useState<boolean>(false); + const [sumCpct, setSumCpct] = useState<number>(0); + const [isDmdModal, setIsDmdModal] = useState<{ + isMoal: boolean; + data: DemandCompType; + }>({ isMoal: false, data: {} as DemandCompType }); -export function PbnstBuseForm() { - const { code, pbnst } = useWrStore((state) => state); - const { pubMngUpHeadList,pbnstBizCycle } = pbnst; - const [getBizCycle, setBizCycle] = useState(''); - - - const form = useForm<z.infer<typeof pbnstRegZod>>({ - resolver: zodResolver(pbnstRegZod), - + const form = useForm<fcBizType>({ + resolver: zodResolver(fcBizSchema), + defaultValues: + formType === "reg" && + pbnst.pbnstMngRegData && + pbnst.pbnstMngRegData[0]?.fcBizs + ? (pbnst.pbnstMngRegData[0].fcBizs[0] ?? {}) + : formType === "update" && + typeof pbnst.pbnsUpdateIndex.biz === "number" && + pbnst.pbnsUpdateIndex.biz >= 0 && + pbnst.pbnstRes + ? pbnst.pbnstRes[pbnst.pbnsUpdateIndex.mng]?.fcBizs[ + pbnst.pbnsUpdateIndex.biz + ] + : fcBizSchema.parse({}), }); // 2. Define a submit handler. - function onSubmit(values: z.infer<typeof pbnstRegZod>) { - // Do something with the form values. - // ✅ This will be type-safe and validated. - console.log(values); + function onSubmit(values: fcBizType) { + // console.log(values); } - const onSelectValue = (val: string) => { - console.log(val); + + // 2. Define a submit handler. + function addRuseFcltObj() { + const newItem = fcBizSchema.parse({}); + const index = pbnst.pbnsUpdateIndex.mng; + + let regData: fcBizType = {} as fcBizType; + if ( + pbnst.pbnstRes && + pbnst.pbnstRes[pbnst.pbnsUpdateIndex.mng] && + pbnst.pbnstRes[pbnst.pbnsUpdateIndex.mng]?.fcBizs + ) { + regData = { + pbnstRuseFcltFk: + String(pbnst.pbnstRes[0]?.swtrmtFcltFk) + + String(pbnst.pbnstRes.length + 1), + cycl: Number( + (pbnst.pbnstRes[pbnst.pbnsUpdateIndex.mng]?.fcBizs?.length || 0) + 1 + ), + } as fcBizType; + + const newPbnstRes = [...(pbnst.pbnstRes as customPbnst[])]; + newPbnstRes[index]?.fcBizs?.length && + newPbnstRes[index]?.fcBizs?.push(regData); + getBizCycle(newPbnstRes[index] as customPbnst); + appendRuseFclt(newPbnstRes); + } + } + + const { + fields: fcDemands, + append: appendDemand, + remove: removeDemand, + } = useFieldArray({ + control: form.control, + name: "fcDemands", + }); + + const { fields: fcOrgwtFiled } = useFieldArray({ + control: form.control, + name: "fcOrgwts", + }); + + const { + fields: pplInstls, + append, + remove, + } = useFieldArray({ + control: form.control, + name: "pplInstls", + }); + + // 등록할때 신규 값을 계속 가지고 있으려고 + useEffect(() => { + const formData = form.watch(); + + formData.pplInstls = ref.current ? [...ref.current] : []; // ← form 내부 상태 기준으로 복사 + if (formType === "reg") { + if ( + firstBizData && + firstBizData[0] && + (form.getValues("pbnstRuseFcltFk") === "" || + form.getValues("pbnstRuseFcltFk") === undefined) + ) { + form.setValue( + "pbnstRuseFcltFk", + String(firstBizData[0].pbnstRuseFcltFk) + ); + form.setValue("cycl", firstBizData[0].cycl); + } + pbnstBizData(formData as fcBizType); + }else if(formType === "update"){ + + console.log('formdata ', formData) + pbnstBizData(formData as fcBizType); + + + } + }, [firstBizData, form.formState, formType, pbnst.pbnstRes]); + + + useEffect(() => { + if (ppiYn === "N") { + form.setValue("pplInstls", []); + } + }, [ppiYn]); + + useEffect(() => { + console.log("pbnst. bug pbnstMngRegData ", pbnst.pbnstMngRegData); + }, [pbnst.pbnstMngRegData]); + + const plusHist = () => { + const regData = { + ruseFcltBizFk: ref.current?.[0]?.ruseFcltBizFk ?? form.getValues('pbnstRuseFcltBizPk'), + choose: false, + ppMqlt: "", + pdmt: "", + cpct: 0, + }; + const currentTableDataFromRef = ref.current ? [...ref.current] : []; + const newDataForRHF = [...currentTableDataFromRef, regData]; + form.setValue("pplInstls", newDataForRHF, { + shouldValidate: true, // 필요하다면 유효성 검사 실행 + shouldDirty: true, // 폼 상태를 dirty로 변경 + }); + const latestOpers = form.getValues("pplInstls"); + ref.current = [...latestOpers]; + }; + + const plusArray = () => { + appendDemand({ + dmdCoNm: "", + daddr: "", + useUsgCd: "", + splyBgngDay: new Date(), + splyEndDay: new Date(), + splyCpct: 0, + splyFmYn: "Y", + pbnstRuseFcltBizPk: form.getValues('pbnstRuseFcltBizPk'), + pbnstRuseFcltFk: form.getValues('pbnstRuseFcltFk')??'', + //telno: "", + }); + }; + + const removeSelected = () => { + if (!ref.current) return; + const selectedIdxs = ref.current + .map((item, idx) => (item.choose ? idx : -1)) + .filter((idx) => idx !== -1); + selectedIdxs.reverse().forEach((idx) => remove(idx)); }; - const [instlAprvDate, setInstlAprvDate] = useState(new Date()); - const [bizCmcnDate, setBizCmcnDate] = useState(new Date()); - const [files, setFiles] = useState(null); useEffect(() => { - if (getBizCycle ) { - console.log("getBizCycle", getBizCycle); + if (bizCycle && formType !== "reg") { + const index = Number(bizCycle) - 1; + form.reset(pbnst.pbnstResObj?.fcBizs[index]); + ref.current = pbnst.pbnstResObj?.fcBizs[index]?.pplInstls ?? []; } - }, [getBizCycle]); + }, [bizCycle, pbnst.pbnstResObj, formType]); + + useEffect(() => { + const ppl = [] as { name: string; val: SelectDataType[] }[]; + if (code.pplLenght) { + ppl.push({ name: "pplLenght", val: code.pplLenght }); + } + if (code.pplLenght) { + ppl.push({ name: "pplQulity", val: code.pplQulity }); + } + if (ppl.length > 0) { + setPpiSelectList(ppl); + } else { + setPpiSelectList(undefined); + } + }, [code]); + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); return ( - <div className=" !mx-[1.6rem] !p-0r"> - {/* 차수별 등록 현황 폼 */} - <div className="justify-between"> - <Form {...form}> - <form - onSubmit={form.handleSubmit(onSubmit)} - className="space-y-8 mx-auto w-full mt-7" - > - <div className="flex justify-between items-center mx-3 mb-3"> - <h1 className=" text-xl"></h1> - <div className="flex items-center gap-2 mx-3"> - <h1 className={formTableName}>등록차수</h1> - - <SelectBox - label="" - selectArray={pbnstBizCycle} - value={pbnstBizCycle.length > 0 && pbnstBizCycle[0]? pbnstBizCycle[0].val : ""} - className={""} - selectClass={selectClass} - onSelectValue={(val) => {setBizCycle}} - > + <> + <div className=" !mx-[1.6rem] !p-0r"> + {/* 차수별 등록 현황 폼 */} + <div className="justify-between"> + <Form {...form}> + <form + onSubmit={form.handleSubmit(onSubmit)} + className="space-y-8 mx-auto w-full mt-7" + > + <div className="flex justify-between items-center mx-3 mb-3"> + <h1 className=" text-xl"></h1> + <div className="flex items-center gap-2 mx-3"> + <h1 className={formTableName}>등록차수</h1> + <SelectBox + label="" + selectArray={pbnstBizCycle} + value={bizCycle ?? pbnstBizCycle[0]?.val} + className={""} + selectClass={selectClass} + onSelectValue={(val) => { + setBizCycle(String(val)); + getpbnstInsertIndex({ + ...pbnst.pbnsUpdateIndex, + biz: Number(val), + }); + }} + ></SelectBox> - </SelectBox> - - <Button size={commonButonSize}>저장</Button> + <Button + onClick={addRuseFcltObj} + type="button" + size={commonButonSize} + > + 저장 + </Button> + </div> </div> - </div> - <MngRegFormDetail - formList={businessSttnInfo} - h1Title={"사업현황"} - form={form} - moClass="grid grid-cols-12 gap-4" - sonClass="col-span-4" - /> - - <MngRegFormDetail - formList={businessMoneyInfo} - h1Title={"사업비"} - form={form} - moClass="grid grid-cols-12 gap-4" - sonClass="col-span-4" - /> - <MngRegFormDetail - formList={ruseFcltinfo} - h1Title={"재처리시설"} - form={form} - moClass="grid grid-cols-12 gap-4" - sonClass="col-span-4" - /> - <MngRegFormDetail - formList={concentratedWaterInfo} - h1Title={"농축수관리"} - form={form} - moClass="grid grid-cols-12 gap-4" - sonClass="col-span-4" - /> - <MngRegFormDetail - formList={wasteWaterFcltINfo} - h1Title={"폐수배출시설 정보"} - form={form} - moClass="grid grid-cols-12 gap-4" - sonClass="col-span-4" - /> + <MngRegFormDetail + formList={businessSttnInfo} + h1Title={"사업현황"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + /> + <MngRegFormDetail + formList={businessMoneyInfo} + h1Title={"사업비"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + /> + <MngRegFormDetail + formList={ruseFcltinfo} + h1Title={"재처리시설"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + /> + <MngRegFormDetail + formList={concentratedWaterInfo} + h1Title={"농축수관리"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + /> + <MngRegFormDetail + formList={wasteWaterFcltINfo} + h1Title={"폐수배출시설 정보"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + /> - <h1 className={formH1Class}>재이용 원수취수 현황</h1> - </form> - </Form> + <h1 className={formH1Class}>재이용 원수취수 현황</h1> + <div> + <div className="text-right mb-3"> + <Button + type="button" + onClick={() => setIsFcOrgwts(true)} + size={commonButonSize} + className="" + > + 추가등록 + </Button> + </div> + <RuseMngTable + tableData={fcOrgwtFiled} + headList={pbnst.pbnstOrgwtHeadList} + upHeadList={[]} + /> + </div> + <h1 className={formH1Class}>재이용 공급관로 설치 현황</h1> + <div className="flex items-center gap-4 mx-3 mb-3"> + <Label className="text-right">공급관로설치유무</Label> + <SelectBox + label="" + selectArray={code.yn} + value={ppiYn} + className="w-32" + selectClass="w-full" + onSelectValue={(val) => setPpiYn(String(val))} + /> + <Label className="text-right">합계</Label> + <Input + type="text" + value={sumCpct} + readOnly={true} + className="w-32 border-gray-300 rounded pr-8 " + /> + {ppiYn === "Y" ? ( + <> + <Button + type="button" + size={commonButonSize} + className="!text-xl" + onClick={plusHist} + > + 추가등록 + </Button> + <Button + type="button" + size={commonButonSize} + className="!text-xl !bg-red-500 !text-white ml-1" + onClick={removeSelected} + > + 삭제 + </Button> + </> + ) : ( + <></> + )} + </div> + <div> + <RuseMngTable + tableData={pplInstls} + headList={pbnst.pplInstlsHeadLisat} + code={ppiSelectList ?? []} + ref={ref} + upHeadList={[]} + onTableSet={(val) => { + if (!ref.current) return; + let sum = 0; + ref.current.forEach((item) => { + sum += Number(item.cpct) || 0; + }); + setSumCpct(sum); + }} + /> + </div> + + <MngRegFormDetail + formList={ruseWaterQulityList} + h1Title={"하수처리수 재이용시설 보증수질"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + /> + <div key={"hasu"}> + <h1 className={formH1Class}>{"하수처리수 재이용수 수요처"}</h1> + <div className="text-right mb-3"> + <Button + type="button" + 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" + > + 수요처 추가 + </Button> + </div> + {fcDemands.map((field, index) => ( + <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="수요처명"> + <SearchInputText + label={undefined} + initValue={f.value} + setValue={(val) => { + f.onChange(val); + + if (val) getDemandCompList(val, 3); + }} + 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}.dmdInfoPk`, + valList['0'].dmdInfoPk ?? "Y" + ); + } + + }} + ></SearchInputText> + </FormUi> + )} + /> + </div> + <div key={index + "keySon2"} className="col-span-4"> + <FormField + control={form.control} + name={`fcDemands.${index}.daddr`} + render={({ field: f }) => ( + <FormUi label="위치(주소)"> + <Input + placeholder="-" + type="text" + readOnly + onChange={(e) => f.onChange(e.target.value)} + value={f.value} + className={selectClass} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon3"} className="col-span-4"> + <FormField + control={form.control} + name={`fcDemands.${index}.useUsgCd`} + render={({ field: f }) => ( + <FormUi label="사용용도"> + <SelectBox + label={""} + selectClass={""} + className="w-full" + selectArray={code.dmdUszCd} + onSelectValue={(val: string | number) => + f.onChange(val) + } + value={f.value} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon4"} className="col-span-3"> + <FormField + control={form.control} + name={`fcDemands.${index}.splyBgngDay`} + render={({ field: f }) => ( + <FormUi label="공급시작일"> + <DatePicker + mode={"single"} + selected={safeDate(f.value) || new Date()} + onSelect={(date: Date | undefined) => + f.onChange(date) + } + className={"rounded-lg !w-full"} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon5"} className="col-span-3"> + <FormField + control={form.control} + name={`fcDemands.${index}.splyEndDay`} + render={({ field: f }) => ( + <FormUi label="공급종료일"> + <DatePicker + mode={"single"} + selected={safeDate(f.value) || new Date()} + onSelect={(date: Date | undefined) => + f.onChange(date) + } + className={"rounded-lg !w-full"} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon6"} className="col-span-3"> + <FormField + control={form.control} + name={`fcDemands.${index}.splyCpct`} + render={({ field: f }) => ( + <FormUi label="(계약)공급량(m3/일)"> + <Input + placeholder="" + type="number" + value={f.value ?? 0} + onChange={(e) => { + f.onChange(Number(e.target.value)); + }} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon7"} className="col-span-3"> + <FormField + control={form.control} + name={`fcDemands.${index}.splyFmYn`} + render={({ field: f }) => ( + <FormUi label="공급 유량계 유무"> + <SelectBox + label={""} + selectClass={""} + className="w-full" + selectArray={code.yn} + onSelectValue={(val: string | number) => + f.onChange(val) + } + value={f.value} + /> + </FormUi> + )} + /> + </div> + </div> + ))} + </div> + </form> + </Form> + </div> + + <UiModal + open={isFcOrgwts} + onOpenChange={(open: boolean) => setIsFcOrgwts(open)} + bodyClass={"w-[50vw] max-w-none h-[60vh]"} + body={ + <FcOrgwtsRegModal + closeModal={() => { + setIsFcOrgwts(false); + }} + onRightSet={(val) => { + form.setValue("fcOrgwts", val); + }} + ></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> - </div> + </> ); } 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 dc18ebd4a..3f0776a4e 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx @@ -1,146 +1,314 @@ "use client"; -import { SelectBox } from "@common/business_components"; -import { FileUpload, FormUi, UiTable } from "@common/business_components/ui"; -import { Form, FormField } from "@common/business_components/ui/form"; -import { Button, Input, TableHead, TableRow } from "@common/components"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect, useState } from "react"; -import { set, useForm } from "react-hook-form"; -import { nullable, z } from "zod"; import { useWrStore } from "@/app/store"; -import { - selectClass, - type TableUpperProps, -} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; - +import { FileUpload, FormUi, SelectBox } from "@common/business_components/ui"; +import { Form, FormField } from "@common/business_components/ui/form"; +import { Button, Input } from "@common/components"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useRef, useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { z } from "zod"; import { commonButonSize, formH1Class, - formH2Class, formTableName, } from "@/components/lib/class/comClass"; -import RuseMngTable from "@/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable"; import { TusClientUpload } from "@/components/lib/useTus_wr/useTusWr"; -import type { Upload } from "tus-js-client"; -import type { fcltOperInfoType, ruseFcltType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import RuseMngTable from "@/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable"; +import { + pbnstRegZod, + type customPbnst, + type fcBizType, + type fcFileType, + type operType, + type selectLabel, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { Label } from "@common/business_components/ui/form/lib/label"; +import React from "react"; +export type FilesType = { + atflCd: string; + file: File; +}; -const formSchema = z.object({ - fcltNm: z.string().min(2).max(50), - psysScdg: z.string(), - designScdg: z.string(), - fcltCpct: z.number(), - swtrmtCpct: z.number(), - ruseCpct: z.number(), - splyExtraCpct: z.number(), -}); +export function PbnstMngForm({ + formType, + firstFcltData, +}: { + formType: string; + firstFcltData?: customPbnst; +}) { + const { + pbnst, + pbnstInsert, + pbnstMngData, + fileDelete, + fileDownLoad, + getBizCycle, + getpbnstInsertIndex, + appendRuseFclt + } = useWrStore((state) => state); -export function PbnstMngForm({ data }: { data: ruseFcltType[] }) { - const { pbnst } = useWrStore((state) => state); - const { pubMngUpHeadList, isInit } = pbnst; - const [ruseOperData, setRuseOperData] = useState<fcltOperInfoType[]>(); - const [planFiles, setPlanFiles] = useState<File[] | null>(null); - const [relationfiles, setRelationFiles] = useState<File[] | null>(null); + const [regFileList, setRegFileList] = useState<fcFileType[] | null>(null); + const [planFiles, setPlanFiles] = useState<FilesType[] | null>(null); + const [relationfiles, setRelationFiles] = useState<FilesType[] | null>(null); + const [fcltNm, setFcltNm] = useState<selectLabel[] | null>(null); + const [fcOpresData, setFcOpresData] = useState<operType[] | null>(null); - const [isModalOpen, setIsModalOpen] = useState(false); - - const [upload, setUpload] = useState<Upload | null>(null); const [progress, setProgress] = useState(0); + //const new File([] as BlobPart[], "") 파일 다운로드 활용 + const ref = useRef<operType[] | null>(null); + + const form = useForm<z.infer<typeof pbnstRegZod>>({ + resolver: zodResolver(pbnstRegZod), + defaultValues: + formType === "reg" && pbnst.pbnstMngRegData + ? pbnst.pbnstMngRegData[0] + : formType === "update" && + typeof pbnst.pbnsUpdateIndex.mng === "number" && + pbnst.pbnsUpdateIndex.mng >= 0 && + pbnst.pbnstRes + ? pbnst.pbnstRes[pbnst.pbnsUpdateIndex.mng] + : pbnstRegZod.parse({}), + }); + const { + fields: fcOpersField, + append, + remove, + } = useFieldArray({ + control: form.control, + name: "fcOpers", + }); const handleProgress = (percentage: string) => { - - console.log("handleProgress", percentage); setProgress(Number(percentage)); }; - const form = useForm<z.infer<typeof formSchema>>({ - resolver: zodResolver(formSchema), - defaultValues: { - fcltNm: "", - psysScdg: "", - designScdg: "", - fcltCpct: 0, - swtrmtCpct: 0, - ruseCpct: 0, - splyExtraCpct: 0, - }, - }); + const selectFclt = (val: number) => { + if (pbnst.pbnstRes && pbnst.pbnstRes[val]) { + getBizCycle(pbnst.pbnstRes[val]); + form.reset(pbnst.pbnstRes[val]); + getpbnstInsertIndex({ ...pbnst.pbnsUpdateIndex, mng: val }); + } + }; - const selectOptions = data?.map((item) => ({ - text: item.fcltNm, - val: item.pbnstRuseFcltPk, - })) || [{ text: "없음", val: "1" }]; - const onSelectValue = (val: string) => {}; + const plusHist = () => { + const currentTableDataFromRef = ref.current ? [...ref.current] : []; + const regData = { + choose: false, + pbnstRuseFcltFk:form.getValues('pbnstRuseFcltPk')??"", + rgtr: "", + mdfr: "", + operMthd: "", + rprtvBzenty: "", + bgngDt: new Date(), + endDt: new Date(), + }; + const newDataForRHF = [...currentTableDataFromRef, regData]; + form.setValue("fcOpers", newDataForRHF, { + shouldValidate: true, // 필요하다면 유효성 검사 실행 + shouldDirty: true, // 폼 상태를 dirty로 변경 + }); + }; + + const removeSelected = () => { + if (!ref.current) return; + const selectedIdxs = ref.current + .map((item, idx) => (item.choose ? idx : -1)) + .filter((idx) => idx !== -1); + selectedIdxs.reverse().forEach((idx) => remove(idx)); + }; + + const fileHandler = (files: File[], type: string) => { + let fileList = [] as FilesType[]; + if (type === "relation" && relationfiles) { + fileList = relationfiles; + } else if (type === "plan" && planFiles) { + fileList = planFiles; + } + + let setReFiles = [...(fileList || [])]; + if (files && (!fileList || fileList.length < files.length)) { + files.find((item) => { + if ( + !fileList || + fileList?.findIndex((reFile) => reFile.file === item) < 0 + ) { + TusClientUpload({ atflCd: "", file: item }) + .then((tusFile) => { + setReFiles.push({ + atflCd: tusFile.atflCd.toString(), + file: item, + }); + type === "relation" + ? setRelationFiles(setReFiles) + : setPlanFiles(setReFiles); + }) + .catch((e) => { + console.log(e); + }); + return item; + } + }); + } else if (fileList && (!files || fileList.length > files.length)) { + if (!files) { + fileList.forEach((file) => { + fileDelete(file.atflCd as string); + }); + setReFiles = []; + setReFiles.length = 0; + } else { + fileList?.forEach((item, index) => { + const idx = files?.indexOf(item.file); + if (idx < 0) { + setReFiles.splice(index); + fileDelete(item.atflCd as string); + } + }); + } + type === "relation" + ? setRelationFiles(setReFiles) + : setPlanFiles(setReFiles); + } + }; + + const regFileFilter = ( + files: FilesType[], + fileKnd: string, + regFile: fcFileType[] + ) => { + const exFileDatas = + regFile?.filter((item) => item.fileKnd !== fileKnd) || []; + const currentdata = files.map((item) => ({ + fileTkn: item.atflCd, + filePath: item.file.name, + fileKnd: fileKnd, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + })); + return [...exFileDatas, ...currentdata]; + }; + + // 등록할때 신규 값을 계속 가지고 있으려고 + useEffect(() => { + if ( + firstFcltData && + (form.getValues("swtrmtFcltFk") === "" || + form.getValues("swtrmtFcltFk") === undefined) + ) { + form.setValue("swtrmtFcltFk", firstFcltData.swtrmtFcltFk); + form.setValue( + "pbnstRuseFcltPk", + String(firstFcltData.swtrmtFcltFk + "1") + ); + form.setValue("fcBizs", firstFcltData.fcBizs); + } + }, [firstFcltData]); useEffect(() => { - if (isInit) { - form.reset(); - setRuseOperData([{}] as fcltOperInfoType[]); + const defaultFiles = form.getValues("fcFiles"); + + if (defaultFiles && defaultFiles.length > 0) { + const relationFileList = defaultFiles.filter( + (item) => item.fileKnd === "계통도" + ); + + const planFileList = defaultFiles.filter( + (item) => item.fileKnd === "평면도" + ); + if (relationFileList.length > 0) + setRelationFiles( + relationFileList.map((item) => ({ + atflCd: item.fileTkn, + file: new File([] as BlobPart[], item.filePath), + })) + ); // 또는 파일 형식 변환 로직이 있다면 여기에 추가 + + if (planFileList.length > 0) + setPlanFiles( + planFileList.map((item) => ({ + atflCd: item.fileTkn, + file: new File([] as BlobPart[], item.filePath), + })) + ); } - }, [isInit]); + }, [form.formState.defaultValues]); // 초기화 이후 1회 실행 + + // 등록할때 신규 값을 계속 가지고 있으려고 + useEffect(() => { + if ( + formType === "update" && + pbnst.pbnsUpdateIndex.biz >= 0 && + pbnst.pbnstRes + ) { + const bizData = pbnst.pbnstRes[pbnst.pbnsUpdateIndex.biz]; + + if (bizData) { + getBizCycle(bizData); + } + } + }, [pbnst.pbnsUpdateIndex, pbnst.pbnstRes, formType, getBizCycle]); + + //신규 등록시 입력할때 데이터 넣기 + useEffect(() => { + const formData = form.watch(); + formData.fcOpers = ref.current ? [...ref.current] : []; + formData.fcFiles = regFileList ? [...regFileList] : []; + if (formType === "reg") { + 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, pbnst.pbnstRes]); + + useEffect(() => { + pbnstMngData(pbnst.pbnstMngRegData); + }, [pbnst.pbnstMngRegData]); + + useEffect(() => { + + 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); + + }, [pbnst.pbnstRes]); // 2. Define a submit handler. - function onSubmit(values: z.infer<typeof formSchema>) { - console.log(values); + function onSubmit(values: z.infer<typeof pbnstRegZod>) { + values.fcOpers = fcOpresData || []; + values.fcFiles = regFileList || []; + pbnstInsert(values); } - const testFile = () => { - if (relationfiles && relationfiles[0]) - TusClientUpload(relationfiles[0], setUpload, { - handleUploadStart: () => { - console.log("handleUploadStart"); - }, - handleProgress, - handleResponse: (req, res) => { - console.log(" handleResponse req", req); - console.log("handleResponse res", res); - }, - handleSuccess: (a) => { - console.log("req", a); - }, - }); - }; + useEffect(() => { - if (relationfiles && relationfiles[0]) - console.log("relationfiles ", relationfiles[0]); - - // TusClientUpload( - // relationfiles[0],{ - // handleUploadStart: setUpload, - // handleProgress, - // handleSuccess: setUpload, - // }) + if (relationfiles) { + setRegFileList(regFileFilter(relationfiles, "계통도", regFileList || [])); + } }, [relationfiles]); - const headMakeColSpan = (upHeadList: TableUpperProps[]) => { - return ( - <> - <TableRow> - {upHeadList.map((head: TableUpperProps) => - !head.upSequnce ? ( - <TableHead rowSpan={2} key={head.id}> - {head.title} - </TableHead> - ) : head.upName === "upChangeRe" && head.upSequnce === 1 ? ( - <TableHead key={head.id} rowSpan={1} colSpan={2}> - {"운영대행기간"} - </TableHead> - ) : null - )} - </TableRow> - <TableRow> - {upHeadList.map((head: TableUpperProps) => - head.upName === "upChangeRe" ? ( - <TableHead key={head.id}>{head.title}</TableHead> - ) : null - )} - </TableRow> - </> - ); - }; - const nodeList = headMakeColSpan(pubMngUpHeadList); + useEffect(() => { + if (planFiles) { + setRegFileList(regFileFilter(planFiles, "평면도", regFileList || [])); + } + }, [planFiles]); + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); return ( <div className=" !mx-[1.6rem] !p-0r"> @@ -149,91 +317,152 @@ export function PbnstMngForm({ data }: { data: ruseFcltType[] }) { <form onSubmit={form.handleSubmit(onSubmit)}> <div className="flex justify-between items-center mx-3 mb-3"> <div className="col-span-2 flex items-top"> - <h2 className={formTableName + "pr-3 col-span-10 "}> + <Label className={formTableName + "pr-3 col-span-2 "}> 재이용 시설명 - </h2> - <FormField - control={form.control} - name="fcltNm" - render={({ field }) => ( - <FormUi label=""> - <SelectBox - label="" - selectArray={selectOptions} - className={""} - selectClass={selectClass} - onSelectValue={() => {}} - {...field} - /> - </FormUi> - )} - /> + </Label> + + {formType === "reg" ? ( + <FormField + control={form.control} + name="ruseUtztnFcltNm" + render={({ field }) => ( + <FormUi label=""> + <Input + className="!text-2xl" + placeholder="" + type="text" + onChange={(e) => { + field.onChange(String(e.target.value)); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + ) : ( + <SelectBox + label="" + className="mt-4 ml-2" + selectClass={undefined} + selectArray={fcltNm ?? []} + onSelectValue={(val) => { + selectFclt(Number(val)); + }} + value={""} + /> + )} </div> + + {/* <Button onClick={addRuseFcltObj} type="button" size={commonButonSize}> + 추가 + </Button> */} </div> <div> - <div className="h-[200px] overflow-y-auto"> + <div className="h-[200px] overflow-y-auto"> <div className="flex justify-between items-center mx-3 mb-3"> <span className={formTableName}>재이용시설 운영정보</span> - <Button - type="button" - size={commonButonSize} - className="!text-xl" - onClick={() => testFile()} - > - 추가등록 - </Button> + <div> + <Button + type="button" + size={commonButonSize} + className="!text-xl" + onClick={plusHist} + > + 추가등록 + </Button> + <Button + type="button" + size={commonButonSize} + className="!text-xl !bg-red-500 !text-white ml-1" + onClick={removeSelected} + > + 삭제 + </Button> + </div> </div> <RuseMngTable - tableData={[]} - headList={pbnst.pubMngUpHeadList} - upHeadList={pbnst.pubMngNm} + tableData={fcOpersField} + headList={pbnst.pbnstMngUpHeadList} + upHeadList={pbnst.pbnstMngNm} + onTableSet={(val) => { + setFcOpresData(val); + }} + ref={ref} ></RuseMngTable> </div> </div> <div> <h1 className={formH1Class}>재이용수 공급 가능량</h1> <div className="grid grid-cols-12 gap-4"> - <div className="col-span-3"> + <div className="col-span-3 mt-4"> <FormField control={form.control} name="fcltCpct" render={({ field }) => ( <FormUi label="시설용량(m³/일)"> - <Input placeholder="" type="number" {...field} /> + <Input + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> </FormUi> )} /> </div> - <div className="col-span-3"> + <div className="col-span-3 mt-4"> <FormField control={form.control} - name="swtrmtCpct" + name="hasuhasu" render={({ field }) => ( <FormUi label="하수처리량(m³/일)"> - <Input placeholder="" type="number" {...field} /> + <Input + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> </FormUi> )} /> </div> - <div className="col-span-3"> + <div className="col-span-3 mt-4"> <FormField control={form.control} name="ruseCpct" render={({ field }) => ( <FormUi label="재이용량(m³/일)"> - <Input placeholder="" type="number" {...field} /> + <Input + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> </FormUi> )} /> </div> - <div className="col-span-3"> + <div className="col-span-3 mt-4"> <FormField control={form.control} name="splyExtraCpct" render={({ field }) => ( <FormUi label="공급여유량(m³/일)"> - <Input placeholder="" type="number" {...field} /> + <Input + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + /> </FormUi> )} /> @@ -244,34 +473,40 @@ export function PbnstMngForm({ data }: { data: ruseFcltType[] }) { </div> <div className="grid grid-cols-12 gap-4 my-4"> <div className="col-span-6"> - <FormField - control={form.control} - name="psysScdg" - render={({}) => ( - <FormUi label="계통도"> - <FileUpload - files={relationfiles || []} - setFiles={setRelationFiles} - fileTypes="PDF" - ></FileUpload> - </FormUi> - )} - /> + <FormUi label="계통도"> + <FileUpload + files={relationfiles?.map((item) => item.file) || []} + setFiles={(files: File[] | null) => { + if (files) fileHandler(files, "relation"); + }} + fileTypes="PDF" + onFileClick={(index) => { + fileDownLoad( + relationfiles?.find( + (item, fileIndex) => fileIndex === index + )?.atflCd as string + ); + }} + ></FileUpload> + </FormUi> </div> <div className="col-span-6"> - <FormField - control={form.control} - name="designScdg" - render={({}) => ( - <FormUi label="평면도"> - <FileUpload - files={planFiles || []} - setFiles={setPlanFiles} - fileTypes="PDF" - ></FileUpload> - </FormUi> - )} - /> + <FormUi label="평면도"> + <FileUpload + files={planFiles?.map((item) => item.file) || []} + setFiles={(files: File[] | null) => { + if (files) fileHandler(files, "plan"); + }} + fileTypes="PDF" + onFileClick={(index) => { + fileDownLoad( + planFiles?.find( + (item, fileIndex) => fileIndex === index + )?.atflCd as string + ); + }} + ></FileUpload> + </FormUi> </div> </div> </div> diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx index 1c3a6dfb2..7f9ff4fef 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx @@ -1,8 +1,17 @@ +'use client' +import { useWrStore } from "@/app/store"; import type { formDetailType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/headList"; import { formH1Class } from "@/components/lib/class/comClass"; import { safeDate } from "@/components/lib/func/commom"; -import { FormField, FormUi } from "@common/business_components/ui"; +import { + FormField, + FormUi, + SearchInputSelect, + SelectBox, + type SelectDataType, +} from "@common/business_components/ui"; import { DatePicker, Input } from "@common/components/ui"; +import { useState } from "react"; export default function MngRegFormDetail({ formList, @@ -10,23 +19,34 @@ export default function MngRegFormDetail({ form, moClass, sonClass, + cycle, + onIputData, + searchSelectData }: { formList: formDetailType[]; - h1Title: string; + h1Title?: string; form: any; moClass?: string; + cycle?: string; sonClass?: string; + onIputData?: (data: any) => void; + searchSelectData?: SelectDataType[]; }) { + const { code } = useWrStore((state) => state); + + const [searchData, setSearchData]= useState<string>('') + return ( <> - <h1 className={formH1Class}>{h1Title}</h1> + {h1Title ? <h1 className={formH1Class}>{h1Title}</h1> : <></>} <div className={moClass}> {formList?.map((item, index) => { + const fieldId = cycle ? `${cycle}.${item.id}` : item.id; return ( <div className={sonClass} key={index + `${item.id}`}> <FormField control={form.control} - name={item.id} + name={fieldId} render={({ field }) => ( <FormUi label={item.lable}> {item.type === "select" ? ( @@ -34,17 +54,39 @@ export default function MngRegFormDetail({ ) : item.type === "date" ? ( <DatePicker mode={"single"} - field={{ - value: safeDate(field.value) || new Date(), - onChange: (date: Date | undefined) => - field.onChange(date), - }} - selectClass={"rounded-lg w-full"} - ></DatePicker> + selected={safeDate(field.value) || new Date()} + onSelect={(date: Date | undefined) => + field.onChange(date) + } + className={"rounded-lg !w-full"} + /> ) : item.type === "number" ? ( - <Input placeholder="" type="number" {...field} /> + <Input + placeholder="" + type="number" + value={field.value ?? 0} + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + /> + ) : item.type === "selectYn" ? ( + <SelectBox + label={""} + selectClass={""} + className="w-full" + selectArray={code.yn} + onSelectValue={(val: string | number) => + field.onChange(val) + } + value={field.value} + /> ) : ( - <Input placeholder="" type="" {...field} /> + <Input + placeholder="" + type="text" + value={field.value ?? ""} + onChange={field.onChange} + /> )} </FormUi> )} @@ -56,3 +98,4 @@ export default function MngRegFormDetail({ </> ); } + diff --git a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx new file mode 100644 index 000000000..8190b96e8 --- /dev/null +++ b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx @@ -0,0 +1,168 @@ +import { useWrStore } from "@/app/store"; +import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel"; +import { pbnPrfRegExcelZod } from "@/app/store/prfmncReg/pbnstPrfmncReg/headList"; +import { yearOptions } from "@/components/lib/func/commom"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import { + FileUpload, + FormField, + FormUi, + SelectBox, + Form, + Title, + type SelectDataType, + filePicked, +} from "@common/business_components/ui"; +import { Button } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import type { z } from "zod"; + +export default function PbnPrfExcelModal() { + const { code, prfmnc, searchFclts } = useWrStore((state) => state); + + + const [bscFctNmList, setBscFctNmList] = useState<SelectDataType[] | null>(null); + const [files, setFiles] = useState<File[] | null>(null); +const [excelData, setExcelData] = useState<any[]>(); + const form = useForm<z.infer<typeof pbnPrfRegExcelZod>>({ + resolver: zodResolver(pbnPrfRegExcelZod), + defaultValues: { + ym: String(new Date().getFullYear()), + }, + }); + const onSubmit = (values: z.infer<typeof pbnPrfRegExcelZod>) => { + console.log(values); + }; + useEffect(() => { + + if(prfmnc.prfmncRes.length > 0){ + + const basFctNmList = prfmnc.prfmncRes.map((item)=>( + { + val:item.bscFctCd, + text:item.bscFctNm + } + )) + setBscFctNmList(basFctNmList) + } + + }, [prfmnc.prfmncRes]); + + + useEffect(()=>{ + + if(files){ + + const file = files[0]; + if (form.getValues("ym").length < 1) { + ToastFormUi({ + body: "파일업로드 이전에 등록년도를 작성해주세요 ", + title: "지도점검 등록", + }); + setFiles(null); + return; + } + + if(file) + filePicked(file, 0).then((res) => { + if (res !== undefined) + console.log(excelToPbnPrfData(res, form.getValues("ym"))) + }); + + + } + },[files]) + + return ( + <> + <div> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <Title title={"하수재이용 실적등록"}> +
+
+

+ {"점검정보 입력"} +

+ + +
+ +
+ ( + + field.onChange(val)} + /> + + )} + /> + + ( + + field.onChange(String(val))} + /> + + )} + /> + ( + + field.onChange(String(val))} + /> + + )} + /> + + ( + + { + if (files) setFiles(files); + }} + fileTypes="xlsx" + > + + )} + /> +
+ + +
+ + ); +} diff --git a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx new file mode 100644 index 000000000..b04386752 --- /dev/null +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -0,0 +1,150 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import type { pubHasuType, fcBizType, fcOrgwtType,} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { tableData } from "@/components/lib/func/commom"; +import RuseMngTable from "@/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable"; +import { Title, UiTable } from "@common/business_components/ui"; +import { Label } from "@common/business_components/ui/form/lib/label"; +import { Button } from "@common/business_components/ui/table/lib/button"; +import { Input } from "@common/components/ui"; +import { ArrowLeft, ArrowRight } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import { useFieldArray } from "react-hook-form"; + +export default function FcOrgwtsRegModal({ onRightSet, closeModal }: { onRightSet?: (rightData:fcOrgwtType[]) => void,closeModal?:(val:boolean) =>void }) { + const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore((state) => state); + const { fcOrgwtsHeadList, pbnstHasuRes } = pbnst; + const [bdstTable, setbdstTable] = useState(); + const rightRef = useRef([] as pubHasuType[]); + const leftRef = useRef([] as pubHasuType[]); + const [rightData, setRightData] = useState(); + const [inputVal, setInputVal] = useState(""); + + useEffect(()=>{ + console.log("searchFclts", searchFclts); + if(Array.isArray(searchFclts)){ + const filteredData = searchFclts.filter((item) => { + if(rightData){ + if(rightData.find(rightItem =>rightItem.bscFctCd===item.bscFctCd)) + return false; + } + return true + }) + setbdstTable(Array.isArray(filteredData) ? [...filteredData] : []) + } + + }, [searchFclts]) + useEffect(() => { + if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { + setbdstTable(tableData(pbnstHasuRes, { code })); + + console.log('bdstTable ', bdstTable) + } + }, [code, pbnstHasuRes]); + + useEffect(() => { + console.log('rightData ', rightData) + if(onRightSet) + onRightSet( + (rightData as pubHasuType[])?.map(item => ({ + swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정 + orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정 + tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정 + dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정 + dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정 + choose: false, // 임시값, 실제 데이터에 맞게 수정 + // 필요한 경우 다른 속성들도 추가 + })) as unknown as fcOrgwtType[] + ); + }, [rightData]) + + return ( + <> +
+
+ + <Button className="m-2" size={"sm"} + onClick={() => {closeModal && closeModal(false) }}>저장</Button> + </div> + + <div className="flex flex-row flex-grow gap-4 mt-4"> + <div className="w-3/7"> + <div className="flex items-center justify-between"> + <Label className="w-full text-2xl text-gray-600 text-left">공공하수처리시설 목록</Label> + <div className="flex items-center"> + <Label className="w-full text-xl text-gray-600 text-right">시설명</Label> + <Input className="w-1/2 !text-xl ml-2" type="text" value={inputVal} onChange={(e) => {setInputVal(e.target.value)}} /> + <Button className="m-2" size={"sm"} onClick={() => getSwtrmtFcltNm(inputVal)}>조회</Button> + </div> + </div> + <div className="border-t border-gray-300 my-4"></div> + <RuseMngTable + tableData={bdstTable || []} + headList={fcOrgwtsHeadList} + upHeadList={[]} // TODO: Replace with actual upHeadList data if available + ref={leftRef} + /> + </div> + <div className="flex flex-col items-center justify-center w-1/7 gap-2"> + <ArrowRight + type="button" + onClick={() => { + if (leftRef && typeof leftRef === "object" && "current" in leftRef) { + const checked = leftRef.current?.filter((item) => ((item as unknown as pubHasuType)?.choose)) + const unchecked = leftRef.current?.filter((item) => !(item as unknown as pubHasuType)?.choose) + if (checked && checked.length > 0) { + checked.forEach(item=>(item as unknown as pubHasuType).choose = false) + setRightData([...(checked as unknown as pubHasuType[]), + ...((rightRef.current ?? []) as unknown as pubHasuType[]) + ]) + + } + if (unchecked && unchecked.length > 0) { + setbdstTable([...(unchecked as unknown as pubHasuType[])]) + }else{ + setbdstTable([]) + } + } + } + } + className="w-12 h-12 text-gray-500 cursor-pointer" + /> + <ArrowLeft + type="button" + onClick={() => { + if (rightRef && typeof rightRef === "object" && "current" in rightRef) { + const checked = rightRef.current?.filter((item) => ((item as unknown as pubHasuType)?.choose)) + const unchecked = rightRef.current?.filter((item) => !(item as unknown as pubHasuType)?.choose) + if (checked && checked.length > 0) { + checked.forEach(item=>(item as unknown as pubHasuType).choose = false) + setbdstTable([ + ...(checked as unknown as pubHasuType[]), + ...((leftRef.current ?? []) as unknown as pubHasuType[]) + ]) + } + if (unchecked && unchecked.length > 0) { + setRightData([...(unchecked as unknown as pubHasuType[])]) + }else{ + setRightData([]) + } + } + }} + className="w-12 h-12 text-gray-500 cursor-pointer mt-5" /> + </div> + {/* Right Table container: Takes 2/5 of the width */} + <div className="w-3/7"> + <Label className="w-full text-2xl text-gray-600 text-right">취수하수처리장 목록</Label> + <div className="border-t border-gray-300 my-4"></div> + <RuseMngTable + tableData={rightData || []} + headList={fcOrgwtsHeadList} + upHeadList={[]} // TODO: Replace with actual upHeadList data if available + ref={rightRef} + /> + </div> + </div> + </div> + </> + ); +} diff --git a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx new file mode 100644 index 000000000..3929551e8 --- /dev/null +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -0,0 +1,90 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import type { + customPbnst, + fcBizType, + pubHasuType, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; +import { ScrollArea } from "@common/business_components"; +import { + Button, + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@common/components/ui"; +import { useEffect, useState } from "react"; +import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm"; + +export function PbnstRegModal({ data }: { data: pubHasuType }) { + const { pbnst, pbnstInsert } = useWrStore((state) => { + return state; + }); + const [pbnstBdstForm, setPbnstBdstForm] = useState<customPbnst>( + {} as customPbnst + ); + const [currentTab, setCurrentTab] = useState<string>("mng"); + + const pbnstRegTotal = () => { + if (pbnst.pbnstMngRegData[0]) pbnstInsert(pbnst.pbnstMngRegData[0]); + }; + + const handleTabChange = (value: string) => { + setCurrentTab(value); + }; + + useEffect(() => { + if (data) { + let regData = { + swtrmtFcltFk: String(data.bscFctCd), + pbnstRuseFcltPk: String(data.bscFctCd + "1"), + fcBizs: [ + { + pbnstRuseFcltFk: String(data.bscFctCd + "1"), + cycl: 1, + } as unknown as fcBizType, + ], + }; + + setPbnstBdstForm(regData as unknown as customPbnst); + } + }, [data]); + + useEffect(() => {}, [pbnstBdstForm]); + useEffect(() => {}, [pbnst.pbnstMngRegData]); + return ( + <> + <h1 className="text-2xl font-medium text-label pl-8"> + 하수재이용 시설 신규등록 + </h1> + + <div className="border-t border-gray-300 my-4"></div> + <ScrollArea className="h-[75vh]"> + <Tabs defaultValue="mng" onValueChange={handleTabChange}> + <TabsList className=""> + <TabsTrigger className="!text-xl" value="mng"> + 시설정보 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="buse"> + 차수별정보 + </TabsTrigger> + <Button + onClick={pbnstRegTotal} + size={"sm"} + className="!justify-end" + > + 저장 + </Button> + </TabsList> + <TabsContent className="max-h-full" value="mng"> + <PbnstMngForm formType={"reg"} firstFcltData={pbnstBdstForm} /> + </TabsContent> + <TabsContent className="max-h-full" value="buse"> + <PbnstBuseForm formType={"reg"} firstBizData={pbnstBdstForm.fcBizs} /> + </TabsContent> + </Tabs> + </ScrollArea> + </> + ); +} 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<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)} <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> + ) +} \ No newline at end of file diff --git a/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx b/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx index 6ba5739db..43a08e203 100644 --- a/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx +++ b/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx @@ -1,51 +1,151 @@ import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; -import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import { Input, Table, TableBody, TableCell, TableHeader, TableRow } from "@common/components/ui"; +import type { customPbnst as OriginalCustomPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; -export default function RuseMngTable({ - tableData, - headList, - upHeadList, -}: { +import { selectClass, type TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { DatePicker, SelectBox, type SelectDataType } from "@common/business_components/ui"; +import { + Checkbox, + Input, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@common/components/ui"; +import React, { useEffect, useState } from "react"; + +type RuseMngTableProps = { tableData: any[]; headList: TableUpperProps[]; upHeadList: chHeadType[]; -}) { - return ( - <> - <Table variant="secondary"> - <TableHeader className="font-bold"> - {headMakeprfmnc(headList, upHeadList)} - </TableHeader> + code?: { name: string, val: SelectDataType[] }[]; + onTableSet?:(item:any)=> void; +}; - <TableBody> - {tableData.map((item, index) => ( - <TableRow key={index}> - {headList.map((head, index) => ( - <TableCell - key={`${head.id}-${index}`} - className="text-center whitespace-nowrap" - onClick={() => {}} - > - {head.edit === true ? ( - <Input - className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`} - type={head.type} - onChange={(e) => { - ; - }} - value={item[head.id] || ""} - /> - ) : ( - `${item[head.id] || ""}` - )} - </TableCell> - ))} - </TableRow> - ))} - </TableBody> - </Table> - </> - ); -} +const RuseMngTable = React.forwardRef<any, RuseMngTableProps>( + ({ + tableData, + headList, + upHeadList, + code, + onTableSet + }, ref) => { + const [tableState, setTableState] = useState([...tableData]); + useEffect(() => { + if (ref && typeof ref === "object" && "current" in ref) { + ref.current = tableState; + } + if(onTableSet) + onTableSet (tableState) + }, [tableState,onTableSet]); + + + + + useEffect(() => { + if(tableData) + setTableState(tableData); + }, [tableData]); + return ( + <> + <Table variant="secondary"> + <TableHeader className="font-bold"> + {upHeadList ? ( + headMakeprfmnc(headList, upHeadList) + ) : ( + <TableRow key={"table-row-test"}> + {headList.map((head) => ( + <TableHead key={"table-head-row " + head.id} className={`border border-gray-600 px-2 py-1 `}> + {head.title} + </TableHead> + ))} + </TableRow> + )} + </TableHeader> + + <TableBody> + {tableState.map((item, i) => ( + <TableRow key={i}> + {headList.map((head, j) => ( + <TableCell + key={`${head.id}-${j}`} + className="text-center whitespace-nowrap" + onClick={() => { }} + > + {head.edit === true ? ( + <Input + className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`} + type={head.type} + onChange={(txt) => { + setTableState(prev => { + const next = [...prev]; + next[i] = { ...next[i], [head.id]: txt.target.value }; + return next; + }) + }} + value={head.type === "number" ? + Number(tableState[i][head.id])||0 : tableState[i][head.id] || "" + } + /> + ) : head.type === "select" ? ( + <SelectBox + label="" + selectArray={code.find(item => item.name === head.selectData)?.val ?? []} + value={ + tableState[i][head.id] || "" + } + className={""} + selectClass={selectClass} + onSelectValue={(val) => { + setTableState((prev) => { + prev[i][head.id] = val + return [...prev] + }) + }} + ></SelectBox> + ) : head.type === "date" ? ( + <DatePicker + mode="single" + field={{ + value: + tableState[i][head.id], // Provide a default Date value + onChange: (date) => { + setTableState(prev => { + const next = [...prev]; + next[i] = { ...next[i], [head.id]: date }; + return next; + }) + } + } + } + selectClass={"w-full"} + /> + ) : head.type === "checkbox" ? ( + <Checkbox + className="" + checked={!!tableState[i][head.id]} + onCheckedChange={(checked) => { + console.log("checked", checked); + setTableState((prev) => { + prev[i][head.id] = checked + return [...prev] + }) + }} + > + </Checkbox> + ) : + ( + `${item[head.id] || ""}` + )} + </TableCell> + ))} + </TableRow> + ))} + </TableBody> + </Table> + </> + ); + }) +export default RuseMngTable; \ No newline at end of file diff --git a/apps/wr/middleware.ts b/apps/wr/middleware.ts index 3ac0d64ed..330252074 100644 --- a/apps/wr/middleware.ts +++ b/apps/wr/middleware.ts @@ -2,10 +2,9 @@ import { NextRequest, NextResponse } from "next/server"; import { CS_URL } from "./components/lib/config"; //import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config"; -const CLAIM_NAME = "Authorization" -const AUTH_USER_ID = "token-id" -const serverApiUrl = - CS_URL; +const CLAIM_NAME = "Authorization"; +const AUTH_USER_ID = "token-id"; +const serverApiUrl = CS_URL; export function middleware(req: NextRequest) { const pathname = req.nextUrl.pathname; console.log(`[Middleware] Request URL: ${req.url}`); @@ -13,29 +12,32 @@ export function middleware(req: NextRequest) { if (pathname.startsWith("/server")) { const queryParams = searchParams.toString(); - const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${queryParams ? `?${queryParams}` : "" - }`; + const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ + queryParams ? `?${queryParams}` : "" + }`; console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`); return NextResponse.rewrite(new URL(newUrl)); } if (pathname.startsWith("/attach-service")) { const requestHeaders = new Headers(req.headers); - const token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhMGVjN2EwNy03NTJhLTRmN2YtODlhNi04NTRlNjNkMmUzYTAiLCJpYXQiOjE3NDc4MTM2MDksImV4cCI6MTc0NzgxMzkwOX0.XGiDHC-ljuEdN6M638q0nDet6XRQcO8o66LKvZZ07r4rGykn2ba8yagC_-8LVpVjrkPzIVZnW10I2fQWgAGT5Q" - const userId = 'a0ec7a07-752a-4f7f-89a6-854e63d2e3a0'; + const token = + "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhMGVjN2EwNy03NTJhLTRmN2YtODlhNi04NTRlNjNkMmUzYTAiLCJhdXRob3JpdGllcyI6IlJPTEVfQURNSU4sUk9MRV9BTk9OWU1PVVMiLCJtbmdyWW4iOiJZIiwiZXhwIjoxNzQ4ODQ4NDkxfQ.ZiYpKxAMyyoPuUquxsWxfQbMBzYN4tsa7itZ49_OEsdGvbUUmKzrIV6Or7VVbEjtAqhh7W97nqB5EP6GRzFC9Q"; + const userId = "a0ec7a07-752a-4f7f-89a6-854e63d2e3a0"; //const token = req.cookies.get(CLAIM_NAME)?.value; //const userId = req.cookies.get(AUTH_USER_ID)?.value; const newUrl = `${serverApiUrl}${pathname}`; console.log("serverApiUrl ", serverApiUrl); console.log("pathname ", pathname); + //wr:dev: pathname /attach-service/api/v1/tus/upload/5e60b70a-956e-40c3-be5a-cdd3100bf8ad - + //http://dev.onasys.co.kr:8000/attach-service/api/v1/tus/upload/5e60b70a-956e-40c3-be5a-cdd3100bf8ad if (token) requestHeaders.set(CLAIM_NAME, token); if (userId) requestHeaders.set(AUTH_USER_ID, userId); console.log( - `[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`, + `[Middleware] Rewriting from '/attach-service' path to: ${newUrl}` ); return NextResponse.rewrite(new URL(newUrl), { headers: requestHeaders, diff --git a/apps/wr/package.json b/apps/wr/package.json index 58a01dc42..44ba5a06b 100644 --- a/apps/wr/package.json +++ b/apps/wr/package.json @@ -17,6 +17,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.474.0", + "moment": "^2.30.1", "next": "15.1.7", "react-hook-form": "^7.54.2", "react-hot-toast": "^2.5.2", diff --git a/apps/wr/public/sample_pbnstPrfmncReg.xlsx b/apps/wr/public/sample_pbnstPrfmncReg.xlsx new file mode 100644 index 000000000..67f0d559e Binary files /dev/null and b/apps/wr/public/sample_pbnstPrfmncReg.xlsx differ diff --git a/apps/wr/public/sample_pbnstSttn.xlsx b/apps/wr/public/sample_pbnstSttn.xlsx new file mode 100644 index 000000000..8bbd47ea3 Binary files /dev/null and b/apps/wr/public/sample_pbnstSttn.xlsx differ diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index cdc88075b..0802a72e4 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -64,10 +64,10 @@ const CustomLegend = (props: any) => { const { payload } = props; return ( <div style={customStyles.legendContainer}> - {payload.map((entry, index: number) => ( + {payload.map((entry: unknown, index: number) => ( <div key={`legend-${index}`} className="flex items-center gap-2"> - <div className="w-[14px] h-[14px] ml-10" style={{ backgroundColor: entry.color }}></div> - <span className="text-[12px]">{entry.value}</span> + <div className="w-[14px] h-[14px] ml-10" style={{ backgroundColor: (entry as {color: string}).color }}></div> + <span className="text-[12px]">{(entry as {value: string}).value}</span> </div> ))} </div> diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index a689af8ca..4014702b6 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -113,20 +113,21 @@ export default function Linechart({ }, []); // 데이터의 최대값 계산 - const getMaxValue = (data: ChartDataType, keyPrefix: string) => { - return Math.max(...Object.values(data).map(item => - Object.keys(item) - .filter(key => key.includes(keyPrefix)) - .map(key => item[key] as unknown as number) - ).flat()); + const getMaxValue = (data: ChartDataType): number => { + return Math.max(...Object.values(data).flatMap(dataset => + dataset.map(entry => { + const val = parseFloat(entry.val); + return isNaN(val) ? 0 : val; + }) + )); }; - const lineMaxValue = getMaxValue(chartData, 'lineVal'); + const lineMaxValue = getMaxValue(chartData); return ( <div className={chartClassName}> {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( - <div style={{ textAlign: "left", fontSize: "13px", fontWeight: "bold", marginTop:"13px", marginBottom: "10px" }}> + <div className="text-left text-[13px] font-bold mt-[13px] mb-[10px]"> {chartTitle} </div> ) : null} diff --git a/packages/common/business_components/src/ui/excel/excelStyle.tsx b/packages/common/business_components/src/ui/excel/excelStyle.tsx index 1c6b2b0e2..3c8f8d219 100644 --- a/packages/common/business_components/src/ui/excel/excelStyle.tsx +++ b/packages/common/business_components/src/ui/excel/excelStyle.tsx @@ -1,5 +1,6 @@ -import type { sheetParameter } from '@/index'; +// import type { sheetParameter } from '@/index'; import * as XlsxPopulate from '@xlsx/xlsx-populate'; +import type { SheetParameter } from './index'; @@ -38,13 +39,13 @@ export const excelStyle = async (data: ArrayBuffer, range: string = "A1:V3", fil } -export const excelArrayStyle = async (data: ArrayBuffer, range: Array<typeof sheetParameter>, fileNm:string) => { +export const excelArrayStyle = async (data: ArrayBuffer, range: SheetParameter[], fileNm: string) => { const workbook = await XlsxPopulate.Workbook.fromData(data); - if(range){ - range.forEach((item , index)=>{ + if (range) { + range.forEach((item, index) => { const rangeCells = workbook.activeSheet(index)?.sheet(index)?.range(item.headerRange); console.log(rangeCells); - rangeCells?.style((styleQ)=>{ + rangeCells?.style((styleQ) => { makeCellStyle(styleQ) }); diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 76b4ba2f9..96f3b3e5d 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -5,7 +5,7 @@ type ExcelDownloadProps = { data: JSON | JSON[]; fileName: string; sheetNameArray: string[]; - headerRange: string; + headerRange?: string; } export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => { @@ -15,17 +15,21 @@ export const downloadExcel = ({ data, fileName = new Date().getTime().toString() excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; -export const sheetParameter ={ - data:[[]], headerNum:0, headerRange:'A1:V3', sheetName:'Sheet1', startCol:0 -} +export type SheetParameter = { + data: any[][]; + headerNum: number; + headerRange: string; + sheetName: string; + startCol?: string; +}; -export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: Array<typeof sheetParameter>): Promise<JSON | undefined> => { +export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: SheetParameter[]): Promise<JSON | undefined> => { const workbook = XLSX.utils.book_new(); for (let i = 0; i < sheetArray.length; i++) { // Create a new worksheet for each sheet const worksheet = XLSX.utils.aoa_to_sheet(sheetArray[i]?.data || [[]]); // Add data to the worksheet starting from the specified header number - if (sheetArray&&sheetArray[i]?.headerNum) { + if (sheetArray && sheetArray[i]?.headerNum) { XLSX.utils.sheet_add_aoa(worksheet, sheetArray[i]?.data || [[]], { origin: `A${sheetArray[i]?.headerNum}` }); } // Append the worksheet to the workbook with the specified sheet name @@ -37,7 +41,6 @@ export const downloadMultipleExcel = (fileName = new Date().getTime().toString() return Promise.resolve(undefined); }; - export const filePicked=(oFile: File, headerNum:number=0, dateType="yyyymmdd"):Promise<JSON | undefined> =>{ // Create A File Reader HTML5 const reader = new FileReader(); @@ -80,8 +83,6 @@ export const filePicked=(oFile: File, headerNum:number=0, dateType="yyyymmdd"):P }); } - - export const filePickedArray=(oFile: File, headerNum:number=0):Promise<JSON[] | undefined> =>{ // Create A File Reader HTML5 const reader = new FileReader(); @@ -123,8 +124,6 @@ export const filePickedArray=(oFile: File, headerNum:number=0):Promise<JSON[] | }); } - - export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0, headerRange='A1:V3', startCol?:number):Promise<JSON | undefined> =>{ // Create A File Reader HTML5 return new Promise<JSON | undefined>(() => { // Ready The Event For When A File Gets Selected @@ -149,28 +148,20 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, header }); } - export const filePickedBlobArray= (oFile: ArrayBuffer,fileNm:string, sheetArray: [typeof sheetParameter]):Promise<JSON | undefined> =>{ // Create A File Reader HTML5 - return new Promise<JSON | undefined> (async () => { - // Ready The Event For When A File Gets Selected - const wb = XLSX.read(oFile, { type: 'binary' }); - // Loop Over Each Sheet - wb.SheetNames.forEach( function (sheetName, index) { - // Obtain The Current Row As CSV - if(sheetArray[index]){ - const sheet = wb.Sheets[sheetName]; - if (sheet) { - //오리진 확인 test 필요 - // const sheet = XLSX.utils.json_to_sheet(data); - const startCol = sheetArray[index].startCol?sheetArray[index].startCol.toString():'A'; - XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol+sheetArray[index].headerNum }); - // XLSX.utils.book_append_sheet(wb, sheet, 'test'); - } else { - console.error(`Sheet "${sheetName}" is undefined.`); - } - } - }); - - - await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) - }); - } \ No newline at end of file +export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[]): Promise<JSON | undefined> => { + return new Promise<JSON | undefined>(async () => { + const wb = XLSX.read(oFile, { type: 'binary' }); + wb.SheetNames.forEach(function (sheetName, index) { + if (sheetArray[index]) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + const startCol = sheetArray[index].startCol ? sheetArray[index].startCol : 'A'; + XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol + sheetArray[index].headerNum }); + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) + }); +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/file-upload/index.tsx b/packages/common/business_components/src/ui/file-upload/index.tsx index a78965954..22ef56820 100644 --- a/packages/common/business_components/src/ui/file-upload/index.tsx +++ b/packages/common/business_components/src/ui/file-upload/index.tsx @@ -12,10 +12,12 @@ const dropZoneConfig = { files: File[]; // 파일 배열 setFiles: (files: File[] | null) => void; // 파일 배열 세터 fileTypes?: string; // 옵션 (파일 확장자 문자열) + onFileClick?:(index:number)=> void; // 파일 클릭 핸들러 + } export const FileUpload = -({ files, setFiles, fileTypes }: FileUploadProps) => { +({ files, setFiles, fileTypes ,onFileClick}: FileUploadProps) => { return ( <FileUploader value={files} @@ -43,9 +45,9 @@ export const FileUpload = {files&& files.length > 0 && files.map((file:File, i:number) => ( - <FileUploaderItem key={i} index={i}> + <FileUploaderItem key={i} index={i} onFileClick={onFileClick }> <Paperclip className="h-4 w-4 stroke-current" /> - <span>{file.name}</span> + <span className="w-4/5 overflow-hidden">{file.name}</span> </FileUploaderItem> ))} </FileUploaderContent> diff --git a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx index 3db4bcceb..96f150ee3 100644 --- a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx +++ b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx @@ -271,7 +271,7 @@ export const FileUploaderContent = forwardRef< {...props} ref={ref} className={cn( - "flex rounded-xl gap-1", + "w-full flex rounded-xl gap-1", orientation === "horizontal" ? "flex-raw flex-wrap" : "flex-col", className, )} @@ -286,31 +286,37 @@ FileUploaderContent.displayName = "FileUploaderContent"; export const FileUploaderItem = forwardRef< HTMLDivElement, - { index: number } & React.HTMLAttributes<HTMLDivElement> ->(({ className, index, children, ...props }, ref) => { - const { removeFileFromSet, activeIndex, direction } = useFileUpload(); + { index: number } & React.HTMLAttributes<HTMLDivElement> &{onFileClick?:(index:number)=>void} +>(({ className, index, children, onFileClick, ...props }, ref) => { + const { removeFileFromSet , activeIndex, direction } = useFileUpload(); const isSelected = index === activeIndex; return ( <div ref={ref} className={cn( buttonVariants({ variant: "ghost" }), - "h-6 p-1 justify-between cursor-pointer relative", + "h-6 p-1 justify-between cursor-pointer relative w-full", className, isSelected ? "bg-muted" : "", )} {...props} + > - <div className="font-medium leading-none tracking-tight flex items-center gap-1.5 h-full w-full"> + <div className="font-medium leading-none tracking-tight flex items-center gap-1.5 h-full !w-4/5 overflow-hidden " + onClick={() =>{onFileClick?.(index) + }}> {children} </div> <button type="button" className={cn( "absolute", + "!w-1/5 z-50", direction === "rtl" ? "top-1 left-1" : "top-1 right-1", + )} - onClick={() => removeFileFromSet(index)} + onClick={() =>{ + removeFileFromSet(index)}} > <span className="sr-only">remove item {index}</span> <RemoveIcon className="w-4 h-4 hover:stroke-destructive duration-200 ease-in-out" /> 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 bd9930c3c..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,19 +1,70 @@ -import React from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { FormControl, FormItem, FormLabel, FormMessage } from "../form"; +import { Button, Checkbox } from "@common/components/ui"; -const FormUi = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes<HTMLDivElement> & {label:string|undefined} ->(({children, label},ref)=>{ +export const BZNM_TOT_NO = '종사업자등록번호'; +export const CANDIDATE = '공급처 후보지 선택(필수아님)'; + +interface FormUiProps extends React.HTMLAttributes<HTMLDivElement> { + label?: string; + onCheckboxChange?: (checked: boolean) => void; + isDisabled?: boolean; +} + +interface FormElementProps extends React.HTMLAttributes<HTMLElement> { + disabled?: boolean; +} + +const FormUi = React.forwardRef<HTMLDivElement, FormUiProps>( + ({ children, label, onCheckboxChange, isDisabled }, ref) => { + const [isChecked, setIsChecked] = useState(isDisabled); + + useEffect(() => { + setIsChecked(isDisabled); + }, [isDisabled]); + + const handleCheckboxChange = useCallback((checked: boolean) => { + setIsChecked(checked); + onCheckboxChange?.(checked); + }, [onCheckboxChange]); return ( - <FormItem ref={ref}> - {label?<FormLabel className="text-xl">{label}</FormLabel>:<></>} - <FormControl> - {children} - </FormControl> - <FormMessage /> - </FormItem> - ) -}) + <FormItem ref={ref}> + {label && <FormLabel className="text-xl">{label}</FormLabel>} + {label === BZNM_TOT_NO && ( + <> + <div className="float-right"> + <div className="flex items-center gap-2"> + <Checkbox + id="check" + checked={isChecked} + onCheckedChange={handleCheckboxChange} + /> + <label className="w-20 text-xl text-gray-600" htmlFor="check">해당없음</label> + </div> + </div> + {children} + </> + )} + {label === CANDIDATE && ( + <FormControl> + {children} + </FormControl> + )} + {label !== BZNM_TOT_NO && label !== CANDIDATE && ( + <FormControl> + {React.isValidElement(children) && isDisabled + ? React.cloneElement(children, { + disabled: isDisabled, + ...(children.props as FormElementProps) + }) + : children} + </FormControl> + )} + <FormMessage /> + </FormItem> + ); + } +); + FormUi.displayName = 'FormUi'; -export { FormUi }; +export { FormUi }; \ No newline at end of file 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<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> + ) + )} + /> + ))} + </> + ); +} \ 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<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" /> + </> + ); +} \ 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<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)}  + <span className="text-gray-500">종사업자번호 : </span> {item.dmdInfoPk.substring(10,14)}  + </> + ) : ( + <> + <span className="text-gray-500">사업자번호 : </span> {item.dmdInfoPk.substring(0,10)}  + </> + )} + <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> + ) +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/search_base_form/index.tsx b/packages/common/business_components/src/ui/search_base_form/index.tsx index 9dc1bf735..b8d8355ae 100644 --- a/packages/common/business_components/src/ui/search_base_form/index.tsx +++ b/packages/common/business_components/src/ui/search_base_form/index.tsx @@ -1,3 +1,5 @@ +"use client" + import { Button } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import { SearchFormLeft, type EnvPartSelectDataType } from "../search_form_left"; diff --git a/packages/common/business_components/src/ui/search_form_left/index.tsx b/packages/common/business_components/src/ui/search_form_left/index.tsx index ec9e814f5..02112f084 100644 --- a/packages/common/business_components/src/ui/search_form_left/index.tsx +++ b/packages/common/business_components/src/ui/search_form_left/index.tsx @@ -1,4 +1,6 @@ -import React, { useEffect, useState } from 'react'; +"use client" + +import React, { useEffect, useState } from 'react'; import type { SelectDataType } from '../search_input_select'; import { SelectBox } from '../select'; diff --git a/packages/common/business_components/src/ui/tab/index.tsx b/packages/common/business_components/src/ui/tab/index.tsx index 4fb305830..aee969460 100644 --- a/packages/common/business_components/src/ui/tab/index.tsx +++ b/packages/common/business_components/src/ui/tab/index.tsx @@ -6,37 +6,41 @@ interface TabItem { label: string; } -interface UITabProps extends React.HTMLAttributes<HTMLDivElement> { +interface UITabProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'dir'> { defaultValue?: string; tabList: TabItem[]; children: React.ReactNode; onValueChange?: (value: string) => void; } -const UITab = React.forwardRef< +const UITab = React.forwardRef< HTMLDivElement, UITabProps ->(({defaultValue, tabList, children, onValueChange})=>{ - // Exclude 'dir' if it exists and is not of the correct type - return ( - <Tabs - defaultValue={defaultValue || tabList[0]?.value} - className={"w-full space-x-1 border-b border-gray-200"} - onValueChange={onValueChange} - > - <TabsList className="flex space-x-1"> - {tabList.map((tab) => ( - <TabsTrigger - key={tab.value} - value={tab.value} - className="py-2 px-4 text-sm font-medium border-b-2 focus:outline-none data-[state=active]:border-blue-500 data-[state=active]:text-blue-600 data-[state=inactive]:border-transparent data-[state=inactive]:text-gray-500 hover:text-gray-700 hover:border-gray-300" - > - {tab.label} - </TabsTrigger> - ))} - </TabsList> - {children} - </Tabs> - ) -}) +>(({defaultValue, tabList, children, onValueChange, ...props}, ref) => { + return ( + <Tabs + defaultValue={defaultValue || tabList[0]?.value} + className={"w-full space-x-1 border-b border-gray-200"} + onValueChange={onValueChange} + ref={ref} + {...props} + > + <TabsList className="flex space-x-1"> + {tabList.map((tab) => ( + <TabsTrigger + key={tab.value} + value={tab.value} + className="py-2 px-4 text-sm font-medium border-b-2 focus:outline-none data-[state=active]:border-blue-500 data-[state=active]:text-blue-600 data-[state=inactive]:border-transparent data-[state=inactive]:text-gray-500 hover:text-gray-700 hover:border-gray-300" + > + {tab.label} + </TabsTrigger> + ))} + </TabsList> + {children} + </Tabs> + ); +}); + +UITab.displayName = 'UITab'; + export { UITab }; diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index 791eddffe..48f4deb62 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -1,3 +1,5 @@ +"use client" + import { Table, TableBody, TableCell, TableHeader, TableRow } from "@common/components"; import { useEffect, useState } from "react"; import { HeadMakeColSpan } from "./HeadMakeColSpan"; diff --git a/packages/common/business_components/src/ui/table/lib/pagination.tsx b/packages/common/business_components/src/ui/table/lib/pagination.tsx index 2c17f4b45..76e3b4578 100644 --- a/packages/common/business_components/src/ui/table/lib/pagination.tsx +++ b/packages/common/business_components/src/ui/table/lib/pagination.tsx @@ -8,7 +8,7 @@ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( <nav role="navigation" aria-label="pagination" - className={cn("mx-auto flex w-full justify-center", className)} + className={cn("mx-auto flex w-full justify-center text-lg mt-2", className)} {...props} /> ) @@ -42,7 +42,7 @@ type PaginationLinkProps = { const PaginationLink = ({ className, isActive, - size = "icon", + size = "default", ...props }: PaginationLinkProps) => ( <a @@ -52,6 +52,7 @@ const PaginationLink = ({ variant: isActive ? "outline" : "ghost", size, }), + "px-4 py-2 text-lg", className )} {...props} @@ -66,10 +67,10 @@ const PaginationPrevious = ({ <PaginationLink aria-label="Go to previous page" size="default" - className={cn("gap-1 pl-2.5", className)} + className={cn("gap-2 pl-4 text-lg", className)} {...props} > - <ChevronLeft className="h-4 w-4" /> + <ChevronLeft className="h-6 w-6" /> <span>Previous</span> </PaginationLink> ) @@ -82,11 +83,11 @@ const PaginationNext = ({ <PaginationLink aria-label="Go to next page" size="default" - className={cn("gap-1 pr-2.5", className)} + className={cn("gap-2 pr-4 text-lg", className)} {...props} > <span>Next</span> - <ChevronRight className="h-4 w-4" /> + <ChevronRight className="h-6 w-6" /> </PaginationLink> ) PaginationNext.displayName = "PaginationNext" @@ -97,10 +98,10 @@ const PaginationEllipsis = ({ }: React.ComponentProps<"span">) => ( <span aria-hidden - className={cn("flex h-9 w-9 items-center justify-center", className)} + className={cn("flex h-12 w-12 items-center justify-center text-lg", className)} {...props} > - <MoreHorizontal className="h-4 w-4" /> + <MoreHorizontal className="h-6 w-6" /> <span className="sr-only">More pages</span> </span> ) diff --git a/packages/common/components/src/ui/input/index.tsx b/packages/common/components/src/ui/input/index.tsx index 11cbca71d..f3abe2d82 100644 --- a/packages/common/components/src/ui/input/index.tsx +++ b/packages/common/components/src/ui/input/index.tsx @@ -7,7 +7,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( return ( <input className={cn( - "flex h-[3.2rem] w-full rounded-md border border-input bg-form px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-placeholder focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + "flex h-[3.2rem] w-full rounded-md border border-input bg-form px-3 py-2 text-xl ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-placeholder focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-xl", className, )} ref={ref}