diff --git a/apps/cs/src/entities/loginlog/api/loginlog-service.ts b/apps/cs/src/entities/loginlog/api/loginlog-service.ts new file mode 100644 index 000000000..c115aebcb --- /dev/null +++ b/apps/cs/src/entities/loginlog/api/loginlog-service.ts @@ -0,0 +1,27 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + LoginlogInfo, + LoginlogList, + LoginlogParams, + LoginlogService, +} from "../model/loginlog-interface"; + +export class LoginlogApiService implements LoginlogService { + public async getLoginlogList(params: LoginlogParams): Promise { + const response = await client.get( + `${ENDPOINT.USER_SERVICE.LOGINLOG}`, + { params }, + ); + return response.data; + } + + public async getLoginlogInfo(lgnSn: string): Promise { + const response = await client.get( + `${ENDPOINT.USER_SERVICE.LOGINLOG}/${lgnSn}`, + ); + return response.data; + } +} + +export const LoginlogApi = new LoginlogApiService(); diff --git a/apps/cs/src/entities/loginlog/index.ts b/apps/cs/src/entities/loginlog/index.ts new file mode 100644 index 000000000..72032d92d --- /dev/null +++ b/apps/cs/src/entities/loginlog/index.ts @@ -0,0 +1,5 @@ +export { LoginlogApiService } from "./api/loginlog-service"; +export type { + Loginlog, + LoginlogParams, +} from "./model/loginlog-interface"; diff --git a/apps/cs/src/entities/loginlog/model/loginlog-interface.ts b/apps/cs/src/entities/loginlog/model/loginlog-interface.ts new file mode 100644 index 000000000..850b6ce5d --- /dev/null +++ b/apps/cs/src/entities/loginlog/model/loginlog-interface.ts @@ -0,0 +1,37 @@ +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface LoginlogParams extends PageableParams { + lgnSn?: string; + userId?: string; + siteId?: string; +} + +export interface Loginlog { + lgnSn: string; + lgnDt: string; + userId: string; + lgnIp: string; + lgnMethodCd: string; + errOcrnYn: string; + lgnErrCd: string; + siteId: string; +} + +export interface LoginlogInfo { + lgnSn: string; + lgnDt: string; + userId: string; + lgnIp: string; + lgnMethodCd: string; + errOcrnYn: string; + lgnErrCd: string; + siteId: string; +} + +export type LoginlogList = PageableList; + +export interface LoginlogService { + getLoginlogList: (params: LoginlogParams) => Promise; + getLoginlogInfo: (lgnSn: string) => Promise; + +} diff --git a/apps/cs/src/pages/loginlog/model/table-columns.tsx b/apps/cs/src/pages/loginlog/model/table-columns.tsx new file mode 100644 index 000000000..dbe19f044 --- /dev/null +++ b/apps/cs/src/pages/loginlog/model/table-columns.tsx @@ -0,0 +1,49 @@ +import dayjs from "dayjs"; +import type { ColumnDef } from "@tanstack/react-table"; +import type { Loginlog } from "@/entities/loginlog"; + + +export const columns: ColumnDef[] = [ + { + id: "no", + header: "NO", + cell: ({ row, table }) => { + const totalRows = table.getRowCount(); + const { pageIndex, pageSize } = table.getState().pagination; + return totalRows - (pageIndex * pageSize + row.index); + }, + }, + { + accessorKey: "lgnDt", + header: "등록일", + cell: ({ row }) => { + const date = dayjs(row.original.lgnDt); + return date.format("YYYY-MM-DD HH24:mi:ss"); + }, + }, + { + accessorKey: "userId", + header: "사용자ID", + }, + { + accessorKey: "로그인IP", + header: "remoteIp", + }, + { + accessorKey: "lgnMthd", + header: "접속방식", + }, + { + accessorKey: "오류여부", + header: "errOcrnYn", + }, + { + accessorKey: "오류코드", + header: "lgnErrCd", + }, + { + accessorKey: "사이트ID", + header: "siteId", + } +]; + diff --git a/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx b/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx new file mode 100644 index 000000000..598eacee2 --- /dev/null +++ b/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx @@ -0,0 +1,92 @@ +"use client"; + +import { useState } from "react"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; +import { + getCoreRowModel, + useReactTable, + type PaginationState, +} from "@tanstack/react-table"; +import { Button, DataTable, Input, Label } from "@common/components"; +import { LoginlogApi} from "../../../entities/loginlog/api/loginlog-service"; +import { type LoginlogParams} from "../../../entities/loginlog/model/loginlog-interface"; + +import { ENDPOINT } from "@/shared/config"; +import { columns } from "../model/table-columns"; + +type LoginlogPageProps = { + lgnSn?: string; +}; + +function LoginlogPage({ }: LoginlogPageProps) { + const [loginlogQuery, setLoginlogQuery] = useState({ + userId: "", + siteId: "", + }); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + + const { data: loginlogList, refetch } = useQuery({ + queryKey: [ENDPOINT.USER_SERVICE.LOGINLOG, , pagination], + queryFn: () => + LoginlogApi.getLoginlogList({ + ...loginlogQuery, + page: pagination.pageIndex, + size: pagination.pageSize, + }), + placeholderData: keepPreviousData, + }); + + const table = useReactTable({ + data: loginlogList?.content ?? [], + columns, + rowCount: loginlogList?.totalElements ?? 0, + state: { pagination }, + manualPagination: true, + enableMultiRowSelection: false, + getCoreRowModel: getCoreRowModel(), + onPaginationChange: setPagination, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + refetch(); + }; + + return ( +
+
+
+ + + setLoginlogQuery({ + ...loginlogQuery, + userId: e.target.value, + }) + } + /> + +
+
+
+
+

로그인로그 목록

+
+
+ +
+
+
+ ); +} + +export { LoginlogPage }; diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index e7ee5cbcc..ee168407e 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -19,6 +19,7 @@ export const ENDPOINT = { AUTHORIZATIONS: "/user-service/api/v1/authorizations", ROLE_AUTHORIZATIONS: "/user-service/api/v1/role-authorizations", ROLES: "/user-service/api/v1/roles", + LOGINLOG: "/user-service//api/v1/login-logs", }, CMS_SERVICE: { SITES: "/cms-service/api/v1/sites", @@ -45,7 +46,6 @@ export const ENDPOINT = { SURVEY_QUESTION: "/cms-service/api/v1/srvy-qitem", SURVEY_ARTICLE: "/cms-service/api/v1/srvy-artcl", ZIPS: "/cms-service/api/v1/zips", - LOGINLOG: "/cms-service/api/v1/loginlog", GROUPS: "/cms-service/api/v1/groups", GROUP_USERS: "/cms-service/api/v1/group-users", MANAGER_IP: "/cms-service/api/v1/mgrip", diff --git a/apps/fo/next-env.d.ts b/apps/fo/next-env.d.ts deleted file mode 100644 index 1b3be0840..000000000 --- a/apps/fo/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/wr/app/globals.css b/apps/wr/app/globals.css index f30aa3031..a439708d4 100644 --- a/apps/wr/app/globals.css +++ b/apps/wr/app/globals.css @@ -1,70 +1,10 @@ -@import "../../../packages/common/components/src/base.css"; +@import "@common/assets/styles/base.css"; +@import "@common/assets/styles/diveicon.css"; @tailwind base; -@tailwind components; @tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem - } - .dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55% - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} +html, +body { + font-family: "Noto Sans KR", sans-serif; + font-size: 10px; +} \ No newline at end of file diff --git a/apps/wr/app/layout.tsx b/apps/wr/app/layout.tsx index a0c14576d..cb930d6f7 100644 --- a/apps/wr/app/layout.tsx +++ b/apps/wr/app/layout.tsx @@ -1,6 +1,8 @@ +'use client' import { BaseLayout } from "@common/components"; import "./globals.css"; import { StoreProvider } from "./store"; +import { MainDiv } from "@common/business_components"; export default function RootLayout({ children, @@ -12,7 +14,9 @@ export default function RootLayout({ + {children} + diff --git a/apps/wr/app/loading.tsx b/apps/wr/app/loading.tsx new file mode 100644 index 000000000..80b268890 --- /dev/null +++ b/apps/wr/app/loading.tsx @@ -0,0 +1,4 @@ +export default function Loading() { + return
페이지 불러오는 중입니다...
; + } + \ No newline at end of file diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx new file mode 100644 index 000000000..c1e601320 --- /dev/null +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -0,0 +1,672 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + DmdCoReqSchema, + type DedMatUpdateType, + type DmdCoReqType, + type DmdMatTableType, +} from "@/app/store/matDmd/coMatRvw/type"; +import { formH1Class, formTableName } from "@/components/lib/class/comClass"; +import { + getCity, + getdmdMatFlag, + tableData, + yearOptions, +} from "@/components/lib/func/commom"; +import { + ContentsDiv, + Form, + FormField, + FormUi, + ScrollArea, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiModal, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, Input, Label } from "@common/components/ui"; +import { useEffect, useRef, useState } from "react"; +import { useForm } from "react-hook-form"; +import toast, { Toaster } from "react-hot-toast"; +import { z } from "zod"; +import moment from "moment"; +import { usageProcData } from "@/app/store/matDmd/coMatRvw/headList"; +import { zodResolver } from "@hookform/resolvers/zod"; +import ConCostForm from "@/components/ui/form/coMat/conCostForm/conCostForm"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; + +export default function CoMatRvw() { + const { code, coRvw, coMatRvwInit, coMatRvwSearch, coMatRvwInqua } = + useWrStore((state) => state); + const { coMatRvwRes } = coRvw; + //init, sub, match + const initRef = useRef(false); + const { coRvwHeadList } = coRvw; + const [pageNation, setPageNatiion] = useState(1); + + const [dmdCoNm, setDmdCoNm] = useState(""); + const [dmdMatData, setDmdMtaData] = useState([]); + const [selectSgg, setSelectSgg] = useState(); + const [objSearch, setObjSearch] = useState({ + sgg: "", + city: "", + }); + + const [fromDate, setFromDate] = useState( + String(new Date().getFullYear()) + ); + const [toDate, setToDate] = useState( + String(new Date().getFullYear()) + ); + const [selectData, setSelectData] = useState( + {} as DmdCoReqType + ); + + const getSearch = ( + startYear: number, + endYear: number, + sggCd: string, + dmdCoNm: string + ) => { + if (dmdCoNm.length > 0) { + coMatRvwSearch( + pageNation, + 13, + String(startYear), + String(endYear), + sggCd, + dmdCoNm + ); + } else { + coMatRvwInqua(pageNation, 13, String(startYear), String(endYear), sggCd); + } + }; + + const form = useForm>({ + resolver: zodResolver(DmdCoReqSchema), + }); + + const TableCellClick = (index: number) => { + console.log(coMatRvwRes); + if (coMatRvwRes && coMatRvwRes[index]) { + + setSelectData(coMatRvwRes[index]); + } + }; + useEffect(() => { + if (form?.getValues("useUsgCd")) { + form.setValue( + "demandCst.usgPrcsProcs", + usageProcData?.find( + (item) => item.usage === (form?.getValues("useUsgCd") as string) + )?.process ?? "" + ); + } + }, [form.watch("useUsgCd")]); + + useEffect(() => { + if (coMatRvwRes.length > 0) { + const data = coMatRvwRes?.map((item, index) => { + let flag = ""; + flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ??""); + return { + ...item, + ctpvCd: item.demandCo?.ctpvCd, + sggCd: item.demandCo?.sggCd, + dmdCoNm: item.demandCo?.dmdCoNm, + daddr: item.demandCo?.daddr, + telno: item.demandCo?.telno, + regDt: moment(item?.demandCo?.regDt).format("YYYY-MM-DD"), + splyFcltFlg: flag, + }; + }); + setDmdMtaData(tableData(data, { code })); + } + }, [coMatRvwRes]); + + 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) => { + setObjSearch({ ...objSearch, sgg: String(val) }); + }; + + useEffect(() => { + if (!initRef.current) { + coMatRvwInit(1, 13, fromDate, toDate); + initRef.current = true; + } + }, [coMatRvwInit, initRef, fromDate, toDate]); + + useEffect(() => { + if (selectData) { + console.log(selectData); + form.reset(selectData); + } + }, [selectData, form]); + return ( + <> + +
+
+ + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 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 ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + /> + <div className="col-span-7 w-full"> + <div className="flex flex-row items-center"> + <Label className="text-xl text-gray-600 mx-6"> + 하수 재이용년도 + </Label> + <SelectBox + className="h-full w-32 text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={fromDate} + onSelectValue={(val: string | number) => + setFromDate(String(val)) + } + /> + <span className="mx-6">~</span> + <SelectBox + className="h-full w-32 text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={toDate} + onSelectValue={(val: string | number) => + setToDate(String(val)) + } + /> + <div className="col-span-2 ml-6"> + <SearchInput + className={"text-2xl"} + label="회사명" + textValue={dmdCoNm} + setText={setDmdCoNm} + /> + </div> + </div> + </div> + </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) => { + if (fromDate && toDate) { + const fromNum = Number(fromDate); + const toNum = Number(toDate); + if (fromNum > toNum) { + toast.error( + "시작년도가 종료년도 이후로 설정할 수 없습니다." + ); + } else { + let con: string | number = ""; + if (String(objSearch.city).length > 0) { + con = String(objSearch.city); + } + if (String(objSearch.sgg).length > 0 && String(objSearch.sgg) !== "0") { + con = String(objSearch.sgg); + } + getSearch(fromNum, toNum, con, dmdCoNm); + } + } + }} + > + 조회 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 g ap-4"> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={"수요매칭검토현황"} + tableData={dmdMatData || []} + headlist={coRvwHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + if (index !== undefined && index >= 0) { + TableCellClick(index); + } + }} + onPageChange={() => {}} + ></UiTable> + </ListDiv> + <hr /> + <ScrollArea className="h-[49vh]"> + <ListDiv> + <h3 className="mt-4 ml-2 text-2xl font-medium text-label"> + 수요신청 상세현황 + </h3> + <Form {...form}> + <div className="grid grid-cols-12 gap-4 ml-4 mr-4"> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="demandCo.regDt" + render={({ field }) => ( + <FormUi label="요구시기"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(String(e.target.value)); + }} + value={ + moment(field.value).format("YYYY-MM-DD") ?? "" + } + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="dmndQty" + 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} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="useUsgCd" + render={({ field }) => ( + <FormUi label="사용용도"> + <Input + placeholder="" + type="text" + step="0.1" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="wntUntprc" + render={({ field }) => ( + <FormUi label="희망단가(원/m3)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-12 w-full"> + <h1 className={formH1Class}>요구수질</h1> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="grssClfg" + render={({ field }) => ( + <FormUi label="총대장균군수 (개/100ml)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="combResdch" + render={({ field }) => ( + <FormUi label="결합잔류염소(mg/L)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="trbt" + render={({ field }) => ( + <FormUi label="탁도(NTU)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="blgcOxygDmndQnt" + render={({ field }) => ( + <FormUi label="생물학적 산소요구량(BOD)(mg/L)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="smll" + render={({ field }) => ( + <FormUi label="냄새"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="crmty" + render={({ field }) => ( + <FormUi label="색도(도)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="tn" + render={({ field }) => ( + <FormUi label="총질소 (T-N)(mg/L)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="tph" + render={({ field }) => ( + <FormUi label="총인(T-P)(mg/L)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="ph" + render={({ field }) => ( + <FormUi label="수소이온농도(pH)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="clIonQnt" + render={({ field }) => ( + <FormUi label="염화물(mgCL/L)"> + <Input + placeholder="" + type="number" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="cdtnv" + render={({ field }) => ( + <FormUi label="전기전도도(㎲/cm)"> + <Input + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="rmrk" + render={({ field }) => ( + <FormUi label="비고"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-12 w-full"> + <h1 className={formH1Class}>공급처 후보지</h1> + </div> + <div className="col-span-4 mt-4"> + <FormField + control={form.control} + name={`demandMatching.splyCndtList.${0}`} + render={({ field }) => ( + <FormUi label="후보지1"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(String(e.target.value)); + }} + value={field.value ?? ""} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4 mt-4"> + <FormField + control={form.control} + name={`demandMatching.splyCndtList.${1}`} + render={({ field }) => ( + <FormUi label="후보지2"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4 mt-4"> + <FormField + control={form.control} + name={`demandMatching.splyCndtList.${2}`} + render={({ field }) => ( + <FormUi label="후보지3"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + readOnly + /> + </FormUi> + )} + /> + </div> + </div> + </Form> + <ConCostForm form={form} type={"mat"} /> + </ListDiv> + </ScrollArea> + </div> + </div> + </ContentsDiv> + <Toaster position="top-center" /> + </> + ); +} diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx new file mode 100644 index 000000000..e8146b37a --- /dev/null +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -0,0 +1,126 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList"; +import { getCity } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useState } from "react"; + +export default function RuseCaseMng() { + const { code,ruseCase} = useWrStore((state) => state); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); + const [selectUtz, setSelectUtz] = useState<string[] | []>() + const [searchTxt, setSearchTxt] = useState<string>() + + const getSelCity = (val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: '' }); + }; + + const getSgg = (val: string | number) => { + setObjSearch({ ...objSearch, sgg: String(val) }) + }; + 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 + className="col-span-2 w-1/3 h-full text-2xl" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + /> + <SelectBox + className="col-span-2 w-1/3 h-full text-2xl" + label="용도" + selectClass={""} + selectArray={ruseUtzType} + onSelectValue={(val) => { + if(val) + setSelectUtz([String(val)])}} + /> + + <SearchInput + className={"text-2xl"} + label="수요처명" + textValue={""} + 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={(e) => { }} + > + 엑셀등록 + </Button> + <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 className="overflow-x-auto"> + <UiTable + headName={"물재이용사례 현황"} + tableData={[]} + children={undefined} + headlist={ruseCase.ruseHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + }} + onPageChange={() => { }} + ></UiTable> + </ListDiv> + </div> + </div> + </ContentsDiv> + </> + ) +} \ No newline at end of file diff --git a/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx b/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx new file mode 100644 index 000000000..2d339b869 --- /dev/null +++ b/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx @@ -0,0 +1,338 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + DmdCoReqSchema, + type DmdCoReqType, + type RvwHistTableType, +} from "@/app/store/matDmd/coMatRvw/type"; +import { + getCity, + getdmdMatFlag, + yearOptions, +} from "@/components/lib/func/commom"; +import ConCostForm from "@/components/ui/form/coMat/conCostForm/conCostForm"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, DatePicker, Input, Label } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import moment from "moment"; +import { useCallback, useEffect, useState } from "react"; +import { set, useFieldArray, useForm } from "react-hook-form"; +import { z } from "zod"; +import { tableData } from "@/components/lib/func/commom"; +import toast, { Toaster } from "react-hot-toast"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +export default function RuseRvwHstry() { + const { + code, + ruseHist, + ruseRvwHistInit, + ruseRvwSearch, + ruseRvwInqua, + ruseRvwComplete, + } = useWrStore((state) => state); + const { rvwHistRes, ruseHistHeadList } = ruseHist; + const [pageNation, setPageNatiion] = useState<number>(1); + + const [dmdCoNm, setDmdCoNm] = useState<string>(""); + const [ruseUtztnFcltNm, setRuseUtztnFcltNm] = useState<string>(""); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const [fromDate, setFromDate] = useState<string>( + String(new Date().getFullYear()) + ); + const [toDate, setToDate] = useState<string>( + String(new Date().getFullYear()) + ); + const [rvwHistData, setRvwHistData] = useState<RvwHistTableType[]>(); + + 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: "0" }); + }; + + const getSgg = (val: string | number) => { + console.log(val); + setObjSearch({ ...objSearch, sgg: String(val) }); + }; + + const getSearch = ( + startYear: number, + endYear: number, + sggCd: string, + dmdCoNm: string, + ruseUtztnFcltNm: string + ) => { + if (dmdCoNm || ruseUtztnFcltNm) { + ruseRvwSearch( + pageNation, + 13, + String(startYear), + String(endYear), + sggCd, + dmdCoNm, + ruseUtztnFcltNm + ); + } else { + + + ruseRvwInqua(pageNation, 13, String(startYear), String(endYear), sggCd); + } + }; + + const form = useForm<DmdCoReqType>({ + resolver: zodResolver(DmdCoReqSchema), + mode: "onChange", + }); + + const TableCellClick = useCallback( + (index: number | undefined) => { + if ( + index !== undefined && + index >= 0 && + rvwHistRes && + rvwHistRes[index] + ) { + form.reset(rvwHistRes[index]); + } + }, + [rvwHistRes, form] + ); + const confrirmToast = () => { + const completeData = rvwHistData + ?.map((item) => { + form.clearErrors; + if (item?.completeYn) { + return Number(item.dmdInfoSn); + } + }) + .filter((item) => item !== undefined); + if (form.formState.isDirty) { + toast((t) => ( + <span className="flex flex-col items-center"> + 표준공사비 정보가 변경되었습니다. 검토 완료 진행시 입력한 정보는 + 초기화 됩니다. + <br /> + 진행하시겠습니까? + <button + onClick={() => { + toast.dismiss(t.id); + ruseRvwComplete(completeData as Number[]); + }} + > + 확인 + </button> + <button onClick={() => toast.dismiss(t.id)}>취소</button> + </span> + )); + } else { + ruseRvwComplete(completeData as Number[]); + } + }; + useEffect(() => { + if (!ruseHist.isInit) { + ruseRvwHistInit( + 1, + 13, + new Date().getFullYear().toString(), + new Date().getFullYear().toString() + ); + } + }, [ruseHist.isInit]); + + useEffect(() => { + if (rvwHistRes) { + const data = rvwHistRes?.map((item, index) => { + let flag = ""; + flag = getdmdMatFlag(item.demandMatching?.splyFcltFlg ?? ""); + + return { + ...item, + ctpvCd: item.demandCo?.ctpvCd, + sggCd: item.demandCo?.sggCd, + dmdCoNm: item.demandCo?.dmdCoNm, + daddr: item.demandCo?.daddr, + telno: item.demandCo?.telno, + regDt: item?.demandCo?.regDt + ? moment(item?.demandCo?.regDt).format("YYYY-MM-DD") + : "없음", + rvwCmptnYmd: item?.rvwCmptnYmd + ? moment(item?.rvwCmptnYmd).format("YYYY-MM-DD") + : "없음", + mtchnCmptnYmd: item?.mtchnCmptnYmd + ? moment(item?.mtchnCmptnYmd).format("YYYY-MM-DD") + : "없음", + ruseUtztnFcltNm: item.ruseUtztnFcltNm, + dmndQty: item.dmndQty, + splyFcltFlg: flag, + completeYn: false, + } as unknown as RvwHistTableType; + }); + + setRvwHistData(tableData(data, { code })); + } + }, [rvwHistRes]); + + + useEffect(() => { + console.log("변경여부 ", form.formState.isDirty); + }, [form.formState]); + + return ( + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"재이용검토이력"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="w-80 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={getSelCity} + > + {""} + </SelectBox> + <SelectBox + className="w-80 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + > + {""} + </SelectBox> + <div className="col-span-4 w-full"> + <div className="flex flex-row items-center"> + <Label className="text-xl text-gray-600 ml-4 mr-4"> + 수요신청년도 + </Label> + <div className="flex flex-row mt-2"> + <SelectBox + className="h-full w-32 text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={fromDate} + onSelectValue={(val: string | number) => + setFromDate(String(val)) + } + /> + <span className="mx-6">~</span> + <SelectBox + className="h-full w-32 text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={toDate} + onSelectValue={(val: string | number) => + setToDate(String(val)) + } + /> + <SearchInput + className={"text-2xl w-60 ml-4 "} + label="시설명" + textValue={ruseUtztnFcltNm} + setText={setRuseUtztnFcltNm} + /> + <SearchInput + className={"text-2xl w-60 ml-4 "} + label="회사명" + textValue={dmdCoNm} + setText={setDmdCoNm} + /> + </div> + </div> + </div> + </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) => { + if (fromDate && toDate) { + const fromNum = Number(fromDate); + const toNum = Number(toDate); + if (fromNum > toNum) { + toast.error( + "시작년도가 종료년도 이후로 설정할 수 없습니다." + ); + } else { + let con: string | number = ""; + if (String(objSearch.city).length > 0) { + con = String(objSearch.city); + } + if (String(objSearch.sgg).length > 0 && objSearch.sgg !== "0") { + con = String(objSearch.sgg); + } + getSearch( + fromNum, + toNum, + con, + dmdCoNm, + ruseUtztnFcltNm + ); + } + } + }} + > + 조회 + </Button> + <Button size={"sm"} onClick={confrirmToast}> + 검토완료 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={"재이용검토현황"} + tableData={rvwHistData || []} + headlist={ruseHistHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + TableCellClick(index); + }} + onPageChange={setPageNatiion} + ></UiTable> + <hr /> + <h3 className="mt-4 ml-2 text-2xl font-medium text-label"> + 수요신청 상세현황 + </h3> + <ConCostForm form={form} type={"hist"} /> + </ListDiv> + </div> + </div> + <Toaster position="top-center" /> + </ContentsDiv> + ); +} diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx new file mode 100644 index 000000000..163195756 --- /dev/null +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -0,0 +1,263 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { fetchType, getFetch } from "@/components/lib/api"; +import { + excelDownload, + getCity, + tableData +} from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiModal, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import SdmInsertForm from "@common/business_components/ui/sdmInsertForm"; +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +import toast, { Toaster } from "react-hot-toast"; + +export default function CoSttn() { + const { + code, + coSttn, + coSttnInit, + coSttnSearch, + getDemandCompList, + regDemandCompany, + updateDemandCompany, + coSttnTxtSearch, + demandCompList, + dmdIsSucees + } = useWrStore((state) => state); + + const [bdstTable, setBdstTable] = useState<DemandCompType[]>(); + const [pageNation, setPageNation] = useState(1); + const { coSttnRes, coSttnResCount } = coSttn; + const [isDmdModal, setIsDmdModal] = useState<{ + isMoal: boolean; + data: DemandCompType; + }>({ isMoal: false, data: {} as DemandCompType }); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const [seartchTxt, setSearchTxt] = useState<string>(); + const [upadateData, setUpdateData] = useState<DemandCompType>(); + + 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) => { + setObjSearch({ ...objSearch, sgg: String(val) }); + }; + + useEffect(() => { + if (!coSttn.isInit) { + coSttnInit("1", "13", "0"); + } + }, [coSttn.isInit, coSttnInit]); + + useEffect(() => { + if (coSttnRes?.length > 0 && code.cityCd.length > 0) { + const data = tableData(coSttnRes, { code }); + const chData = data.map((item: any) => ({ + ...item, + })); + // .filter((item: any) => item !== undefined); + setBdstTable(chData); + } else if (coSttnRes.length == 0) { + setBdstTable([]); + } + }, [coSttnRes, code]); + + useEffect(() => { + if (coSttn.excelList.length > 0) { + if (coSttn.excelList.length === 0) return; + excelDownload( + coSttn.excelList, + "수요처 관리", + "sample_coSttn.xlsx", + "A1:F1", + 2 + ); + } + }, [coSttn.excelList]); + + useEffect(() => { + const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city ? objSearch.city : "0"; + if(con){ + coSttnSearch(String(pageNation), "13", String(con)); + } + }, [coSttnSearch, objSearch.city, objSearch.sgg, pageNation]); + + const excelClick = useCallback(() => { + const con = objSearch.sgg ? objSearch.sgg : objSearch.city; + if (con) coSttnSearch("1", "10000", String(con)); + }, [coSttnSearch, objSearch.city, objSearch.sgg]); + + const searchClick = () => { + const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city; + if (con) { + if (seartchTxt && seartchTxt?.length > 0) { + coSttnTxtSearch("1", "13", String(con), seartchTxt ?? ""); + } else { + coSttnSearch("1", "13", String(con)); + } + } + }; + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"수요처 관리"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl" + label="시군구" + selectClass={""} + selectArray={selectSgg?selectSgg : [{text:"전체",val:"0"}]} + onSelectValue={getSgg} + /> + <SearchInput + className={"!text-3xl"} + label="회사명" + textValue={seartchTxt ?? ""} + 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="flex flex-row space-x-2"> + <Button size={"sm"} onClick={searchClick}> + 조회 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => { + + setUpdateData(undefined); + setIsDmdModal({ ...isDmdModal, isMoal: true }); + }} + > + 등록 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => { + /* 저장 로직 */ + if (!upadateData) { + <ToastFormUi body={"수요처 변경을 원하는 행을 선택해주세요"} title={"수요처관리"}></ToastFormUi>; + }else{ + setIsDmdModal({ + ...isDmdModal, + isMoal: true}) + } + }} + > + 저장 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={bdstTable || []} + headlist={coSttn.coSttHeadList} + pageSize={bdstTable?.length ? Math.ceil(bdstTable.length / 13) : 0} + total={ coSttnResCount || 0 } + cellClick={(index: number | undefined, headId: string) => { + setUpdateData(coSttnRes[index as number]); + }} + onPageChange={setPageNation} + ></UiTable> + </ListDiv> + </div> + </div> + <UiModal + bodyClass={"w-[40vw] max-w-none h-[90vh]"} + open={isDmdModal.isMoal} + onOpenChange={() => { + setIsDmdModal({ isMoal: false, data: {} as DemandCompType }); + }} + body={ + <SdmInsertForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + dataList={demandCompList} + rtnValue={(searchText: string, pageCnt: number) => { + getDemandCompList(searchText, pageCnt); + }} + rtnInputData={ async(data) => { + if(upadateData?.dmdInfoPk){ + updateDemandCompany(data as DemandCompType) + }else{ + const dmd= await getFetch(fetchType.WR,`/reuseFc/request/${data.dmdInfoPk}`) + if(dmd) { + toast.error(`사업자번호 ${dmd.dmdInfoPk}는 이미 등록되었습니다. 다시등록해주세요`) + return + } + regDemandCompany(data as DemandCompType); + } + if(dmdIsSucees ==='success'){ + toast.success(`수요처 등록 및 수정 완료되었습니다..`) + setIsDmdModal({ isMoal: false, data: data as DemandCompType }); + }else{ + toast.error(`수요처 등록 및 수정 에러입니다.`) + } + + }} + bodyClassNm={"w-full mx-auto bg-white rounded-lg shadow-md p-6"} + updateList={upadateData} + ></SdmInsertForm> + } + ></UiModal> + </ContentsDiv> + <Toaster position="top-center" /> + </> + ); +} diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx new file mode 100644 index 000000000..77bbe4a5a --- /dev/null +++ b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx @@ -0,0 +1,132 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import type { customCoSttnBdst } from "@/app/store/matDmd/coSttn"; +import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList"; +import { excelDownload, getCity } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, DatePicker, Label } from "@common/components/ui"; +import { useCallback, useState } from "react"; + +export default function DrgtSttn() { + const { code, } = useWrStore((state) => state); + const [fromDate, setFromDate] = useState<Date>(new Date()); + const [toDate, setToDate] = useState<Date>(new Date()); + 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 excelClick = useCallback(() => { + excelDownload( + [[]], + "우리동네가뭄", + "sample_drgtSttn.xlsx", + "A1:H1" + ); + }, []); + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"우리동네가뭄"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + /> + <div className="col-span-5 w-auto"> + <div className="flex flex-row items-center"> + <Label className="text-xl text-gray-600">조회기간</Label> + <div className="flex flex-row w-1/4"> + <DatePicker mode={'single'} + selected={fromDate} + onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} /> + <span>~</span> + <DatePicker mode={'single'} + selected={toDate} + onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} /> + </div> + </div> + </div> + </div> + </SearchFormLeft> + <SearchFormRight + className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + onClick={(e) => { }} + > + 조회 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => { }} + > + 상세보기 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => { }} + > + 지도보기 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={[]} + headlist={drgtHeadListExport} + pageSize={20} + total={10000} + cellClick={(index: number | undefined, headId: string) => { }} + onPageChange={() => { }} + ></UiTable> + </ListDiv> + </div> + </div> + </ContentsDiv> + </> + ) +} \ No newline at end of file diff --git a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx new file mode 100644 index 000000000..f5dffbf8b --- /dev/null +++ b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx @@ -0,0 +1,293 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { type customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn"; +import { ldrmntHeadListExport, ldrmntNodeListExport } from "@/app/store/matDmd/ldrmntCoSttn/headList"; +import { + excelDownload, + getCity, + getCityOrSgg, + tableData +} from "@/components/lib/func/commom"; +import CoRegExcel from "@/components/ui/modal/coReg/coRegExcel/coRegExcelModal"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import CoDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/coDatailModal"; +import { + ContentsDiv, + downloadExcel, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiModal, + UiTable, + type ReturnType, + type SelectDataType +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; + +export default function LdrmntCoSttn() { + const { code, ldrmnt, ldrmntInit, ldrmntSearch, } = useWrStore( + (state) => state + ); + const { ldrmntRes, excelList } = ldrmnt; + const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customLdrmnt | undefined }>({ data: undefined, isOpen: false }) + const [pageNation, setPageNation] = useState(1); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); + const [bdstTable, setBdstTable] = useState<customLdrmnt[]>(); + const [searchTxt, setSearchTxt] = useState<string>(); + const ref = useRef<ReturnType>({} as ReturnType); + const [excelData, setExcelData] = useState<any[]>(); + + + const excelClick = useCallback(() => { + excelDownload( + [[]], + "잠재수요처 관리", + "sample_ldrmntCoSttn.xlsx", + "A1:AL1", + 2 + ); + }, []); + + const excelDownloadClick = () => { + downloadExcel({ + data: excelData ?? [], + fileName: "잠재수요처 관리", + sheetNameArray: ["Sheet1"], + headerRange: "A1:F1" + }) + } + + const [isExcelOpen, setIsExcelOpen] = useState<{ + isModal: boolean; + }>({ isModal: false }); + + const cellClick = (index: number | undefined, headId: string) => { + if (index !== undefined) { + setDetailModal({ isOpen: true, data: ldrmntRes[index] }); + } + + } + + 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) => { + setObjSearch({ ...objSearch, sgg: String(val) }) + }; + + useEffect(() => { + if (!ldrmnt.isInit) { + ldrmntInit("0"); + } + }, [ldrmnt.isInit, ldrmntInit]); + + useEffect(() => { + const transData = Array.isArray(ldrmntRes) ? ldrmntRes : [ldrmntRes]; + const data = tableData(transData, { code }); + const tbData = Array.isArray(data) + ? data.map((item: customLdrmnt) => ({ + ...item, + ctpvCd: item.fctryAddr.split(" ")[0] || "", + sggCd: item.fctryAddr.split(" ")[1] || "" + + })) + : []; + + setBdstTable(tbData); + }, [ldrmntRes, code]); + + useEffect(() => { + const transData = Array.isArray(excelList) ? excelList : [excelList]; + const exData = tableData(transData, { code }); + const data = Array.isArray(exData) + ? exData.map((item: customLdrmnt) => { + return { + "시도": item.fctryAddr.split(" ")[0] || "", + "시군구": item.fctryAddr.split(" ")[1] || "", + "회사명": item.dmdCoNm, + "공장주소": item.fctryAddr, + "공장관리번호": item.fctryMngNo, + "전화번호": item.telno + } + }) : []; + setExcelData(data) + }, [excelList]) + + + useEffect(() => { + let cityTxt = ''; + let sggTxt = ''; + + if (objSearch?.city.toString().length > 0) { + cityTxt = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? ''; + } + + if (objSearch?.sgg.toString().length > 1) { + sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? ''; + } + + let cityNm = ""; + + if (sggTxt !== "0") { + cityNm = cityTxt + " " + sggTxt; + } else { + cityNm = cityTxt; + } + + if (cityNm) { + if (cityTxt === '') cityNm = "0"; + ldrmntSearch(pageNation, cityNm, searchTxt || ""); + } else { + ldrmntSearch(pageNation, "", searchTxt || ""); + } + }, [pageNation, objSearch, code, searchTxt, ldrmntSearch]) + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"잠재수요처 관리"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + /> + <SearchInput + className={"text-2xl ml-4"} + 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="flex flex-row space-x-2"> + <Button + size={"sm"} + onClick={(e) => { + + let cityTxt = '' + let sggTxt = '' + + if (objSearch?.city.toString().length > 0) { + cityTxt = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? ''; + } + + if (objSearch?.sgg.toString().length > 1) { + sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? ''; + } + + let cityNm = "" + + if (sggTxt !== "0") { + cityNm = cityTxt + " " + sggTxt + } else { + cityNm = cityTxt + } + + if (cityNm) { + if (cityTxt === '') cityNm = "0"; + ldrmntSearch(pageNation, cityNm, searchTxt || ""); + } + }} + > + 조회 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => { + setIsExcelOpen({ isModal: true }); + }} + > + 엑셀등록 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => excelDownloadClick()} + > + 다운로드 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 양식받기 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <div className="overflow-x-auto"> + <ListDiv className="min-w-max"> + <UiTable + headName={[]} + tableData={bdstTable || []} + headlist={ldrmntHeadListExport} + pageSize={20} + total={10000} + cellClick={(index: number | undefined, headId: string) => { cellClick(index ?? undefined, headId) }} + onPageChange={setPageNation} + /> + </ListDiv> + </div> + </div> + <UiModal + open={detailModal.isOpen} + onOpenChange={() => { + setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen }) + }} + bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"} + body={<CoDetailModal formObj={detailModal.data} />} + /> + + <UiModal + open={isExcelOpen.isModal} + onOpenChange={() => { + setIsExcelOpen({ isModal: !isExcelOpen.isModal }) + }} + bodyClass={"w-[40vw] max-w-none"} + body={<CoRegExcel />} + /> + + </div> + + </ContentsDiv> + </> + + ) + +} \ No newline at end of file diff --git a/apps/wr/app/matMng/matDmd/coReg/page.tsx b/apps/wr/app/matMng/matDmd/coReg/page.tsx deleted file mode 100644 index 0c6ceccaa..000000000 --- a/apps/wr/app/matMng/matDmd/coReg/page.tsx +++ /dev/null @@ -1,84 +0,0 @@ -"use client"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import * as z from "zod"; -import { - Form, - FormField, - FormUi, - ScrollArea, - SelectBox, -} from "@common/business_components/ui"; -import { Button } from "@common/components/ui"; -import { CoRegForm } from "@/components/ui/matDmd/coRegForm/component"; -import toast, { Toaster } from "react-hot-toast"; -import { ToastFormUi } from "@/components/toastFromUi"; - -const formSchema = z.object({ - ctpv: z.string(), - sgg: z.string(), - coNm: z.string().min(1).max(20), - bznmNo: z.string().min(1).min(9).max(12), - addr: z.string().min(1).min(6).max(20), - telNo: z.string().min(8).max(12), - -}); - -export default function MyForm() { - - - - const form = useForm<z.infer<typeof formSchema>>({ - resolver: zodResolver(formSchema), - defaultValues: { - coNm:'', - bznmNo:'', - addr:'', - telNo:'', - }, - }); - - function onSubmit(values: z.infer<typeof formSchema>) { - try { - toast.dismiss(); - ToastFormUi("수요신청 화면으로 이동하시겠습니까?", "수요처 등록 완료", "./dmdReg", "이동") - - } catch (error) { - console.error("Form submission error", error); - toast.error("수요처 등록이 실패하였습니다."); - } - } - - return ( - <> - <div className="grid h-40 grid-cols-3 place-content-center"> - <h1 className=" col-start-2 col-span-1 text-4xl underline"> - 수요처 정보입력 - </h1> - </div> - - <div className="grid grid-cols-5 gap-4 overflow-hidden"> - <div className="col-span-1 "/> - <div className="col-span-3 "> - <div> - <Form {...form}> - <form - onSubmit={form.handleSubmit(onSubmit)} - className="space-y-8 max-w-3xl mx-auto" - > - <CoRegForm>{form}</CoRegForm> - - - <Button type="submit">수요처등록</Button> - </form> - </Form> - </div> - </div> - <div className="col-span-1 " > - <Toaster position="top-center" /> - </div> - </div> - </> - ); -} - diff --git a/apps/wr/app/matMng/matDmd/dmdReg/page.tsx b/apps/wr/app/matMng/matDmd/dmdReg/page.tsx deleted file mode 100644 index d405d45a6..000000000 --- a/apps/wr/app/matMng/matDmd/dmdReg/page.tsx +++ /dev/null @@ -1,106 +0,0 @@ -"use client"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import * as z from "zod"; -import { Form } from "@common/business_components/ui"; -import { Button } from "@common/components/ui"; -import { DmdRegForm } from "@/components/ui/matDmd/dmdRegform/component"; -import toast, { Toaster } from "react-hot-toast"; -import { useRouter } from "next/navigation"; - -const formSchema = z.object({ - dmdSel: z.string().min(1), - dmdTmng: z.coerce.date(), - dmdWq: z.string().min(1).optional(), - useUsgCd: z.string(), - wntUntprc: z.string().min(1), - grssClfg: z.string().min(1), - tn: z.string().min(1).optional(), - combResdch: z.string().min(1).optional(), - tp: z.string().min(1).optional(), - trbt: z.string().min(1).optional(), - ph: z.string().min(1).optional(), - bod: z.string().min(1).optional(), - clIonQnt: z.string().min(1).optional(), - smll: z.string().min(1).optional(), - cdtnv: z.string().min(1).optional(), - crmty: z.string().min(1).optional(), - rmrk: z.string().min(1).optional(), - sply1: z.string().min(1).optional(), - sply2: z.string().min(1).optional(), - sply3: z.string().min(1).optional(), - password: z.string().min(1), -}); - -export default function MyForm() { - const form = useForm<z.infer<typeof formSchema>>({ - resolver: zodResolver(formSchema), - defaultValues: { - dmdSel:'', - dmdTmng: new Date(), - dmdWq: "", - useUsgCd: "", - wntUntprc: "", - grssClfg: "", - tn: "", - combResdch: "", - tp: "", - trbt: "", - ph: "", - bod: "", - clIonQnt: "", - smll: "", - cdtnv: "", - crmty: "", - rmrk: "", - sply1: "", - sply2: "", - sply3: "", - password: "", - }, - }); - - const router = useRouter(); - - async function onSubmit(values: z.infer<typeof formSchema>) { - try { - - toast.success("✅ 수요신청이 완료되었습니다."); - await new Promise((resolve) => setTimeout(resolve, 3000)); - router.push("../../") - } catch (error) { - toast.error("수요신청을 다시입력해주시길 바랍니다."); - } - } - - return ( - <> - <div className="grid h-40 grid-cols-3 place-content-center"> - <h1 className=" col-start-2 col-span-1 text-4xl underline"> - 수요 신청정보입력 - </h1> - </div> - - <div className="grid grid-cols-5 gap-4 overflow-hidden"> - <div className="col-span-1 " /> - <div className="col-span-3 max-h-[78vh] overflow-y-scroll "> - <div> - <Form {...form}> - <form - onSubmit={form.handleSubmit(onSubmit)} - className="space-y-8 max-w-3xl mx-auto" - > - <DmdRegForm> {form}</DmdRegForm> - - <Button type="submit"> 수요신청</Button> - </form> - </Form> - </div> - </div> - <div className="col-span-1 "> - <Toaster position="top-center" /> - </div> - </div> - </> - ); -} diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx index 356cfe8c3..4c6c58faf 100644 --- a/apps/wr/app/page.tsx +++ b/apps/wr/app/page.tsx @@ -1,26 +1,159 @@ -import Link from 'next/link'; -import { Button } from '@common/components/ui'; - - +"use client"; +import { Button } from "@common/components/ui"; +import { useRouter } from "next/navigation"; export default function Home() { + const router = useRouter(); return ( <> - <div className='flex'> - <Button className='place-self-auto'> - <Link href="reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert"> 하수재이용 시설 등록</Link> - </Button> - <Button className='place-self-auto'> - <Link href="reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert"> 빗물재이용 시설 등록</Link> - </Button> - <Button className='place-self-auto'> - <Link href="reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert"> 중수도시설 등록</Link> - </Button> + <div className="flex flex-col gap-8 p-6"> + {/* 시설 등록 */} + <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" onClick={() => {router.push('/ruseFcltMng/fcltReg/pbnstReg')}}> + 하수재이용 시설 등록 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/rainReg')}}> + 빗물재이용 시설 등록 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/rswtReg')}}> + 중수도시설 등록 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/htdflReg')}}> + 온배수시설 등록 + </Button> + </div> + </div> + </div> - <Button className='place-self-auto'> - <Link href="matMng/matDmd/coReg">수요처 등록</Link> - </Button> + {/* 지도점검 결과 */} + <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" onClick={() => {router.push('/ruseFcltMng/prfmncReg/pbnstPrfmncReg')}}> + 하수재이용실적 등록 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn')}}> + 하수재이용실적 현황 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncReg/chkgdnReg')}}> + 지도점검 결과 등록 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncSttn/chkgdnSttn')}}> + 지도점검 결과 현황 + </Button> + </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" onClick={() => {router.push('/ruseFcltMng/fcltSttn/pbnstSttn')}}> + 하수재이용시설현황 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/rainSttn')}}> + 빗물재이용시설현황 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/rswtSttn')}}> + 중수도이용시설현황 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/htdflSttn')}}> + 온배수현황 + </Button> + </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" onClick={() => {router.push('/matMng/coMng/coSttn')}}> + 수요처 관리 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/matMng/coMng/ldrmntCoSttn')}}> + 잠재수요처 관리 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/matMng/coMng/drgtSttn')}}> + 우리동네가뭄 + </Button> + </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" onClick={() => {router.push('/matMng/coMat/coMatRvw')}}> + 수요처 매칭 검토 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/matMng/coMat/ruseRvwHstry')}}> + 재이용검토이력 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/matMng/coMat/ruseCaseMng')}}> + 물재이용사례 관리 + </Button> + </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" onClick={() => {router.push('/stats/rainStat')}}> + 빗물재이용시설통계 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/stats/rswtStat')}}> + 중수도시설통계 + </Button> + </div> + <div className="col-span-2"> + <Button className="w-full" onClick={() => {router.push('/stats/matStat')}}> + 물재이용사례 관리 + </Button> + </div> + </div> + </div> </div> - </> + </> ); } diff --git a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert/page.tsx b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert/page.tsx deleted file mode 100644 index 57c48a84d..000000000 --- a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -'use client' - -import { ScrollArea, SearchDiv, SelectBox, UiTable } from '@common/business_components'; -import { Button } from '@common/components'; -import { PublicReuseFaciltyFormInsert } from '../../../../components/ui/publicReuseInsertForm'; -import { useWrStore } from '../../../store'; - - - - export default function PublicReuseFaciltyInsert() { - const {pubFac} = useWrStore((state) => state); - const { pubFacHeadList, publicReuseFacilityData} = pubFac; - const searchDiv = '하수재이용시설 ' - const selectBox = [{ text: '서울하수처리시설', val: "1_hasu" }, { text: '서울1하수처리시설', val: "2_hasu" }, { text: '서울2하수처리시설', val: "3_hasu" }] - const headName = () => { - return ( - <h1 className="pl-3 text-xl">○공공 하수처리시설 등록</h1> - ) - } - const onSelectValue = (val: string) => { - console.log(val) - } - return ( - <> - <div className="grid grid-cols-2 gap-4 h-full"> - <div className='col-span-4'> - <h1 className="pl-3 text-xl" > 하수재이용시설 등록</h1> - <div className="m-8"> - <SearchDiv> - <div className='w-4/5 grid grid-cols-12 gap-1 mx-4'> - <SelectBox className='col-span-4' label={searchDiv} selectArray={selectBox} onSelectValue={onSelectValue} /> - </div> - <div className='w-1/5 flex items-center justify-end'> - <Button className='mr-4 rounded-lg'>조회</Button> - <Button className='mr-4 rounded-lg'>신규추가</Button> - </div> - </SearchDiv> - </div> - </div> - <div className="col-span-1 pl-11"> - <UiTable tableData={publicReuseFacilityData} headlist={pubFacHeadList} pageSize={100} total={10} headName={headName()} children={undefined}/> - </div> - <div className="col-span-1 pr-6 max-h-full overflow-y-scroll"> - <ScrollArea> - <PublicReuseFaciltyFormInsert /> - {/* <ScrollBar orientation="vertical"></ScrollBar> */} - </ScrollArea> - </div> - </div> - </> - ) -} diff --git a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert/page.tsx b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert/page.tsx deleted file mode 100644 index c94a93075..000000000 --- a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert/page.tsx +++ /dev/null @@ -1,67 +0,0 @@ -'use client' -import { useWrStore } from "@/app/store"; -import type { TableUpperProps } from "@/app/store/rainReuseFacilityInsert" -import { RainReuseFaciltyInsertForm } from "@/components/ui/rainReuseInsertForm"; -import { ScrollArea, SearchDiv, SearchInput, UiTable } from "@common/business_components"; -import { Button, TableHead, TableRow } from "@common/components"; - - -export 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={3}>{'중축 개축 증축 '}</TableHead> : null))} - </TableRow> - <TableRow> - {upHeadList.map((head: TableUpperProps) => - (head.upName === 'upChangeRe' ? <TableHead key={head.id}>{head.title}</TableHead> : null))} - </TableRow> - </>) -} - - -export default function Home() { - const { rain } = useWrStore((state) => state); - const nodeList = headMakeColSpan(rain.upHeadList); - - - const headName = () => { - return ( - <h1 className="pl-3 text-xl">○건축물 목록</h1> - ) - } - return ( - <> - <div className="grid grid-cols-2 gap-4 "> - <div className='col-span-2'> - <h1 className="pl-3 text-2xl" >빗물이용시설 등록 </h1> - <div className="m-8"> - <SearchDiv > - <div className='w-4/5 grid grid-cols-12 gap-1 mx-4'> - <SearchInput className='col-span-3 my-7' label="건물명" /> - </div> - <div className='w-1/5 flex items-center justify-end'> - <Button className='mr-4 rounded-lg'>조회</Button> - <Button className='mr-4 rounded-lg'>저장</Button> - <Button className='mr-4 rounded-lg'>신규등록</Button> - </div> - </SearchDiv> - </div> - </div> - - <div className="colsapn-1 pl-11"> - <UiTable tableData={rain.rainOfObject} headlist={rain.upHeadList} pageSize={100} total={10} headName={headName()}> - {nodeList} - </UiTable> - </div> - <div className="colsapn-1 pr-11 max-h-full ovreflow-y-scroll"> - <ScrollArea> - <RainReuseFaciltyInsertForm></RainReuseFaciltyInsertForm> - </ScrollArea> - </div> - - </div> - </> - ); -} diff --git a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert/page.tsx b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert/page.tsx deleted file mode 100644 index cecd4177c..000000000 --- a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert/page.tsx +++ /dev/null @@ -1,69 +0,0 @@ -'use client' -import { useWrStore } from "@/app/store"; -import type { TableUpperProps } from "@/app/store/rainReuseFacilityInsert"; -import { RswtrReuseFaciltyInsertForm } from "@/components/ui/rswtrReuseInsetForm"; -import { ScrollArea, SearchDiv, SearchInput, UiTable } from "@common/business_components"; -import { Button, TableHead, TableRow } from "@common/components"; - - -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={3}>{'중축 개축 증축 '}</TableHead> : null))} - </TableRow> - <TableRow> - {upHeadList.map((head: TableUpperProps) => - (head.upName === 'upChangeRe' ? <TableHead key={head.id}>{head.title}</TableHead> : null))} - </TableRow> - </>) -} - - -export default function Home() { - const {rswt} = useWrStore((state) => state); - const nodeList = headMakeColSpan(rswt.upHeadList); - const total = 100; - - - const headName = () => { - return ( - <h1 className="pl-3 text-xl">○건축물 목록</h1> - ) - } - - return ( - <> - <div className="grid grid-cols-2 gap-4 "> - <div className='col-span-2'> - <h1 className="pl-3 text-2xl" >중수도시설 등록 </h1> - <div className="m-8"> - <SearchDiv > - <div className='w-4/5 grid grid-cols-6 gap-1 mx-4'> - <SearchInput className='col-span-1 my-7 text-2xl' label="건물명:" /> - </div> - <div className='w-1/5 flex items-center justify-end'> - <Button className='mr-4 rounded-lg'>조회</Button> - <Button className='mr-4 rounded-lg'>저장</Button> - <Button className='mr-4 rounded-lg'>신규등록</Button> - </div> - </SearchDiv> - </div> - </div> - - <div className="colsapn-1 pl-11"> - <UiTable tableData={rswt.rswtOfObject} headlist={rswt.upHeadList} pageSize={100} total={10} headName={headName()}> - {nodeList} - </UiTable> - </div> - <div className="colsapn-1 pr-11 max-h-full ovreflow-y-scroll"> - <ScrollArea> - <RswtrReuseFaciltyInsertForm></RswtrReuseFaciltyInsertForm> - </ScrollArea> - </div> - - </div> - </> - ); -} diff --git a/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx new file mode 100644 index 000000000..8227e0347 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx @@ -0,0 +1,167 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + ContentsDiv, + ScrollArea, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + Title, + UiModal, + UiTable, +} from "@common/business_components"; +import { Button } from "@common/components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useEffect, useState } from "react"; +import { tableData } from "@/components/lib/func/commom"; +import HtdflRegForm from "@/components/ui/form/fcltReg/htdflReg/htdflRegForm"; +import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; + +export default function Home() { + const { code, htdfl, initHtdfl,getHtdflBdstData } = useWrStore((state) => state); + const [searchTxt, setSearchTxt] = useState(""); + const [pageNation, setPageNation] = useState(1); + + const [htdflForm, setHtdflForm] = useState<customHtdflBdst>(); + const [bdstTable, setbdstTable] = useState<customHtdflBdst[]>(); + const [issNewReg,setIsNewReg] = useState<boolean>( false); + + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chgBuilding: chgBuilding[]; + }>({ isOpen: false, chgBuilding: [] }); + + const { htdflRes } = htdfl; + + useEffect(() => { + if (!htdfl.isInit) { + initHtdfl('0'); + } + }, [htdfl.isInit, initHtdfl]); + + useEffect(() => { + if (code.sggCd.length > 0) { + + const htdflData = tableData(htdflRes, { code }).map( + (item: customHtdflBdst) => { + return { + ...item, + bdstChgYn: item?.chgBuilding && item.chgBuilding.length > 0 && item.chgBuilding[0]?.bdstChgNm !== null ? 'Y' : 'N', + bdstUsgCd: item.reuseBuilding[0]?.bdstUsgCd + }; + } + ); + setbdstTable(htdflData); + } + }, [code, htdflRes]); + + const cellClick: (val: number|undefined ,headId: string) => void = ( + val + ,headId) => { + if ((bdstTable ?? []).length > 0 && val !== undefined) { + { + setHtdflForm(htdflRes[val]); + } + } + + if (headId === "bdstChgYn" && val !== undefined) { + if ( + htdflRes[val]?.bdstInfoPk != null && + htdflRes[val]?.chgBuilding?.length > 0 + ) { + bdstChgModal(htdflRes[val]?.chgBuilding as chgBuilding[]); + } + } + }; + const bdstChgModal = (chgBuilding: chgBuilding[]) => { + setBdstChgProps({ isOpen: true, chgBuilding: chgBuilding }); + }; + useEffect(() => { + getHtdflBdstData(pageNation, "0"); + }, [pageNation]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <Title title={"온배수시설 등록"} /> + <SearchDiv> + <SearchFormLeft> + <SearchInput + className="col-span-3 my-7 !text-3xl" + label="건물명" + textValue={searchTxt || ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </SearchFormLeft> + <SearchFormRight> + <div className="flex flex-row space-x-2 items-center"> + <Button size={"sm"} onClick={()=>{initHtdfl(searchTxt||"")}}>조회</Button> + <Button + size={"sm"} + onClick={() => setIsNewReg(!issNewReg)} + > + 신규등록 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-4 h-full min-h-0"> + <ListDiv className="min-h-0"> + <UiTable + tableData={bdstTable || []} + headlist={htdfl.upHeadList} + pageSize={100} + total={10} + headName={"건축물 목록"} + cellClick={(val, headId) => { cellClick(val || undefined, headId); } } + onPageChange={setPageNation} + > + </UiTable> + </ListDiv> + </div> + <div className="col-span-2 max-h-full"> + <ListDiv> + <ScrollArea className="h-[60vh]"> + <HtdflRegForm + htdflForm={htdflForm ?? ({} as customHtdflBdst)} + isModal={false} + /> + </ScrollArea> + </ListDiv> + </div> + </div> + <UiModal + body={<BdstReg moType="HTB"></BdstReg>} + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={issNewReg} + onOpenChange={(open) => { + setIsNewReg(open); + }} + /> + + <UiModal + bodyClass={""} + open={bdstChgProps.isOpen} + body={<BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal>} + onOpenChange={(open) => { + setBdstChgProps({ ...bdstChgProps, isOpen: open }); + } + } + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx new file mode 100644 index 000000000..441eabf25 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -0,0 +1,329 @@ +"use client"; +import { + ContentsDiv, + ScrollArea, + SearchDiv, + SearchFormLeft, + SearchInput, + Title, + UiModal, + UiTable, +} from "@common/business_components"; +import { Button } from "@common/components"; +import { useWrStore } from "../../../store"; +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components"; +import { useCallback, useEffect, useState } from "react"; +import ListDiv from "@common/business_components/ui/list_div"; +import { tableData } from "@/components/lib/func/commom"; +import type { + customPbnst, + fcBizType, + PbnstRegArrrayType, + pubHasuType, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { + pbnstRegZod, + pbnstRegArrayZod, + fcBizSchema, +} 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 { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal"; +import toast, { Toaster } from "react-hot-toast"; +import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; +import { useFieldArray, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +export default function PublicReuseFaciltyInsert() { + const { + code, + pbnst, + searchFclts, + pubHasuInit, + pbnstRegClick, + pbnstRuseFcltUpdate, + pubHasuSearch, + pbnstRuseBizUpdate, + } = useWrStore((state) => state); + const { pubFacHeadList, pbnstHasuRes, pbnstRes } = pbnst; + const [currentTab, setCurrentTab] = useState<string>("mng"); + const [isRuseReg, setIsRuseReg] = useState<{ + isModal: boolean; + data: pubHasuType; + }>({ isModal: false, data: {} as pubHasuType }); + + const [indexMng, setIndexMng] = useState<number>(0); + const [indexBiz, setIndexBiz] = useState<number>(0); + const [searchTxt, setSearchTxt] = useState<string>(); + const [bdstTable, setbdstTable] = useState<pubHasuType[]>(); + const [selectRow, setSelectRow] = useState<pubHasuType>(); + const [pageNation, setPageNation] = useState<number>(1); + + + const form = useForm<{ + items: z.infer<typeof pbnstRegZod>[]; + }>({ + resolver: zodResolver(z.object({ items: pbnstRegArrayZod })), + // defaultValues: { items: pbnstRegArrayZod.parse([]) }, + mode: "onChange", + }); + const { + fields: items, + append, + remove, + } = useFieldArray({ + control: form.control, + name: `items`, + }); + useEffect(() => { + if (!pbnst.isInit) { + pubHasuInit("1", "41"); + } + }, [pbnst.isInit, pubHasuInit]); + + const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { + const data = writeRgtrAndMdfr(valuse.items); + let bizs = [] as fcBizType[]; + data.forEach((fclt) => { + bizs = [...bizs, ...fclt.fcBizs]; + }); + const pbnst =data.map((fclt) => { + return({ + pbnstRuseFcltPk: fclt.pbnstRuseFcltPk, + fcltCpct: fclt.fcltCpct, + ruseCpct: fclt.ruseCpct, + splyExtraCpct: fclt.splyExtraCpct, + rgtr: fclt.rgtr, + mdfr: fclt.mdfr, + fcFiles: fclt.fcFiles, + fcOpers: fclt.fcOpers, + }) + }) + await pbnstRuseFcltUpdate(pbnst as customPbnst[]); + + await pbnstRuseBizUpdate(bizs); + }; + + useEffect(() => { + if (code.sggCd.length) { + if (searchFclts) { + setbdstTable(tableData(searchFclts, { code })); + } else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { + setbdstTable(tableData(pbnstHasuRes, { code })); + } + } + }, [code, pbnstHasuRes, searchFclts]); + + const cellClick = (val: number | undefined) => { + if (val === undefined) return; + const selectRow = pbnstHasuRes?.[val]; + setSelectRow(pbnstHasuRes?.[val]); + if (selectRow) { + pbnstRegClick(selectRow); + } + }; + + const handleTabChange = (value: string) => { + setCurrentTab(value); + }; + + useEffect(() => { + if (pbnst.pbnstRes&& pbnst.pbnstRes?.length>0) { + console.log("pbnst.pbnstRes", pbnstRes); + form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) }); + }else{ + + console.log("pbnst.pbnstRes is null", pbnstRes); + form.reset({ items: [] }); + } + }, [pbnst.pbnstRes]); + useEffect(() => { + if (indexMng) { + setIndexBiz(0); + } + }, [indexMng]); + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + const appendPbnstRuseFclt = useCallback(() => { + const selecTableRow = selectRow; + + if (pbnstRes && pbnstRes.length > 0) { + const lastPbnst = pbnstRes[pbnstRes.length - 1]; + if (lastPbnst && selecTableRow) { + + append( + pbnstRegZod.parse({ + pbnstRuseFcltPk: + String(selecTableRow.bscFctCd) + String(pbnstRes.length + 1), + ruseUtztnFcltNm: "", + fcBizs: [ + fcBizSchema.parse({ + pbnstRuseFcltFk: + String(lastPbnst.pbnstRuseFcltPk) + + String(pbnstRes.length + 1), + cycl: 1, + }), + ] as fcBizType[], + }) + ); + setIsRuseReg({ ...isRuseReg, isModal: true }); + } + } else { + if (selecTableRow) { + append( + pbnstRegZod.parse({ + pbnstRuseFcltPk: String(selecTableRow.bscFctCd)+'1', + ruseUtztnFcltNm: "", + regDt: null, + mdfcnDt: null, + fcBizs: [fcBizSchema.parse({pbnstRuseFcltFk:String(selecTableRow.bscFctCd)+'1',cycl:1})] as fcBizType[], + }) + ) + setIsRuseReg({ ...isRuseReg, isModal: true }); + } + } + }, [pbnstRegZod, pbnstRes, append, selectRow, form]); + + useEffect(() => { + // console.log(" page getgetvaslue ", form.getValues()); + }, [form.getValues()]); + +useEffect(() =>{ + pubHasuInit(String(pageNation), "41"); +},[pageNation,pubHasuInit]) + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"하수재이용 시설 등록"} /> + <SearchDiv> + <SearchFormLeft> + <SearchInput + className={"col-span-3 my-7 !text-3xl"} + label="건물명" + textValue={searchTxt || ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + <div className="flex flex-row space-x-2 items-center"> + <Button + className="" + size={"sm"} + onClick={(e) => { + searchTxt && searchTxt?.length > 0 + ? pubHasuSearch(String(pageNation), "41", searchTxt) + : pubHasuInit(String(pageNation), "41"); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className="" + onClick={() => { + // pbnstMngData([] as customPbnst[]); + if (!selectRow) { + toast.error( + "하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요" + ); + return; + } else { + appendPbnstRuseFclt(); + } + }} + > + 신규등록 + </Button> + </div> + </SearchFormLeft> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 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={pubFacHeadList} + pageSize={10} + total={100000} + headName={"공공 하수처리시설 등록"} + cellClick={cellClick} + onPageChange={setPageNation} + 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> + <Button + size={"sm"} + onClick={form.handleSubmit(onSubmit)} + className="!justify-end" + > + 저장 + </Button> + </TabsList> + <TabsContent className="max-h-full" value="mng"> + <PbnstMngForm + formType="update" + form={form} + setIndexMng={(index: number) => { + console.log("setIndexMng", index); + setIndexMng(index); + }} + indexMng={indexMng} + /> + </TabsContent> + <TabsContent className="max-h-full" value="buse"> + <PbnstBuseForm + formType="update" + form={form} + setIndexBiz={(index: number) => { + setIndexBiz(index); + }} + indexMng={indexMng} + indexBiz={indexBiz} + /> + </TabsContent> + </Tabs> + </ScrollArea> + </ListDiv> + <UiModal + body={ + <PbnstRegModal + form={form} + data={isRuseReg.data || ({} as customPbnst)} + ></PbnstRegModal> + } + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={isRuseReg.isModal} + onOpenChange={(open) => { + remove(form.getValues("items").length - 1); + setIsRuseReg({ data: {} as pubHasuType, isModal: open }); + }} + /> + </div> + <Toaster position="top-center" /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/TableHead.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/TableHead.tsx new file mode 100644 index 000000000..9dd67dc44 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/TableHead.tsx @@ -0,0 +1,30 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { TableRow, TableHead } from "@common/components/ui"; + +export const makeFcltRegHead = (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={3}> + {"중축 개축 재축 "} + </TableHead> + ) : null + )} + </TableRow> + <TableRow> + {upHeadList.map((head: TableUpperProps) => + head.upName === "upChangeRe" ? ( + <TableHead key={head.id}>{head.title}</TableHead> + ) : null + )} + </TableRow> + </> + ); + }; + \ No newline at end of file diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx new file mode 100644 index 000000000..31743aaa9 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx @@ -0,0 +1,181 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { + type chgBuilding, + type customRainBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { tableData } from "@/components/lib/func/commom"; +import RainRegForm from "@/components/ui/form/fcltReg/rainReg/rainRegForm"; +import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + ScrollArea, + SearchDiv, + SearchFormLeft, + SearchInput, + Title, + UiModal, + UiTable, +} from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components"; +import { useEffect, useState } from "react"; + +export default function Home() { + const { rain, code, initRain, rainSearch, getRainData } = useWrStore((state) => { + return state; + }); + const { rainRes } = rain; + const [rainForm, setRainForm] = useState<customRainBdst>(); + const [issNewReg, setIsNewReg] = useState<boolean>(false); + const [pageNation, setPageNation] = useState(1); + const [bdstTable, setbdstTable] = useState<customRainBdst[]>(); + const [searchTxt, setSearchTxt] = useState<string>(); + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chgBuilding: chgBuilding[]; + }>({ isOpen: false, chgBuilding: [] }); + + useEffect(() => { + if (!rain.isInit) { + initRain('41'); + } + }, [rain.isInit, initRain]); + + useEffect(() => { + if (code.sggCd.length > 0) { + const rainData = tableData(rainRes, { code }).map( + (item: customRainBdst) => { + return { + ...item, + bdstChgYn: item.chgBuilding.length > 0 ? "Y" : "N", + }; + } + ); + + setbdstTable(rainData); + } + }, [code, rainRes]); + const cellClick: (val: number | undefined, headId: string) => void = ( + val, + headId + ) => { + if (val !== undefined) { + setRainForm(rainRes[val]); + } + + if (headId === "bdstChgYn" && val !== undefined) { + if ( + rainRes[val]?.bdstInfoPk != null && + rainRes[val]?.chgBuilding.length > 0 + ) { + bdstChgModal(rainRes[val].chgBuilding); + } + } + }; + + const bdstChgModal = (chgBuilding: chgBuilding[]) => { + setBdstChgProps({ isOpen: true, chgBuilding: chgBuilding }); + }; + + useEffect(() => { + console.log("getPageNation", pageNation); + getRainData(pageNation, "41"); + }, [pageNation]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"빗물이용시설 등록"} /> + <SearchDiv> + <SearchFormLeft> + <SearchInput + className={"col-span-3 my-7 !text-3xl"} + label="건물명" + textValue={searchTxt || ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + <div className="flex flex-row space-x-2 items-center"> + <Button + className="" + size={"sm"} + onClick={(e) => { + rainSearch(searchTxt || "", pageNation, "41"); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className="" + onClick={() => setIsNewReg(!issNewReg)} + > + 신규등록 + </Button> + </div> + </SearchFormLeft> + {/* <SearchFormRight> + + </SearchFormRight> */} + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4"> + <div className="col-span-4 h-full min-h-0"> + <ListDiv className="min-h-0"> + {/* <ScrollArea className="h-[60vh]"> */} + <UiTable + tableData={bdstTable || []} + headlist={rain.upHeadList} + pageSize={20} + total={10000} + headName={"건축물 목록"} + cellClick={(val, headId) => { + cellClick(val, headId); + }} + onPageChange={setPageNation} + > + </UiTable> + {/* </ScrollArea> */} + </ListDiv> + </div> + <div className="col-span-2 max-h-full"> + + <ListDiv className=""> + <ScrollArea className="h-[60vh]"> + <RainRegForm + rainForm={rainForm ?? ({} as customRainBdst)} + isModal={false} + ></RainRegForm> + </ScrollArea> + </ListDiv> + </div> + </div> + <UiModal + body={<BdstReg moType="RNB"></BdstReg>} + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={issNewReg} + onOpenChange={(open) => { + setIsNewReg(open); + }} + /> + + <UiModal + bodyClass={""} + open={bdstChgProps.isOpen} + body={ + <BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal> + } + onOpenChange={(open) => { + setBdstChgProps({ ...bdstChgProps, isOpen: open }); + }} + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx new file mode 100644 index 000000000..2931690b9 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx @@ -0,0 +1,174 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { tableData } from "@/components/lib/func/commom"; +import RswtrRegForm from "@/components/ui/form/fcltReg/rswtrReg/rswtrRegForm"; +import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + ScrollArea, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + Title, + UiModal, + UiTable, +} from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components"; +import { useEffect, useState } from "react"; + +export default function Home() { + const { code, rswt, initRswt, rswtBdstSearch, getRswtBdstData } = useWrStore( + (state) => state + ); + const [rswtForm, setRswtForm] = useState<customRswtBdst>(); + const [searchTxt, setSearchTxt] = useState<string>(); + const [issNewReg, setIsNewReg] = useState<boolean>(false); + const [pageNation, setPageNation] = useState(1); + const [selectVal, setSelect] = useState<number>(); + const [bdstTable, setbdstTable] = useState<customRswtBdst[]>(); + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chgBuilding: chgBuilding[]; + }>({ isOpen: false, chgBuilding: [] }); + const { rswtRes } = rswt; + useEffect(() => { + if (!rswt.isInit) { + initRswt("41"); + } + }, [rswt.isInit, initRswt]); + useEffect(() => { + if (code.sggCd.length > 0) { + const rswtData = tableData(rswtRes, { code }).map( + (item: customRswtBdst) => { + return { + ...item, + bdstChgYn: item.chgBuilding[0]?.bdstChgNm != null ? "Y" : "N", + bdstUsgCd: item.reuseBuilding[0]?.bdstUsgCd, + }; + } + ); + setbdstTable(rswtData); + setRswtForm(rswtRes[selectVal ?? 0]); + } + }, [code, rswtRes, selectVal]); + const cellClick: (val: number | undefined, headId: string) => void = ( + val, + headId + ) => { + if ((bdstTable ?? []).length > 0 && val !== undefined) { + { + setSelect(val); + setRswtForm(rswtRes[val]); + } + } + + if (headId === "bdstChgYn" && val !== undefined) { + if ( + rswtRes[val]?.bdstInfoPk != null && + rswtRes[val]?.chgBuilding?.length > 0 + ) { + bdstChgModal(rswtRes[val].chgBuilding); + } + } + }; + const bdstChgModal = (chgBuilding: chgBuilding[]) => { + setBdstChgProps({ isOpen: true, chgBuilding: chgBuilding }); + }; + useEffect(() => { + getRswtBdstData(pageNation, "41"); + }, [pageNation]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"중수도시설 등록"} /> + <SearchDiv> + <SearchFormLeft> + <SearchInput + className="col-span-3 my-7" + label="건물명" + textValue={searchTxt || ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </SearchFormLeft> + <SearchFormRight> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + className="mr-4 " + onClick={(e) => { + rswtBdstSearch(searchTxt || "", pageNation, "41"); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className="mr-4" + onClick={() => setIsNewReg(!issNewReg)} + > + 신규등록 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + + <div className="grid grid-cols-6 gap-4"> + <div className="col-span-4 h-full min-h-0"> + <ListDiv className="min-h-0"> + <UiTable + tableData={bdstTable || []} + headlist={rswt.upHeadList} + pageSize={20} + total={10000} + headName={"건축물 목록"} + cellClick={cellClick} + onPageChange={setPageNation} + ></UiTable> + </ListDiv> + </div> + <div className="col-span-2 max-h-full"> + <ListDiv> + <ScrollArea className="h-[60vh]"> + <RswtrRegForm + rswtForm={rswtForm ?? ({} as customRswtBdst)} + isModal={false} + /> + </ScrollArea> + </ListDiv> + </div> + </div> + + <UiModal + body={<BdstReg moType="RST"></BdstReg>} + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={issNewReg} + onOpenChange={(open) => { + setIsNewReg(open); + }} + /> + <UiModal + bodyClass={""} + open={bdstChgProps.isOpen} + body={ + <BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal> + } + onOpenChange={(open) => { + setBdstChgProps({ ...bdstChgProps, isOpen: open }); + }} + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx new file mode 100644 index 000000000..e43d5b04f --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx @@ -0,0 +1,193 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import type { + chgBuilding, + customRainBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + excelDownload, + getCityOrSgg, + tableData, +} from "@/components/lib/func/commom"; +import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; +import { + ContentsDiv, + SearchBaseForm, + Title, + UiModal, + UiTable, + type ReturnType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { excelHtdflSttnParsing } from "@/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; + +export default function HtdflSttn() { + const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore( + (state) => state + ); + const { htdflSttnRes } = htdflSttn; + + const ref = useRef<ReturnType>({} as ReturnType); + + const [pageNation, setPageNation] = useState(1); + const [isChHead, setIsChHead] = useState(false); + const [bdstTable, setBdstTable] = useState<customRainBdst[]>(); + const [detailModal, setDetailModal] = useState<{ + isOpen: boolean; + data: customRainBdst | undefined; + }>({ data: undefined, isOpen: false }); + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); + + const codeData = getCityOrSgg(ref); + + const excelClick = useCallback(() => { + if (codeData) htdflSttnSearch(1, "10000", codeData); + }, [htdflSttnSearch, codeData]); + + useEffect(() => { + if (codeData != null) htdflSttnSearch(pageNation, "20", codeData); + }, [pageNation]); + + const cellClick = (index: number | undefined, headId: string) => { + if ( + headId !== "chBuildingYn" && + index !== undefined && + bdstTable !== undefined + ) { + setDetailModal({ isOpen: true, data: bdstTable[index] }); + } + }; + useEffect(() => { + const excelDown = async () => { + if (htdflSttn.excelList.length > 0) { + if (htdflSttn.excelList.length === 0) return; + const excelData = await tableData(htdflSttn.excelList, { code }); + const htdflExcel = excelHtdflSttnParsing(excelData, code.cityCd, code.sggCd); + excelDownload( + htdflExcel as [[]], + "온배수시설 현황", + "sample_htdflSttn.xlsx", + "A1:S3" + ); + } + }; + excelDown(); + }, [htdflSttn.excelList, code]); + + useEffect(() => { + if (!htdflSttn.isInit) { + initHtdfl(codeData ?? "0"); + } + }, [htdflSttn.isInit, initHtdfl]); + + useEffect(() => { + if (htdflSttnRes.length > 0 && code.cityCd.length > 0) { + const data = tableData(htdflSttnRes, { code }); + const chData = data + .map((item: any) => ({ + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0].htdflObj, + chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", + })) + .filter((item: any) => item.htdflObj !== undefined); + + setBdstTable(chData); + } + }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"온배수 시설 현황"} /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + rightChildren={<Button + className={btnClass} + onClick={() => { + setIsChHead(true); + } } + > + 조회항목설정 + </Button>} + leftChildren={undefined} + queryClick={function (returnType: ReturnType): void { + // '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}' + let con; + if (returnType.sigunValue != "0") { + con = returnType.sigunValue; + } else { + con = returnType.sidoValue; + } + htdflSttnSearch(pageNation, "20", con); + } } + excelClick={excelClick} + initClick={function (): void { } } + btnClass={btnClass} + ref={ref} isAdmin={false} /> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={ + <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> + } + headlist={htdflSttn.htdflSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + cellClick(index ?? undefined, headId); + }} + onPageChange={setPageNation} + ></UiTable> + </ListDiv> + </div> + </div> + <UiModal + open={isChHead} + onOpenChange={() => { + setIsChHead(!isChHead); + }} + bodyClass={""} + body={ + <HeadChModal + type={"HTB"} + headList={htdflSttn.htdflSttHeadList.map((item) => ({ + ...item, + visible: item.visible ?? false, + }))} + /> + } + /> + <UiModal + open={detailModal.isOpen} + onOpenChange={() => { + setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen }); + }} + bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"} + body={<SttnDetailModal type={"HTB"} formObj={detailModal.data} />} + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx new file mode 100644 index 000000000..7a7a125d2 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx @@ -0,0 +1,223 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { + ContentsDiv, + SearchBaseForm, + SearchInput, + SelectBox, + Title, + UiModal, + UiTable, + type ReturnType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, Label } 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/type"; +import { excelDownload, yearOptions } from "@/components/lib/func/commom"; +import SearchInputText from "@/components/ui/searchInput/searchInput"; +import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; +import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal"; + +export default function PbnstSttn() { + const { + code, + pbnstSttn, + pbnstSttnSearch, + pbnSttnInit, + getPbnStnDetail, + pbnNmSearch, + } = useWrStore((state) => state); + + const ref = useRef<ReturnType>({} as ReturnType); + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + const [isChHead, setIsChHead] = useState(false); + const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>(); + const [pbnDetaileData, setPbnDetaileData] = useState<{ + isModal: boolean; + data: customPbnst; + }>({ isModal: false, data: {} as customPbnst }); + + const [pbnRuseNm, setPbnRuseNm] = useState<string>(""); + + const excelClick = useCallback(() => { + pbnstSttnSearch("1", "10000", ref.current, "", searchYear); + }, [pbnstSttnSearch, searchYear, ref.current]); + + const cellCLick = useCallback( + (index: number | undefined) => { + if ( + index !== undefined && + pbnstSttn.pbnstSttnRes && + pbnstSttn.pbnstSttnRes[index] + ) { + const pbnstRuseFcltPk = pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk; + getPbnStnDetail(pbnstRuseFcltPk); + } + }, + [getPbnStnDetail, pbnstSttn.pbnstSttnRes] + ); + + useEffect(() => { + if (pbnstSttn.excelList.length >= 0) { + excelDownload( + pbnstSttn.excelList, + "하수재이용시설 현황", + "sample_pbnstSttn.xlsx", + "A1:U2" + ); + } + }, [pbnstSttn.excelList]); + useEffect(() => { + if (!pbnstSttn.isInit) { + pbnSttnInit("1", "13", ref.current, searchYear); + } + }, [pbnstSttn.isInit, pbnSttnInit, ref.current]); + useEffect(() => { + setBdstTable(pbnstSttn.pbnstSttnRes); + }, [pbnstSttn.pbnstSttnRes]); + + // useEffect(() => { + // pbnstSttnSearch("1", "20", ref.current, pbnRuseNm, searchYear); + // }, [pageNation, ref.current, pbnRuseNm, searchYear, pbnstSttnSearch]); + + // useEffect(() => { + // if (pbnRuseNm.length > 1) + // pbnNmSearch(String(pageNation), "23", ref.current, pbnRuseNm, searchYear); + // }, [pbnRuseNm,pageNation]); + + useEffect(() => { + if (pbnstSttn.pbnstDetail.pbnstRuseFcltPk) + setPbnDetaileData({ + isModal: true, + data: pbnstSttn.pbnstDetail, + }); + }, [pbnstSttn.pbnstDetail]); + + useEffect(() => { + console.log("pbnDetaileData.isModal ", pbnDetaileData.isModal); + }, [pbnDetaileData.isModal]); + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <Title title={"하수재이용 시설 현황"} /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + rightChildren={ + <Button + className={btnClass} + onClick={() => { + setIsChHead(true); + }} + > + 조회항목설정 + </Button> + } + leftChildren={ + <div className="col-span-3 flex items-center"> + <SelectBox + className="w-48 h-full text-2xl" + label="연도" + selectClass={"w-full "} + selectArray={yearOptions} + value={searchYear} + onSelectValue={(val: string | number) => + setSearchYear(String(val)) + } + /> + <Label className="w-64 mt-2 mx-4 text-xl text-gray-600"> + {"하수처리시설명 "} + </Label> + <SearchInput + className={"text-2xl w-full ㅍ"} + label="" + textValue={pbnRuseNm || ""} + setText={(e) => { + setPbnRuseNm(e || ""); + }} + /> + </div> + } + queryClick={function (returnType: ReturnType): void { + pbnstSttnSearch("1", "20", returnType, pbnRuseNm, searchYear); + }} + excelClick={excelClick} + initClick={function (): void {}} + btnClass={btnClass} + ref={ref} + isAdmin={false} + /> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={ + <HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} /> + } + headlist={pbnstSttn.pbnstSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + cellCLick(index); + }} + onPageChange={(page: number) => { + pbnstSttnSearch( + String(page), + String(13), + ref.current, + pbnRuseNm, + searchYear + ); + }} + ></UiTable> + </ListDiv> + </div> + </div> + <UiModal + open={isChHead} + onOpenChange={() => { + setIsChHead(!isChHead); + }} + bodyClass={""} + body={ + <HeadChModal + type={"PBN"} + headList={pbnstSttn.pbnstSttHeadList.map((item) => ({ + ...item, + visible: item.visible ?? false, + }))} + /> + } + /> + <UiModal + body={ + <PbnstSttnModal + data={pbnDetaileData.data || ({} as customPbnst)} + ></PbnstSttnModal> + } + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={pbnDetaileData.isModal} + onOpenChange={(open) => { + setPbnDetaileData({ data: {} as customPbnst, isModal: false }); + }} + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx new file mode 100644 index 000000000..631e24350 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -0,0 +1,216 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +"use client"; +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { + excelDownload, + getCityOrSgg, + tableData, +} from "@/components/lib/func/commom"; +import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; +import { + ContentsDiv, + SearchBaseForm, + Title, + UiModal, + UiTable, + type ReturnType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; +import type { + chgBuilding, + customRainBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; +import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; + +export default function RainSttn() { + const { code, rainSttn, rainSttnSearch, rainSttnInit } = useWrStore( + (state) => state + ); + const { rainSttnRes } = rainSttn; + const ref = useRef<ReturnType>({} as ReturnType); + + const [pageNation, setPageNation] = useState(1); + const [isChHead, setIsChHead] = useState(false); + const [bdstTable, setBdstTable] = useState<customRainBdst[]>(); + const [detailModal, setDetailModal] = useState<{ + isOpen: boolean; + data: customRainBdst | undefined; + }>({ data: undefined, isOpen: false }); + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); + + const codeData = getCityOrSgg(ref); + + const cellClick = (index: number | undefined, headId: string) => { + if ( + headId !== "chBuildingYn" && + index !== undefined && + bdstTable !== undefined + ) { + setDetailModal({ isOpen: true, data: bdstTable[index] }); + } else if ( + headId === "chBuildingYn" && + index !== undefined && + bdstTable !== undefined + ) { + if ( + bdstTable[index] && + bdstTable[index]?.chgBuilding[0]?.bdstChgNm !== null + ) { + setBdstChgProps({ + isOpen: true, + chBuilding: bdstTable[index].chgBuilding, + }); + } + } + }; + + useEffect(() => { + if (!rainSttn.isInit) { + rainSttnInit(codeData ?? "0"); + } + }, [rainSttn.isInit, rainSttnInit]); + + useEffect(() => { + if (rainSttnRes.length > 0 && code.cityCd.length > 0) { + const data = tableData(rainSttnRes, { code }); + const chData = data + .map((item: any) => ({ + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0].rainyObj, + chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", + })) + .filter((item: any) => item.rainyObj !== undefined); + + setBdstTable(chData); + } + }, [code, rainSttnRes, rainSttn.rainSttHeadList]); + + useEffect(() => { + if (codeData != null) rainSttnSearch(pageNation, "20", codeData, code.cityCd, code.sggCd); + }, [pageNation]); + + useEffect(() => { + if (rainSttn.excelList.length > 0) { + if (rainSttn.excelList.length === 0) return; + excelDownload( + rainSttn.excelList, + "빗물재이용시설 현황", + "sample_rainSttn.xlsx", + "A1:W3" + ); + } + }, [rainSttn.excelList]); + + const excelClick = useCallback(() => { + if (codeData) rainSttnSearch(1, "10000", codeData, code.cityCd, code.sggCd); + }, [rainSttnSearch, codeData, code.cityCd, code.sggCd]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"빗물재이용 시설 현황"} /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + rightChildren={ + <Button + className={btnClass} + onClick={() => { + setIsChHead(true); + }} + > + 조회항목설정 + </Button> + } + leftChildren={undefined} + queryClick={function (returnType: ReturnType): void { + // '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}' + let con; + if (returnType.sigunValue != "0") { + con = returnType.sigunValue; + } else { + con = returnType.sidoValue; + } + rainSttnSearch(1, "20", con, code.cityCd, code.sggCd); + }} + excelClick={excelClick} + initClick={function (): void { }} + btnClass={btnClass} + ref={ref} + isAdmin={false} + /> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={ + <HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} /> + } + headlist={rainSttn.rainSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + cellClick(index ?? undefined, headId); + }} + onPageChange={setPageNation} + ></UiTable> + </ListDiv> + </div> + </div> + <UiModal + open={isChHead} + onOpenChange={() => { + setIsChHead(!isChHead); + }} + bodyClass={""} + body={ + <HeadChModal + type={"RNB"} + headList={rainSttn.rainSttHeadList.map((item) => ({ + ...item, + visible: item.visible ?? false, + }))} + /> + } + /> + <UiModal + open={detailModal.isOpen} + onOpenChange={() => { + setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen }); + }} + bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"} + body={<SttnDetailModal type={"RNB"} formObj={detailModal.data} />} + /> + <UiModal + bodyClass={""} + open={bdstChgProps.isOpen} + body={ + <BdstChgModal chgBuilding={bdstChgProps.chBuilding}></BdstChgModal> + } + onOpenChange={(open) => { + setBdstChgProps({ ...bdstChgProps, isOpen: open }); + }} + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx new file mode 100644 index 000000000..7a3444a9b --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx @@ -0,0 +1,199 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { + excelDownload, + getCityOrSgg, + tableData, +} from "@/components/lib/func/commom"; +import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; +import { + ContentsDiv, + SearchBaseForm, + Title, + UiModal, + UiTable, + type ReturnType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; + +export default function RswtSttn() { + const { code, rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore( + (state) => state + ); + const [pageNation, setPageNation] = useState(1); + + const { rswtSttnRes } = rswtSttn; + const [isChHead, setIsChHead] = useState(false); + const ref = useRef<ReturnType>({} as ReturnType); + const [bdstTable, setBdstTable] = useState<customRswtBdst[]>(); + const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false }) + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); + + const codeCityOrSgg = getCityOrSgg(ref); + + const cellClick = (index: number | undefined, headId: string) => { + + if (headId !== 'chBuildingYn' && index !== undefined && bdstTable !== undefined) { + setDetailModal({ isOpen: true, data: bdstTable[index] }) + } else if (headId === 'chBuildingYn' && index !== undefined && bdstTable !== undefined) { + if ( + bdstTable[index] && + bdstTable[index]?.chBuildingYn === 'Y' + + ) { + + setBdstChgProps({ isOpen: true, chBuilding: bdstTable[index].chgBuilding }); + } + } + } + + useEffect(() => { + if (!rswtSttn.isInit) { + rswtSttnInit("0"); + } + }, [rswtSttn.isInit, rswtSttnInit]); + + useEffect(() => { + if (rswtSttnRes.length > 0 && code.cityCd.length > 0) { + const data = tableData(rswtSttnRes, { code }); + const chData = data + .map((item: customRswtBdst) => ({ + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0]?.rswtrObj, + chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N' + })) + .filter( + (item: customRswtBdst) => + item.reuseBuilding[0]?.rswtrObj !== undefined + ); + + setBdstTable(chData); + } + }, [code, rswtSttnRes]); + + useEffect(() => { + if (rswtSttn.excelList.length > 0) { + if (rswtSttn.excelList.length === 0) return; + excelDownload( + rswtSttn.excelList, + "중수도시설 현황", + "sample_rswtSttn.xlsx", + "A1:W3", + ); + } + }, [rswtSttn.excelList]); + + const excelClick = useCallback( + (returnType: ReturnType) => { + if (codeCityOrSgg) + rswtSttnSearch(1, "10000", codeCityOrSgg, code.cityCd, code.sggCd); + }, + [rswtSttnSearch, code.cityCd, code.sggCd, codeCityOrSgg] + ); + + useEffect(() => { + if (codeCityOrSgg != null) rswtSttnSearch(pageNation, "20", codeCityOrSgg, code.cityCd, code.sggCd); + }, [pageNation]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"중수도시설 현황"} /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + rightChildren={<Button + className={btnClass} + onClick={() => { + setIsChHead(true); + } } + > + 조회항목설정 + </Button>} + leftChildren={undefined} + queryClick={function (returnType: ReturnType): void { + // '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}' + if (codeCityOrSgg) + rswtSttnSearch(1, "20", codeCityOrSgg, code.cityCd, code.sggCd); + } } + excelClick={excelClick} + initClick={function (): void { } } + btnClass={btnClass} + ref={ref} isAdmin={false} /> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={ + <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> + } + headlist={rswtSttn.rswtSttHeadList} + pageSize={20} + total={10000} + cellClick={(index: number | undefined, headId: string) => { cellClick(index ?? undefined, headId) }} + onPageChange={setPageNation} + ></UiTable> + </ListDiv> + </div> + </div> + <UiModal + open={isChHead} + onOpenChange={() => { + setIsChHead(!isChHead); + }} + bodyClass={""} + body={ + <HeadChModal + headList={(rswtSttn.rswtSttHeadList || []).map((item) => ({ + ...item, + visible: item.visible ?? false, + }))} + type={"RST"} + /> + } + /> + <UiModal + open={detailModal.isOpen} + onOpenChange={() => { + setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen }); + }} + bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"} + body={<SttnDetailModal type={"RST"} formObj={detailModal.data} />} + /> + + <UiModal + bodyClass={""} + open={bdstChgProps.isOpen} + body={ + <BdstChgModal chgBuilding={bdstChgProps.chBuilding}></BdstChgModal> + } + onOpenChange={(open) => { + setBdstChgProps({ ...bdstChgProps, isOpen: open }); + }} + /> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx new file mode 100644 index 000000000..10a0bf85f --- /dev/null +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx @@ -0,0 +1,54 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { TableHead, TableRow } from "@common/components/ui"; + +export const headMakeprfmnc = ( + upHeadList: TableUpperProps[], + makeHeadNm: chHeadType[] + ) => { + return ( + <> + <TableRow> + {upHeadList + .filter((data) => data.visible != false) + .map((head: TableUpperProps) => + !head.upSequnce ? ( + <TableHead + className={`border border-gray-600 px-2 py-1 `} + rowSpan={2} + key={head.id} + > + {head.title} + </TableHead> + ) : head.upSequnce === 1 ? ( + <TableHead + className={`border border-gray-600 px-2 py-1 `} + key={head.id} + rowSpan={1} + colSpan={ + makeHeadNm.find((ch) => ch.headNm === head.upName)?.length + } + > + {makeHeadNm.find((ch) => ch.headNm === head.upName)?.text} + </TableHead> + ) : null + )} + </TableRow> + + <TableRow> + {upHeadList + .filter((data) => data.visible != false) + .map((head: TableUpperProps) => + makeHeadNm.find((ch) => ch.headNm === head.upName) ? ( + <TableHead + className={`border border-gray-600 px-2 py-1 `} + key={head.id} + > + {head.title} + </TableHead> + ) : null + )} + </TableRow> + </> + ); + }; \ No newline at end of file diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx new file mode 100644 index 000000000..18dbc7943 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -0,0 +1,283 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { + rswtChkZodType, + rswtrChkDetailsZodType, +} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { + excelArrayDownload, + excelDownload, + yearOptions, +} from "@/components/lib/func/commom"; +import ChkgdExcel from "@/components/ui/modal/chkgd/chkgdExcel/chkgdExcel"; +import ChkgdModal from "@/components/ui/modal/chkgd/chkgdModal/chkgdModal"; +import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable"; +import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiModal, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, Label } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; +import { shallow } from "zustand/shallow"; + +//----------------------- +export default function Page() { + const { + code, + rainChkgdn, + rswtChkgdn, + rainChkInit, + rainChkDetilUpdate, + rainChkDelete, + rainChkExcelDownload, + rainChkHeadChange, + isRainSttn, + + rswtChkgdnInit, + rswtChkPolInit, + rswtChkDetilsUpdate, + rswtChkDelete, + rswtrChkExcelDownload, + rswtChkHeadChange, + } = useWrStore((state) => { + return state; + }); + const [isRegExcel, setIsRegExcel] = useState(false); + const [updateRainDetail, setUpdateRainDetail] = useState<rainChkZodType[]>( + [] + ); + const [updateRswtDetail, setUpdateRSwtDetail] = useState< + rswtrChkDetailsZodType[] + >([]); + const [deleteIdx, setDeleteIdx] = useState<number[]>([]); + + const [selFcltDiv, setSelFcltDiv] = useState("RNB"); + const [isModal, setIsModal] = useState({ + open: false, + data: {}, + }); + const cellClick = () => { + if (searchYear) rainChkInit(searchYear, "0"); + }; + + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + + useEffect(() => { + if (!rainChkgdn.isInit) { + rainChkInit(new Date().getFullYear().toString(), "0"); + isRainSttn(false); + setIsModal({ data: {}, open: false }); + setIsRegExcel(false); + } + + if (!rswtChkgdn.isInit) { + rswtChkgdnInit(new Date().getFullYear().toString(), "0"); + setIsModal({ data: {}, open: false }); + setIsRegExcel(false); + } + }, [rainChkgdn.isInit, rainChkInit]); + + useEffect(() => { + if (rswtChkgdn.excelList[0]&&rswtChkgdn.excelList[0]?.data.length > 1) { + + excelArrayDownload( + "중수도시설 지도점검", + "sample_rs_chk.xlsx", + rswtChkgdn.excelList + ); + } + }, [rswtChkgdn.excelList]); + + useEffect(() => { + excelDownload( + rainChkgdn.excelList, + "빗물이용시설 지도점검", + "sample_rainy.xlsx", + "A1:AN1", + 2 + ); + }, [rainChkgdn.excelList]); + + const excelClick = useCallback(() => { + if (selFcltDiv === "RNB") { + rainChkExcelDownload(code.cityCd, code.sggCd); + } else { + rswtrChkExcelDownload(code.cityCd, code.sggCd); + } + }, [ + rainChkExcelDownload, + rswtrChkExcelDownload, + code.cityCd, + code.sggCd, + selFcltDiv, + ]); + + const handleClick = useCallback( + (data: any) => { + if (data) setIsModal({ open: !isModal.open, data: data }); + }, + [isModal] + ); + + useEffect(() => { + rainChkgdn.isSttn = false; + rswtChkgdn.isSttn = false; + if (!rainChkgdn.isSttn && !rswtChkgdn.isSttn) { + rainChkHeadChange({ rainChkgdn }, true); + rswtChkHeadChange({ rswtChkgdn }, true); + } + }, [rainChkgdn.isSttn, rswtChkgdn.isSttn]); + + const clickDelete = (data: number[], selFcltDiv: string) => { + if (data.length > 0) { + if (selFcltDiv === "RNB") { + rainChkDelete(data); + } else { + rswtChkDelete(data); + } + } + }; + const chkUpdateTable = (selFcltDiv: string) => { + if (selFcltDiv === "RNB") { + rainChkDetilUpdate(updateRainDetail); + } else { + rswtChkDetilsUpdate(updateRswtDetail); + } + }; + + 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> + <SelectBox + className={"w-full h-full text-2xl"} + label={""} + selectClass={""} + selectArray={rainChkgdn.fcltDiv} + onSelectValue={(val: string | number) =>{ + if(val === "RST") rswtChkPolInit(); + setSelFcltDiv(String(val)) + } + } + value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val} + /> + </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) => { + cellClick(); + }} + > + 조회 + </Button> + <Button + size={"sm"} + onClick={() => { + clickDelete(deleteIdx, selFcltDiv); + }} + > + 삭제 + </Button> + <Button + size={"sm"} + onClick={() => { + chkUpdateTable(selFcltDiv); + }} + > + 저장 + </Button> + <Button + size={"sm"} + onClick={() => { + setIsRegExcel(!isRegExcel); + }} + > + 결과등록 + </Button> + <Button color="green" size={"sm"} onClick={excelClick}> + 양식받기 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + {selFcltDiv === "RNB" ? ( + <RainChkgdTable + deleteData={setDeleteIdx} + detailUp={setUpdateRainDetail} + handleClick={handleClick} + rainChkData={rainChkgdn.rainChk} + /> + ) : ( + <RswtChkgdTable + detaiRswtlUpdate={setUpdateRSwtDetail} + deleteRswtData={setDeleteIdx} + handleRswtClick={handleClick} + rswtChkData={rswtChkgdn.rswtChkRes} + /> + )} + </ListDiv> + </div> + </div> + <UiModal + open={isModal.open} + onOpenChange={() => { + setIsModal({ open: !open, data: {} }); + }} + bodyClass={"w-[80vw] max-w-none"} + body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />} + ></UiModal> + <UiModal + open={isRegExcel} + onOpenChange={setIsRegExcel} + bodyClass={"w-[30vw] max-w-none"} + body={<ChkgdExcel type={selFcltDiv}></ChkgdExcel>} + /> + <Toaster position="top-center" /> + </ContentsDiv> + ); +} 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..6be50a8c5 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx @@ -0,0 +1,279 @@ +"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, + Table, + TableBody, + TableHeader, +} from "@common/components/ui"; +import { useState, useEffect, useCallback } from "react"; +import { set } from "react-hook-form"; +import { Toaster } from "react-hot-toast"; +import { headMakeprfmnc } from "../chkgdnReg/TableHead"; +import { InputTableBody } from "@/components/lib/func/TableData"; +import { + initValInUsage, + initValOutUsage, + subTotalObj, + totalObj, + type FcPrfType, + type PrfType, +} from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel"; + +export default function prfmncPrfmncReg() { + const { code, prfmnc, prfmncHasuInit, prfmnRegData } = useWrStore( + (state) => state + ); + const { pbnstPrfmncHeadList, monthHeadList, prfmncRes } = prfmnc; + const [bdstTable, setbdstTable] = useState<FcPrfType[]>(); + const [searchTxt, setSearchTxt] = useState<string>(); + const [isExcelOpen, setIsExcelOPen] = useState<{ + isModal: boolean; + data: FcPrfType; + }>({ isModal: false, data: {} as FcPrfType }); + const [inputData, setInputData] = useState<any[]>(); + const [pbnstRuseFcltPk, setPbnstRuseFcltPk] = useState<string>(); + const [pageNation , setPageNation] = useState<number>(1); + + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + const excelClick = useCallback(() => { + excelDownload( + [[]], + "하수재이용실적", + "sample_pbnstPrfmncReg.xlsx", + "A1:O1" + ); + }, [prfmnc.excelList]); + + const onSave = useCallback(() => { + if (pbnstRuseFcltPk) { + prfmnRegData( + excelToPbnPrfData( + inputData as unknown as JSON[], + searchYear, + pbnstRuseFcltPk + ) as unknown as PrfType + ); + prfmncHasuInit(String(pageNation), "41"); + } + }, [inputData, searchYear, pbnstRuseFcltPk,prfmncHasuInit,pageNation]); + + + + + const cellClick = useCallback( + (index: number | undefined, headId: string) => { + if (index !== undefined) { + setIsExcelOPen({ + isModal: false, + data: prfmncRes[index] || ({} as FcPrfType), + }); + const inUsageData = structuredClone(initValInUsage); + const outUsageData = structuredClone(initValOutUsage); + const inSubtotal = structuredClone(subTotalObj); + const outSubtotal = structuredClone(subTotalObj); + if (prfmncRes[index]?.prfList) { + prfmncRes[index].prfList?.forEach((prfItem) => { + const month = Number(prfItem?.ym?.slice(4)); // "MM" + const monthKey = `${month}월`; + + inUsageData.forEach((inData: any) => { + const usageKey = inData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + inData[monthKey] = value; + inData.total += value; + + if (inSubtotal && usageKey !== "subtotal") { + (inSubtotal as any)[monthKey] = + ((inSubtotal as any)[monthKey] ?? 0) + value; + inSubtotal.total += value; + } + }); + + outUsageData.forEach((outData: any) => { + const usageKey = outData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + outData[monthKey] = value; + outData.total += value; + + if (usageKey !== "subtotal") { + (outSubtotal as any)[monthKey] = + ((outSubtotal as any)[monthKey] ?? 0) + value; + outSubtotal.total += value; + } + }); + }); + inUsageData.push(inSubtotal); + outUsageData.push(outSubtotal); + const usageTotalObj: { [key: string]: any } = structuredClone(totalObj); + for (const key in usageTotalObj) { + if (["usage", "gubun", "edit"].includes(key)) continue; + const val = + (Number((inSubtotal as any)[key]) || 0) + + (Number((outSubtotal as any)[key]) || 0); + + (usageTotalObj as any)[key] = val; + } + const tableData = [...inUsageData, ...outUsageData, usageTotalObj]; + setPbnstRuseFcltPk(prfmncRes[index].pbnstRuseFcltPk); + + setInputData(tableData); + } else { + setInputData([]); + } + } + }, + [prfmncRes] + ); + + useEffect(() => { + if (!prfmnc.isInit) { + prfmncHasuInit('1', "41"); + } + }, [prfmnc.isInit, prfmncHasuInit]); + useEffect(() => { + setbdstTable(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={() => { + onSave(); + }} + > + 저장 + </Button> + <Button + size={"sm"} + onClick={() => { + setIsExcelOPen({ ...isExcelOpen, isModal: 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={10} + total={10000} + headName={"하수재이용시설 목록"} + cellClick={(index: number | undefined, headId: string) => { + cellClick(index, headId); + }} + onPageChange={setPageNation} + children={undefined} + /> + </ScrollArea> + </ListDiv> + </div> + <ListDiv className="col-span-8 pr-6 "> + <ScrollArea className="h-[60vh]"> + <div className="card-header"> + <h3 className="text-2xl font-medium text-label"> + {"하수처리수재이용 실적(m3) 등록결과"} + </h3> + </div> + + <Table variant="secondary"> + <TableHeader className="font-bold"> + {headMakeprfmnc(monthHeadList, [])} + </TableHeader> + <TableBody> + {InputTableBody(inputData ?? [], monthHeadList, setInputData)} + </TableBody> + </Table> + </ScrollArea> + </ListDiv> + </div> + </ContentsDiv> + <UiModal + bodyClass={""} + body={<PbnPrfExcelModal data={isExcelOpen.data}></PbnPrfExcelModal>} + open={isExcelOpen.isModal} + onOpenChange={() => { + setIsExcelOPen({ isModal: false, data: {} as FcPrfType }); + }} + ></UiModal> + <Toaster position="top-center" /> + </> + ); +} diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx new file mode 100644 index 000000000..f9a799c24 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -0,0 +1,187 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { + excelArrayDownload, + excelDownload, + yearOptions, +} from "@/components/lib/func/commom"; +import ChkgdExcel from "@/components/ui/modal/chkgd/chkgdExcel/chkgdExcel"; +import ChkgdModal from "@/components/ui/modal/chkgd/chkgdModal/chkgdModal"; +import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable"; +import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiModal, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, Label } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; + +//----------------------- +export default function Page() { + const { + code, + rainChkSttn, + rswtChkSttn, + + + rainChkSttnInit, + rainChkSttnSearch, + rswtChkSttnInit, + isRainSttn, + isRswtSttn + } = useWrStore((state) => { + return state; + }); + + const [selFcltDiv, setSelFcltDiv] = useState("RNB"); + const [isModal, setIsModal] = useState({ + open: false, + data: {}, + }); + const [searchYear, setSearchYear] = useState<string>(String(new Date().getFullYear())); + const cellClick = () => { + if (searchYear) rainChkSttnSearch(searchYear, "0"); + }; + + useEffect(() => { + if (!rainChkSttn.isInit) { + isRainSttn(true) + rainChkSttnInit( "0"); + setIsModal({ data: {}, open: false }); + } + }, [rainChkSttn.isInit, rainChkSttnInit]); + + useEffect(() => { + + if (!rswtChkSttn.isInit) { + setIsModal({ data: {}, open: false }); + rswtChkSttnInit( "0"); + isRswtSttn(true) + } + },[rswtChkSttn.isInit, rswtChkSttnInit]) + + + const handleClick = useCallback( + (data: any) => { + if (data) setIsModal({ open: !isModal.open, data: data }); + }, + [isModal] + ); +const excelDown=() =>{ + if(rainChkSttn.sttnExcelList && selFcltDiv === "RNB"){ + excelDownload( + rainChkSttn.sttnExcelList, + "빗물이용시설 지도점검 현황", + "sample_rainChkSttn.xlsx", + "A1:Q2", + 3 + ); + }else if(rswtChkSttn.sttnExcelList && selFcltDiv === "RST"){ + excelDownload( + rswtChkSttn.sttnExcelList, + "중수도시설 지도점검 현황", + "sample_rswtChkSttn.xlsx", + "A1:X2", + 3 + ); + } +} + + 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> + <SelectBox + className={"w-full h-full text-2xl"} + label={""} + selectClass={""} + selectArray={rainChkSttn.fcltDiv} + onSelectValue={(val: string | number) => + setSelFcltDiv(String(val)) + } + value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val} + /> + </div> + </SearchFormLeft> + <SearchFormRight> + <div className="space-y"> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + onClick={(e) => { + cellClick(); + }} + > + 조회 + </Button> + <Button color="green" size={"sm"} onClick={(e) =>{excelDown()}}> + 다운로드 + </Button> + </div> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + {selFcltDiv === "RNB" ? ( + <RainChkgdTable + deleteData={() => {}} + detailUp={() => {}} + handleClick={handleClick} + rainChkData={rainChkSttn.rainChkRes} + /> + ) : ( + <RswtChkgdTable + detaiRswtlUpdate={() => {}} + deleteRswtData={() => {}} + handleRswtClick={handleClick} + rswtChkData={rswtChkSttn.rswtChkRes} + /> + )} + </ListDiv> + </div> + </div> + <UiModal + open={isModal.open} + onOpenChange={() => { + setIsModal({ open: !open, data: {} }); + }} + bodyClass={"w-[80vw] max-w-none"} + body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />} + ></UiModal> + <Toaster position="top-center" /> + </ContentsDiv> + ); +} diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx new file mode 100644 index 000000000..a50c08071 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx @@ -0,0 +1,650 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { initValInUsage, initValOutUsage, subTotalObj, totalObj, type FcPrfType, type PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +import { excelPrfmncSttnParsing } from "@/app/store/prfmncSttn/pbnstPrfmncSttn/excel"; +import { excelDownload, yearOptions } from "@/components/lib/func/commom"; +import SearchInputText from "@/components/ui/searchInput/searchInput"; +import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ChartConfig, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button, Label } from "@common/components/ui"; +import { useCallback, useEffect, useRef, useState } from "react"; +import toast, { Toaster } from "react-hot-toast"; + +export default function PbnstPrfmncSttn() { + const { + code, + tableHeadMaker, + pbnstPrfmncSttn, + pbnstPrfmncInit, + pbnstPrfmncSearch, + prfmnc, + pbnstSttn, + pbnNmSearch + } = useWrStore((state) => state); + const { monthHeadList } = prfmnc + const { pbnstPrfHeadList, prfmncRes, rawData, selectYearList } = pbnstPrfmncSttn; + const [isChHead, setIsChHead] = useState(false); + const [fromDate, setFromDate] = useState<string>(String(new Date().getFullYear())); + const [toDate, setToDate] = useState<string>(String(new Date().getFullYear())); + const [year, setYear] = useState<string>(String(new Date().getFullYear())); + const [bdstTable, setbdstTable] = useState<FcPrfType[]>(); + const ref = useRef<ReturnType>({} as ReturnType); + const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(); + const [pbnRuseNm, setPbnRuseNm] = useState<string>(""); + + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + const [excelData, setExcelData] = useState<any[]>([]); + const excelClick = useCallback(() => { + excelDownload( + excelData, + "하수재이용실적 현황", + "sample_pbnstPrfmncSttn.xlsx", + "A1:N1", + 2 + ); + }, [excelData]); + const [chartConfig, setChartConfig] = useState<ChartConfig>({}); + const [chartData, setChartData] = useState<dnChartForm[]>([]); + const [dnInUsage, setDnInUsage] = useState<dnChartForm[]>([]); + const [dnInChartConfig, setDnInChartConfig] = useState<ChartConfig>({}); + const [dnOutUsage, setDnOutUsage] = useState<dnChartForm[]>([]); + const [dnOutChartConfig, setDnOutChartConfig] = useState<ChartConfig>({}); + const [barData, setBarData] = useState<barChartForm[]>([]); + const [barChartConfig, setBarChartConfig] = useState<ChartConfig>({}); + const [prfData, setPrfData] = useState<any[]>(); + const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({}); + const [lnOutChartConfig, setLnOutChartConfig] = useState<ChartConfig>({}); + const [inLineData, setInLineData] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + chartData3: lineChartDataProps[]; + chartData4: lineChartDataProps[]; + chartData5: lineChartDataProps[]; + chartData6: lineChartDataProps[]; + chartData7: lineChartDataProps[]; + chartData8: lineChartDataProps[]; + chartData9: lineChartDataProps[]; + }>(); + const [outLineData, setOutLineData] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + chartData3: lineChartDataProps[]; + chartData4: lineChartDataProps[]; + chartData5: lineChartDataProps[]; + chartData6: lineChartDataProps[]; + }>(); + + type dnChartForm = { label: string, val: number, fill?: string }; + type barChartForm = { label: string, "장내용수": number, "장외용수": number }; + type lineChartDataProps = { label: string, val: number, color: string }; + + const prfTableData = (prfList: PrfType[], year: string) => { + const inUsageData = structuredClone(initValInUsage); + const outUsageData = structuredClone(initValOutUsage); + + const inSubtotal = structuredClone(subTotalObj); + const outSubtotal = structuredClone(subTotalObj); + + const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year) + if (prfList && prfFilter.length > 0) { + + prfList?.forEach((prfItem) => { + const month = Number(prfItem?.ym?.slice(4)); // "MM" + const monthKey = `${month}월`; + + inUsageData.forEach((inData: any) => { + const usageKey = inData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + inData[monthKey] = value; + inData.total += value; + + if (inSubtotal && usageKey !== "subtotal") { + (inSubtotal as any)[monthKey] = + ((inSubtotal as any)[monthKey] ?? 0) + value; + inSubtotal.total += value; + } + }); + + outUsageData.forEach((outData: any) => { + const usageKey = outData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + outData[monthKey] = value; + outData.total += value; + + if (usageKey !== "subtotal") { + (outSubtotal as any)[monthKey] = + ((outSubtotal as any)[monthKey] ?? 0) + value; + outSubtotal.total += value; + } + }); + }); + inUsageData.push(inSubtotal); + outUsageData.push(outSubtotal); + const usageTotalObj: { [key: string]: any } = structuredClone(totalObj); + for (const key in usageTotalObj) { + if (["usage", "gubun", "edit"].includes(key)) continue; + const val = + (Number((inSubtotal as any)[key]) || 0) + + (Number((outSubtotal as any)[key]) || 0); + + (usageTotalObj as any)[key] = val; + } + const tableData = [...inUsageData, ...outUsageData, usageTotalObj]; + setPrfData(tableData); + setExcelData(excelPrfmncSttnParsing(prfData ?? [])) + } else { + setPrfData([]); + } + } + + const [chartId, setChartId] = useState<string>(); + useEffect(() => { + // 누적 변수 초기화 + let wshwtr = 0, + coolUswtr = 0, + cleanUswtr = 0, + drwtUswtr = 0, + dltnUswtr = 0, + ithlRswtr = 0, + ithlEtcUswtr = 0, + cleanToilUswtr = 0, + resn = 0; + + let lndwtr = 0, + hydUswtr = 0, + ugwtChr = 0, + farmUswtr = 0, + rvrKeepUswtr = 0, + indstUswtr = 0; + + let inUsageSum = 0, + outUsageSum = 0; + + let inUsageArr: number[] = [], + outUsageArr: number[] = []; + + // 라인차트용 데이터 + const wshwtrData: lineChartDataProps[] = [], + coolUswtrData: lineChartDataProps[] = [], + cleanUswtrData: lineChartDataProps[] = [], + drwtUswtrData: lineChartDataProps[] = [], + dltnUswtrData: lineChartDataProps[] = [], + ithlRswtrData: lineChartDataProps[] = [], + ithlEtcUswtrData: lineChartDataProps[] = [], + cleanToilUswtrData: lineChartDataProps[] = [], + resnData: lineChartDataProps[] = []; + const lndwtrData: lineChartDataProps[] = [], + hydUswtrData: lineChartDataProps[] = [], + ugwtChrData: lineChartDataProps[] = [], + farmUswtrData: lineChartDataProps[] = [], + rvrKeepUswtrData: lineChartDataProps[] = [], + indstUswtrData: lineChartDataProps[] = []; + + // 막대차트 데이터 + const barData: barChartForm[] = []; + + // 도넛차트 데이터 + const dnData: dnChartForm[] = []; + const dnInData: dnChartForm[] = []; + const dnOutData: dnChartForm[] = []; + + if (rawData) { + prfTableData([], year) + rawData.forEach((item) => { + // 실적 테이블 데이터 + if (chartId){ + if(item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId){ + prfTableData(item.prfList, year); + } + item.prfList.filter((e) => e.ym.slice(0, 4) === year && e.pbnstRuseFcltFk === chartId).forEach((element) => { + // 누적 + wshwtr += element.wshwtr; + coolUswtr += element.coolUswtr; + cleanUswtr += element.cleanUswtr; + drwtUswtr += element.drwtUswtr; + dltnUswtr += element.dltnUswtr; + ithlRswtr += element.ithlRswtr; + ithlEtcUswtr += element.ithlEtcUswtr; + cleanToilUswtr += element.cleanToilUswtr; + resn += element.resn; + lndwtr += element.lndwtr; + hydUswtr += element.hydUswtr; + ugwtChr += element.ugwtChr; + farmUswtr += element.farmUswtr; + rvrKeepUswtr += element.rvrKeepUswtr; + indstUswtr += element.indstUswtr; + + // 월별 합계 + inUsageSum = element.wshwtr + + element.coolUswtr + + element.cleanUswtr + + element.drwtUswtr + + element.dltnUswtr + + element.ithlRswtr + + element.ithlEtcUswtr + + element.cleanToilUswtr + + element.resn; + outUsageSum = element.lndwtr + + element.hydUswtr + + element.ugwtChr + + element.farmUswtr + + element.rvrKeepUswtr + + element.indstUswtr; + inUsageArr.push(inUsageSum); + outUsageArr.push(outUsageSum); + + // 막대차트 + const month = Number(element.ym.slice(4, 6)); + barData.push({ label: `${month}월`, "장내용수": inUsageSum, "장외용수": outUsageSum }); + + // 라인차트 + wshwtrData.push({ label: `${month}월`, val: element.wshwtr, color: "red" }); + coolUswtrData.push({ label: `${month}월`, val: element.coolUswtr, color: "orange" }); + cleanUswtrData.push({ label: `${month}월`, val: element.cleanUswtr, color: "yellow" }); + drwtUswtrData.push({ label: `${month}월`, val: element.drwtUswtr, color: "pink" }); + dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "purple" }); + ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "green" }); + ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "blue" }); + cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "magenta" }); + resnData.push({ label: `${month}월`, val: element.resn, color: "skyblue" }); + + lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" }); + hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "orange" }); + ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "yellow" }); + farmUswtrData.push({ label: `${month}월`, val: element.farmUswtr, color: "green" }); + rvrKeepUswtrData.push({ label: `${month}월`, val: element.rvrKeepUswtr, color: "blue" }); + indstUswtrData.push({ label: `${month}월`, val: element.indstUswtr, color: "purple" }); + }); + } + }); + + // 도넛차트 데이터 + dnData.push({ label: "세척수", val: wshwtr, fill: "red" }); + dnData.push({ label: "냉각수", val: coolUswtr, fill: "orange" }); + dnData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" }); + dnData.push({ label: "식수대", val: drwtUswtr, fill: "green" }); + dnData.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" }); + dnData.push({ label: "중수도", val: ithlRswtr, fill: "blue" }); + dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" }); + dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" }); + dnData.push({ label: "세척살수용수", val: resn, fill: "orange" }); + dnData.push({ label: "조경용수", val: lndwtr, fill: "yellow" }); + dnData.push({ label: "친수용수", val: hydUswtr, fill: "green" }); + dnData.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" }); + dnData.push({ label: "농업용수", val: farmUswtr, fill: "blue" }); + dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" }); + dnData.push({ label: "공업용수", val: indstUswtr, fill: "black" }); + + // 장내용수 도넛 + dnInData.push({ label: "세척수", val: wshwtr, fill: "yellow" }); + dnInData.push({ label: "냉각수", val: coolUswtr, fill: "green" }); + dnInData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" }); + dnInData.push({ label: "식수대", val: drwtUswtr, fill: "green" }); + dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" }); + dnInData.push({ label: "중수도", val: ithlRswtr, fill: "green" }); + dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" }); + dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" }); + dnInData.push({ label: "세척살수용수", val: resn, fill: "yellow" }); + + // 장외용수 도넛 + dnOutData.push({ label: "조경용수", val: lndwtr, fill: "yellow" }); + dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "black" }); + dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" }); + dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "black" }); + dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" }); + dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "black" }); + + // 라인차트 데이터 + setInLineData({ + chartData1: wshwtrData, + chartData2: coolUswtrData, + chartData3: cleanUswtrData, + chartData4: drwtUswtrData, + chartData5: dltnUswtrData, + chartData6: ithlRswtrData, + chartData7: ithlEtcUswtrData, + chartData8: cleanToilUswtrData, + chartData9: resnData + }); + setOutLineData({ + chartData1: lndwtrData, + chartData2: hydUswtrData, + chartData3: ugwtChrData, + chartData4: farmUswtrData, + chartData5: rvrKeepUswtrData, + chartData6: indstUswtrData, + }); + setBarData(barData); + setChartData(dnData); + setDnInUsage(dnInData); + setDnOutUsage(dnOutData); + } + }, [rawData, year, chartId]); + + useEffect(() => { + const newChartConfig = chartData.reduce((config, element) => { + config[element.label] = { + label: element.label, + fill: element.fill + }; + return config; + }, {} as Record<string, { label: string; fill?: string }>) satisfies ChartConfig; + setChartConfig(newChartConfig); + }, [chartData]); + + useEffect(() => { + const newChartConfig = dnInUsage.reduce((config, element) => { + config[element.label] = { + label: element.label, + fill: element.fill + }; + return config; + }, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig; + setDnInChartConfig(newChartConfig); + }, [dnInUsage]); + + useEffect(() => { + const newChartConfig = dnOutUsage.reduce((config, element) => { + config[element.label] = { + label: element.label, + fill: element.fill + }; + return config; + }, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig; + setDnOutChartConfig(newChartConfig); + }, [dnOutUsage]); + + useEffect(() => { + const newChartConfig = barData.reduce((config, element) => { + config[element.label] = { + label: element.label, + }; + return config; + + }, {} as Record<string, { label: string }>) satisfies ChartConfig; + setBarChartConfig(newChartConfig); + }, [barData]); + + useEffect(() => { + if (inLineData?.chartData1) { + const newChartConfig = { + chartData1: { + label: '세척수', + color: "red", + }, + chartData2: { + label: '냉각수', + color: "orange", + }, + chartData3: { + label: '청소수', + color: "yellow", + }, + chartData4: { + label: '식수대', + color: "pink", + }, + chartData5: { + label: '희석용수', + color: "purple", + }, + chartData6: { + label: '중수도', + color: "green", + }, + chartData7: { + label: '기타', + color: "blue", + }, + chartData8: { + label: '청소화장실용수', + color: "magenta", + }, + chartData9: { + label: '세척살수용수', + color: "skyblue", + }, + } satisfies ChartConfig; + setLnInChartConfig(newChartConfig); + } + }, [inLineData]); + + + useEffect(() => { + if (outLineData?.chartData1) { + const newChartConfig = { + chartData1: { + label: '조경용수', + color: "red", + }, + chartData2: { + label: '친수용수', + color: "orange", + }, + chartData3: { + label: '지하수충전', + color: "yellow", + }, + chartData4: { + label: '농업용수', + color: "green", + }, + chartData5: { + label: '하천등유지용수', + color: "blue", + }, + chartData6: { + label: '공업용수', + color: "purple", + }, + } satisfies ChartConfig; + setLnOutChartConfig(newChartConfig); + } + }, [outLineData]); + + useEffect(() => { + const returnType: ReturnType = { + sidoValue: "0", + sigunValue: "0", + partValue: "A", + regionValue: "0", + unitWaterShedValue: "", + middleRegionValue: "", + envPartValue: "", + sidoText: "", + sigunText: "" + } + tableHeadMaker(Number(searchYear), Number(searchYear)); + pbnstPrfmncSearch(returnType, "", searchYear, searchYear) + }, []); + + useEffect(() => { + setbdstTable(prfmncRes); + if(prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk) + }, [prfmncRes]); + + + + useEffect(() => { + if (pbnRuseNm.length > 1) + pbnNmSearch("1", "23", ref.current, pbnRuseNm, searchYear); + }, [pbnRuseNm]); + + const cellClick = (index: number | undefined, headId: string) => { + if (index !== undefined && headId !== undefined) { + setChartId(prfmncRes[index].pbnstRuseFcltPk) + } + } + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"하수재이용실적 현황"} /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + rightChildren={<Button + className={btnClass} + onClick={() => { + setIsChHead(true); + }} + > + 조회항목설정 + </Button>} + leftChildren={<div className="col-span-5"> + <div className="flex flex-row"> + <div className="flex flex-row items-center"> + <Label className="text-xl text-gray-600">하수 재이용년도</Label> + <SelectBox + className="h-full text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={fromDate} + onSelectValue={(val: string | number) => setFromDate(String(val))} /> + <SelectBox + className="h-full text-2xl" + label="" + selectClass={""} + selectArray={yearOptions} + value={toDate} + onSelectValue={(val: string | number) => setToDate(String(val))} /> + </div> + <SearchInputText + label={"하수처리시설"} + initValue={pbnRuseNm ?? ""} + setValue={(searchNm?: string) => { + setPbnRuseNm(searchNm || ""); + }} + setRtnData={function (rtnData: any[]): void { + // setbdstTable(rtnData); + }} + searchList={pbnstSttn.pbnstSearchData || []} + ></SearchInputText> + {/* <SelectBox + className="h-full text-2xl" + label="재이용시설명" + selectClass={""} + selectArray={[]} + value={""} + onSelectValue={() => { }} /> */} + </div> + </div>} + queryClick={function (returnType: ReturnType): void { + pbnstPrfmncSearch(returnType, pbnRuseNm ?? "", fromDate, toDate); + if (fromDate && toDate) { + const fromNum = Number(fromDate); + const toNum = Number(toDate); + if (fromNum > toNum) { + toast.error("시작년도가 종료년도 이후로 설정할 수 없습니다."); + } else { + if (toNum - fromNum > 5) { + toast.error("최대 5년까지 선택 가능합니다."); + } else { + tableHeadMaker(fromNum, toNum); + + } + } + } + }} + excelClick={excelClick} + initClick={function (): void { }} + btnClass={btnClass} + ref={ref} isAdmin={false} /> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={bdstTable || []} + headlist={pbnstPrfHeadList} + pageSize={20} + total={10000} + cellClick={(index: number | undefined, headId: string) => { + cellClick(index ?? undefined, headId); + }} + onPageChange={() => { } + } + index={0} + /> + + <hr /> + <h3 className="mt-4 ml-2 text-2xl font-medium text-label">하수재이용실적 상세현황</h3> + + <SelectBox + className="h-full w-1/12 text-2xl" + label="" + selectClass={""} + selectArray={selectYearList} + value={year} + onSelectValue={(val: string | number) => setYear(String(val))} /> + <h1 className="">장내·외 용도별 하수재이용실적 현황(계)</h1> + + <div className="flex"> + <div className="w-1/3"> + <MainChart chartConfig={chartConfig} chartData={chartData} chartTitle={"장내·외 용수 총 실적"} chartForm={CHART_FORM.DN} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={dnInChartConfig} chartData={dnInUsage} chartTitle={"장내용수 실적"} chartForm={CHART_FORM.DN} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={dnOutChartConfig} chartData={dnOutUsage} chartTitle={"장외용수 실적"} chartForm={CHART_FORM.DN} /> + </div> + </div> + <h1 className="">장내·외 월별 하수재이용실적 현황</h1> + <div className="flex"> + <div className="w-1/3"> + <MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={lnOutChartConfig} chartData={outLineData} chartTitle={"월별 장외 용수 총 실적"} chartForm={CHART_FORM.LINE} /> + </div> + </div> + <h1 className="">하수재이용실적 현황</h1> + <ListDiv className="col-span-8 pr-6 "> + <ScrollArea className="h-[60vh]"> + <div className="card-header"> + <h3 className="text-2xl font-medium text-label"> + {"하수처리수재이용 실적(m3) 등록결과"} + </h3> + </div> + + <UiTable + headName={undefined} + tableData={prfData || []} + headlist={monthHeadList} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }}> + </UiTable> + </ScrollArea> + </ListDiv> + + </ListDiv> + </div> + </div> + </ContentsDiv> + <Toaster position="top-center" /> + </> + ) +} diff --git a/apps/wr/app/stats/matStat/page.tsx b/apps/wr/app/stats/matStat/page.tsx new file mode 100644 index 000000000..a147ff597 --- /dev/null +++ b/apps/wr/app/stats/matStat/page.tsx @@ -0,0 +1,74 @@ +'use client' +import { useWrStore } from "@/app/store"; +import { yearOptions } from "@/components/lib/func/commom"; +import { ContentsDiv, Title, SearchDiv, SearchFormLeft, SelectBox, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useState } from "react"; + +export default function MatStat(){ +const { code, matStat } = useWrStore((state) => { + return state; + }); + const [pageNation, setPageNation] = useState(1); + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + + return( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"재이용 통계"} /> + <SearchDiv> + <SearchFormLeft> + <SelectBox + className="w-full h-full text-2xl" + label="통계년도" + selectClass={""} + selectArray={yearOptions} + value={searchYear} + onSelectValue={(val: string | number) => + setSearchYear(String(val)) + } + /> + <div className="flex flex-row space-x-2 items-center"> + <Button className="" size={"sm"} onClick={(e) => {}}> + 조회 + </Button> + <Button className="" size={"sm"} onClick={(e) => {}}> + 다운로드 + </Button> + <Button className="" size={"sm"} onClick={(e) => {}}> + 전체통계다운로드 + </Button> + </div> + </SearchFormLeft> + {/* <SearchFormRight> + + </SearchFormRight> */} + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4"> + <div className="col-span-6 h-full min-h-0"> + <ListDiv className="min-h-0"> + {/* <ScrollArea className="h-[60vh]"> */} + <UiTable + tableData={[]} + headlist={matStat.matStatHeadList} + pageSize={20} + total={10000} + headName={"건축물 목록"} + cellClick={(val, headId) => {}} + onPageChange={setPageNation} + ></UiTable> + {/* </ScrollArea> */} + </ListDiv> + </div> + </div> + </ContentsDiv> + </> + ) +} \ No newline at end of file diff --git a/apps/wr/app/stats/rainStat/page.tsx b/apps/wr/app/stats/rainStat/page.tsx new file mode 100644 index 000000000..08990028f --- /dev/null +++ b/apps/wr/app/stats/rainStat/page.tsx @@ -0,0 +1,275 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import type { RainStatProps } from "@/app/store/stats/rainStat"; +import { excelDownload, tableData, yearOptions } from "@/components/lib/func/commom"; +import { downloadExcel } from "@common/business_components"; + +import { + ContentsDiv, + ScrollArea, + SearchDiv, + SelectBox, + Title, + UiTable +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useEffect, useState } from "react"; + +export default function RainStat() { + const { code, rainStat, searchRainStatList } = useWrStore((state) => state); + const { rainStatList, excelList, rainDetailList } = rainStat; + + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + const [exData, setExData] = useState<Array<Record<string, string | number>>>(); + const [bdstTable, setBdstTable] = useState<RainStatProps[]>(); + + useEffect(() => { + searchRainStatList(searchYear); + }, [code.cityCd, code.sggCd, searchRainStatList, searchYear]) + + const excelDetailDownLoad=() =>{ + const sidoList = new Set<string>(); + const sidoDataMap = new Map<string, (string | number)[]>(); // 시도별 데이터를 저장할 Map + const sidoDetailMap = new Map<string, (string | number)[][]>(); // 시도별 상세 데이터를 저장할 Map + + // 먼저 시도별 상세 데이터를 그룹화 + rainDetailList.forEach((item: RainStatProps[]) => { + const sidoVal = item.at(0)?.toString().trim(); + const sigunVal = item.at(1)?.toString().trim(); + + let sidoText = ''; + let sigunText = ''; + + const sido = code.cityCd.find((city) => city.val === sidoVal); + const sigun = code.sggCd.find((sgg) => sgg.val === sigunVal); + + sidoText = sido?.text || ''; + sigunText = sigun?.text || ''; + + // 시도, 시군구 코드를 텍스트로 변환하여 데이터에 할당 + if (item.length > 0) { + item[0] = sidoText as unknown as RainStatProps; + } + if (item.length > 1) { + item[1] = sigunText as unknown as RainStatProps; + } + if (item.length > 2) { + item[2] = sidoText+' '+sigunText as unknown as RainStatProps; + } + + // 시도별 상세 데이터 수집 + if (sidoText) { + if (!sidoDetailMap.has(sidoText)) { + sidoDetailMap.set(sidoText, []); + } + sidoDetailMap.get(sidoText)!.push(item as unknown as (string | number)[]); + } + + // 시도별 합계 데이터 수집 + if (sidoText && !sidoList.has(sidoText)) { + sidoList.add(sidoText); + // 시도별 초기 데이터 구조 생성 (예: [시도명, 0, 0, 0, ...]) + const initialData: (string | number)[] = [sidoText]; + // 데이터의 나머지 컬럼들을 0으로 초기화 (실제 컬럼 수에 맞게 조정 필요) + for (let i = 1; i < item.length; i++) { + initialData.push(0); + } + sidoDataMap.set(sidoText, initialData); + } + }); + + // 시도별 합계 계산 + rainDetailList.forEach((item: RainStatProps[]) => { + const sidoText = item[0]?.toString(); + if (sidoText && sidoDataMap.has(sidoText)) { + const sidoData = sidoDataMap.get(sidoText)!; + // 각 컬럼의 값을 합계에 더하기 (1번 인덱스부터 시작) + for (let i = 1; i < item.length && i < sidoData.length; i++) { + if(i == 2){ + sidoData[i] = '합계'; + } else if(i == 3){ + // 해당 시도의 데이터 개수를 계산 + const sidoDataCount = rainDetailList.filter((data: unknown[]) => + Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText + ).length; + sidoData[i] = sidoDataCount; + } else if( i == 4){ + sidoData[i] = ''; + } else if(i == 6){ + // 법정 대상 건축물 개수 (인덱스 6이 'Y'인 경우) + const bumCnt = rainDetailList.filter((data: unknown[]) => + Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText + ).filter((data: unknown[]) => String(data[6]) === '대상').length; + sidoData[i] = bumCnt; + } else if(i==8){ + const value = Number(item[8]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==11){ + const value = Number(item[11]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==12){ + const value = Number(item[12]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==14){ + const value = Number(item[14]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } + else { + sidoData[i] = ''; + } + } + } + }); + + // 시도별로 합계와 상세 데이터를 순서대로 배치 + const summaryExcelData: (string | number)[][] = []; + + // 시도별로 순회하면서 합계와 상세 데이터를 추가 + Array.from(sidoList).forEach((sidoText) => { + const sidoSummary = sidoDataMap.get(sidoText); + const sidoDetails = sidoDetailMap.get(sidoText) || []; + + if (sidoSummary) { + // 시도 합계 데이터 추가 + summaryExcelData.push(sidoSummary); + // 해당 시도의 상세 데이터들 추가 + sidoDetails.forEach(detail => { + summaryExcelData.push(detail); + }); + } + }); + + const sidoSummaryList = Array.from(sidoDataMap.values()); + const sidoTotalSummary = [['전체', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '', 0]]; + sidoSummaryList.forEach((item: (string | number)[]) => { + item.forEach((value: string | number, index: number) => { + if(typeof value === 'number') { + if (sidoTotalSummary[0] && index < sidoTotalSummary[0].length) { + sidoTotalSummary[0][index] = Number(sidoTotalSummary[0][index]) + Number(value); + } + } + }); + }); + + // sidoSummaryList와 rainDetailList를 합쳐서 엑셀 데이터 생성 + const excelData = [...sidoTotalSummary, ...sidoSummaryList, ...summaryExcelData]; + + excelDownload( + excelData as unknown as [][], + "빗물이용시설 통계", + "sample_rainStats.xlsx", + "A1:V3", + 4 + ); + } + + const excelClick = () => { + downloadExcel({ + data: (exData as unknown as JSON[]) ?? [], + fileName: "재이용통계", + sheetNameArray: ["Sheet1"], + headerRange: "A1:M1" + }) + } + + useEffect(() => { + const transExcel = Array.isArray(excelList) ? excelList : [excelList]; + const excelData = tableData(transExcel, { code }); + const tbData = Array.isArray(excelData) + ? excelData.map((item: RainStatProps) => { + return { + "시도": item.cityText, + "개소": item.tmp, + "설치비(백만원)": item.instlCst, + "저류조용량(m³/년)": item.dtfcltCpct, + "연간사용량(m³/년)": item.wtrUsqty, + "조경용수": item.lndwtr, + "청소화장실용수": item.cleanToilUswtr, + "세척살수용수": item.resn, + "농업용수": item.farmUswtr, + "공업용수": item.indstUswtr, + "친수용수": item.hydUswtr, + "기타": item.ithlEtcUswtr, + "연간운영비(백만원/년)": item.anlOperCst, + }; + }) + : []; + setExData(tbData); + }, [code, excelList]) + + useEffect(() => { + const transData = Array.isArray(rainStatList) ? rainStatList : [rainStatList]; + const data = tableData(transData, { code }); + const tbData = Array.isArray(data) + ? data.map((item: RainStatProps) => { + return { + ...item, + ctpvCd: item.cityText + }; + }) + : []; + setBdstTable(tbData); + }, [code, rainStatList]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title={"빗물이용시설 통계"} /> + <SearchDiv> + <div className="grid grid-cols-12 gap-5 m-4"> + <div className="col-span-2"> + <SelectBox + className="w-full h-full text-2xl" + label="통계년도" + selectClass={""} + selectArray={yearOptions} + value={searchYear} + onSelectValue={(val: string | number) => + setSearchYear(String(val)) + } + /> + </div> + <div className="flex flex-row space-x-2 items-center"> + <Button className="" size={"sm"} onClick={() => { }}> + 조회 + </Button> + <Button className="" size={"sm"} onClick={() => excelClick()}> + 다운로드 + </Button> + <Button className="" size={"sm"} onClick={() => excelDetailDownLoad()}> + 전체통계다운로드 + </Button> + </div> + </div> + </SearchDiv> + {/* <SearchFormRight> + </SearchFormRight> */} + </div> + </div> + <div className="grid grid-cols-6 gap-4"> + <div className="col-span-6 h-full min-h-0"> + <ListDiv className=""> + <ScrollArea className="h-[67vh]"> + <UiTable + tableData={bdstTable || []} + headlist={rainStat.rainStatHeadList} + pageSize={10} + total={0} + headName={""} + cellClick={undefined} + onPageChange={() => {}} + ></UiTable> + </ScrollArea> + </ListDiv> + </div> + </div> + </ContentsDiv> + </> + ); +} \ No newline at end of file diff --git a/apps/wr/app/stats/rswtStat/page.tsx b/apps/wr/app/stats/rswtStat/page.tsx new file mode 100644 index 000000000..c0741433c --- /dev/null +++ b/apps/wr/app/stats/rswtStat/page.tsx @@ -0,0 +1,289 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import type { RswtStatProps } from "@/app/store/stats/rswtStat"; +import { excelDownload, tableData, yearOptions } from "@/components/lib/func/commom"; +import { + ContentsDiv, + downloadExcel, + ScrollArea, + SearchDiv, + SelectBox, + Title, + UiTable +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useEffect, useState } from "react"; + +export default function RswtStat() { + const { code, rswtStat, searchRswtStatList } = useWrStore((state) => state); + const { rswtStatList, excelList, rswtDetailList } = rswtStat; + const [rswtTable, setRswtTable] = useState<RswtStatProps[]>(); + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + const [excelData, setExceData] = useState<Record<string, string | number>[]>(); + + useEffect(() => { + searchRswtStatList(searchYear) + }, [searchRswtStatList, searchYear]) + + const excelClick = () => { + downloadExcel({ + data: (excelData as unknown as JSON[]) ?? [], + fileName: "중수도재이용통계", + sheetNameArray: ["Sheet1"], + headerRange: "A1:O1" + }) + } + + useEffect(() => { + const transData = Array.isArray(rswtStatList) ? rswtStatList : [rswtStatList]; + const data = tableData(transData, { code }); + const tbData = Array.isArray(data) + ? data.map((item: RswtStatProps) => { + return { + ...item, + ctpvCd: item.cityText + }; + }) + : []; + setRswtTable(tbData); + }, [code, rswtStatList]) + + useEffect(() => { + const transExcel = Array.isArray(excelList) ? excelList : [excelList]; + const exData = tableData(transExcel, { code }); + const tbData = Array.isArray(exData) ? + exData.map((item: RswtStatProps) => { + return { + "시도": item.cityText, + "개소": item.ctpvCnt, + "건축연면적(m³)": item.archArea, + "처리용량(m³)": item.filtrTrtmfctCpct, + "물사용량(m³/년)": item.wtrUsqty, + "중수도+대체수이용량(m³/년)": item.rswtAltSum, + "중수도이용량(m³/년)": item.rswtrUsqty, + "대체수이용량(m³/년)": item.altUswtrUtztnQnt, + "조경용수": item.lndwtr, + "청소화장실용수": item.cleanToilUswtr, + "농업용수": item.farmUswtr, + "공업용수": item.indstUswtr, + "친수용수": item.hydUswtr, + "기타": item.ithlEtcUswtr, + "연간운영비(백만원/년)": item.anlOperCst, + }; + }) : []; + setExceData(tbData); + }, [code, excelList]) + + const excelDetailDownLoad=() =>{ + const sidoList = new Set<string>(); + const sidoDataMap = new Map<string, (string | number)[]>(); // 시도별 데이터를 저장할 Map + const sidoDetailMap = new Map<string, (string | number)[][]>(); // 시도별 상세 데이터를 저장할 Map + + // 먼저 시도별 상세 데이터를 그룹화 + rswtDetailList.forEach((item: RswtStatProps[]) => { + const sidoVal = item.at(0)?.toString().trim(); + const sigunVal = item.at(1)?.toString().trim(); + let sidoText = ''; + let sigunText = ''; + + const sido = code.cityCd.find((city) => city.val === sidoVal); + const sigun = code.sggCd.find((sgg) => sgg.val === sigunVal); + + sidoText = sido?.text || ''; + sigunText = sigun?.text || ''; + + // 시도, 시군구 코드를 텍스트로 변환하여 데이터에 할당 + if (item.length > 0) { + item[0] = sidoText as unknown as RswtStatProps; + } + if (item.length > 1) { + item[1] = sigunText as unknown as RswtStatProps; + } + if (item.length > 2) { + item[2] = sidoText+' '+sigunText as unknown as RswtStatProps; + } + + // 시도별 상세 데이터 수집 + if (sidoText) { + if (!sidoDetailMap.has(sidoText)) { + sidoDetailMap.set(sidoText, []); + } + sidoDetailMap.get(sidoText)!.push(item as unknown as (string | number)[]); + } + + // 시도별 합계 데이터 수집 + if (sidoText && !sidoList.has(sidoText)) { + sidoList.add(sidoText); + // 시도별 초기 데이터 구조 생성 (예: [시도명, 0, 0, 0, ...]) + const initialData: (string | number)[] = [sidoText]; + // 데이터의 나머지 컬럼들을 0으로 초기화 (실제 컬럼 수에 맞게 조정 필요) + for (let i = 1; i < item.length; i++) { + initialData.push(0); + } + sidoDataMap.set(sidoText, initialData); + } + }); + + // 시도별 합계 계산 + rswtDetailList.forEach((item: RswtStatProps[]) => { + const sidoText = item[0]?.toString(); + if (sidoText && sidoDataMap.has(sidoText)) { + const sidoData = sidoDataMap.get(sidoText)!; + // 각 컬럼의 값을 합계에 더하기 (1번 인덱스부터 시작) + for (let i = 1; i < item.length && i < sidoData.length; i++) { + if(i == 2){ + sidoData[i] = '합계'; + } else if(i == 3){ + // 해당 시도의 데이터 개수를 계산 + const sidoDataCount = rswtDetailList.filter((data: unknown[]) => + Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText + ).length; + sidoData[i] = sidoDataCount; + } else if( i == 4){ + sidoData[i] = ''; + } else if(i == 6){ + // 법정 대상 건축물 개수 (인덱스 6이 'Y'인 경우) + const bumCnt = rswtDetailList.filter((data: unknown[]) => + Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText + ).filter((data: unknown[]) => String(data[6]) === '대상').length; + sidoData[i] = bumCnt; + } else if(i==9){ + const value = Number(item[9]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==10){ + const value = Number(item[10]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==11){ + const value = Number(item[11]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==12){ + const value = Number(item[12]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + }else if(i==13){ + const value = Number(item[13]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==14){ + const value = Number(item[14]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else if(i==16){ + const value = Number(item[16]) || 0; + sidoData[i] = (Number(sidoData[i]) + value) as string | number; + } else { + sidoData[i] = ''; + } + } + } + }); + + // 시도별로 합계와 상세 데이터를 순서대로 배치 + const summaryExcelData: (string | number)[][] = []; + + // 시도별로 순회하면서 합계와 상세 데이터를 추가 + Array.from(sidoList).forEach((sidoText) => { + const sidoSummary = sidoDataMap.get(sidoText); + const sidoDetails = sidoDetailMap.get(sidoText) || []; + + if (sidoSummary) { + // 시도 합계 데이터 추가 + summaryExcelData.push(sidoSummary); + // 해당 시도의 상세 데이터들 추가 + sidoDetails.forEach(detail => { + summaryExcelData.push(detail); + }); + } + }); + + const sidoSummaryList = Array.from(sidoDataMap.values()); + const sidoTotalSummary = [['전체', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '', 0]]; + sidoSummaryList.forEach((item: (string | number)[]) => { + item.forEach((value: string | number, index: number) => { + if(typeof value === 'number') { + if (sidoTotalSummary[0] && index < sidoTotalSummary[0].length) { + sidoTotalSummary[0][index] = Number(sidoTotalSummary[0][index]) + Number(value); + } + } + }); + }); + + // sidoSummaryList와 rainDetailList를 합쳐서 엑셀 데이터 생성 + const excelData = [...sidoTotalSummary,...sidoSummaryList, ...summaryExcelData]; + + excelDownload( + excelData as unknown as [][], + "중수도시설 통계", + "sample_rswtStats.xlsx", + "A1:Q3", + 4 + ); + } + + return (<> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> + <Title title="중수도시설 통계" /> + <SearchDiv> + <div className="grid grid-cols-12 gap-5 m-4"> + <div className="col-span-2"> + <SelectBox + className="w-full h-full text-2xl" + label="통계년도" + selectClass="" + selectArray={yearOptions} + value={searchYear} + onSelectValue={(val: string | number) => setSearchYear(String(val))} + /> + </div> + <div className="flex flex-row space-x-2 items-center"> + <Button + className="" + size="sm" + onClick={() => { + searchRswtStatList(searchYear) + }} + > + 조회 + </Button> + <Button + className="" + size="sm" + onClick={() => excelClick()} + > + 다운로드 + </Button> + <Button + className="" + size="sm" + onClick={() => { excelDetailDownLoad() }} + > + 전체통계다운로드 + </Button> + </div> + </div> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4"> + <div className="col-span-6 h-full min-h-0"> + <ListDiv className=""> + <ScrollArea className="h-[67vh]"> + <UiTable + tableData={rswtTable || []} + headlist={rswtStat.rswtStatHeadList} + pageSize={10} + total={0} + headName="" + cellClick={undefined} + onPageChange={() => { }} + /> + </ScrollArea> + </ListDiv> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/store/common/code/index.ts b/apps/wr/app/store/common/code/index.ts new file mode 100644 index 000000000..e7fca8180 --- /dev/null +++ b/apps/wr/app/store/common/code/index.ts @@ -0,0 +1,370 @@ +import { getFetch } from "@/components/lib/api"; +import type { EnvPartSelectDataType, SelectDataType } from "@common/business_components/ui"; + + + +export const btnClass = "p-4 m-2 text-white rounded-sm text-lg font-medium " + +const yn: SelectDataType[] = [ + { val: 'Y', text: 'Y' }, + { val: 'N', text: 'N' }, +] + +//공통 건축물 용도 +const bdstUsgCd: SelectDataType[] = [ + { val: '-1', text: "확인불가" }, + { val: '1', text: "운동장" }, + { val: '2', text: "체육관" }, + { val: '3', text: "공공업무시설" }, + { val: '4', text: "공공기관의청사" }, + { val: '5', text: "아파트" }, + { val: '6', text: "연립주택" }, + { val: '7', text: "다세대주택" }, + { val: '8', text: "기숙사" }, + { val: '9', text: "초등학교" }, + { val: '10', text: "중학교" }, + { val: '11', text: "고등학교" }, + { val: '12', text: "전문대학" }, + { val: '13', text: "대학및대학교" }, + { val: '14', text: "골프장" }, + { val: '15', text: "대규모점포" }, + { val: '16', text: "기타" }, +]; +//빗물 집수면 종류 용도 +// const rainFcltCd = [ +// { val: "1", text: "지붕" }, +// { val: "2", text: "건물옥상" }, +// { val: "3", text: "골프장 연못" }, +// { val: "4", text: "운동장 바닥" }, +// { val: "5", text: "운동장 지붕" }, +// { val: "6", text: "주차장" }, +// { val: "7", text: "태양광패널" }, +// { val: "8", text: "연못" }, +// { val: "9", text: "기타" }, +// { val: "10", text: "확인불가" }, +// ]; +// 중수도 필수 원수종류 +const rswtBdstType: SelectDataType[] = [ + { text: "숙박업", val: "17" }, + { text: "목욕장업", val: "18" }, + { text: "공장", val: "19" }, + { text: "발전시설", val: "20" }, + { text: "관광단지개발사업", val: "21" }, + { text: "도시개발사업", val: "22" }, + { text: "산업단지개발사업", val: "23" }, + { text: "택지개발사업", val: "24" }, + { text: "대규모점포(대형마트)", val: "25" }, + { text: "대규모점포(전문점)", val: "26" }, + { text: "대규모점포(백화점)", val: "27" }, + { text: "대규모점포(쇼핑센터)", val: "28" }, + { text: "대규모점포(복합쇼핑몰)", val: "29" }, + { text: "물류시설", val: "30" }, + { text: "운수시설(여객자동차터미널)", val: "31" }, + { text: "운수시설(철도시설)", val: "32" }, + { text: "운수시설(공항시설)", val: "33" }, + { text: "운수시설(항만시설)", val: "34" }, + { text: "업무시설(공공업무시설)", val: "35" }, + { text: "업무시설(금융업소)", val: "36" }, + { text: "업무시설(사무소)", val: "37" }, + { text: "업무시설(결혼상담소 등 소개업소)", val: "38" }, + { text: "업무시설(출판사)", val: "39" }, + { text: "업무시설(신문사)", val: "40" }, + { text: "업무시설(오피스텔)", val: "41" }, + { text: "교정시설", val: "42" }, + { text: "방송국", val: "43" }, + { text: "전신전화국", val: "44" }, + { text: "기타", val: "45" } +]; + +const orgwtKndCd = [ + { text: "오수(변기 오수 제외)", val: "1" }, + { text: "오수", val: "2" }, + { text: "폐수", val: "3" }, + { text: "오수 및 폐수", val: "4" }, + { text: "기타", val: "5" }, +]; + +const chBuCd = [ + { val: "증축", text: "증축" }, + { val: "개축", text: "개축" }, + { val: "재축", text: "재축" }, +] + + + +const dmdUszCd = [ + { val: "조경용수", text: "조경용수" }, + { val: "청소화장실용수", text: "청소화장실용수" }, + { val: "농업용수", text: "농업용수" }, + { val: "공업용수", text: "공업용수" }, + { val: "친수용수", text: "친수용수" }, + { val: "기타", text: "기타" }, + +] + +const ruseMngMtd = [ + { 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", + HTB:"HTB" +} + +const rainFcltCd = [ + { val: "지붕", text: "지붕" }, + { val: "건물옥상", text: "건물옥상" }, + { val: "골프장 연못", text: "골프장 연못" }, + { val: "운동장 바닥", text: "운동장 바닥" }, + { val: "운동장 지붕", text: "운동장 지붕" }, + { val: "주차장", text: "주차장" }, + { val: "태양광패널", text: "태양광패널" }, + { val: "연못", text: "연못" }, + { val: "기타", text: "기타" }, + { val: "확인불가", text: "확인불가" }, +]; + +export type comCodeState = { + code: { + isInit: boolean; + chBuCd: SelectDataType[] + rainFcltCd: SelectDataType[]; + bdstUsgCd: SelectDataType[]; + cityCd: SelectDataType[]; + sggCd: SelectDataType[]; + rvbsnCd: SelectDataType[]; + mdmbsnCd: SelectDataType[]; + lgbsnCd: SelectDataType[]; + lclgvrnmntCd: EnvPartSelectDataType[]; + orgwtKndCd: SelectDataType[]; + yn: SelectDataType[]; + pplQulity: SelectDataType[]; + pplLenght: SelectDataType[]; + dmdUszCd:SelectDataType[] + buildingType: { + RST:string, + RNB:string, + HTB:string + } + ruseMngMtd:SelectDataType[] + }; +}; + +export type comCodeType = comCodeState & comCodeActions; + +export const getSggCd = async (set: any) => { + const sgg = await getFetch("common", "/addr/sgg"); + /* const sggCd = sgg.map((item: any) => ({ + val: item.adstDstrCd, + text: item.gugun, + })); */ + + const sggCd = [ + { text: "전체", val: '0' }, + ...sgg + .map((item: any) => ({ + text: item.gugun || '', + val: String(item.adstDstrCd) || '' + })) + ]; + + set((state: comCodeState) => ({ + code: { + ...state.code, + sggCd: sggCd, + }, + })); +}; +export const getCityCd = async (set: any) => { + const city = await getFetch("common", "/addr/city"); + const cityCd = city.map((item: any) => ({ + val: item.adstDstrCd, + text: item.sido, + })); + cityCd.unshift({ val: '0', text: '전체' }) + set((state: comCodeState) => ({ + code: { + ...state.code, + cityCd: cityCd, + }, + })); +}; + +// 권역 API 호출 함수 추가 +export const getRegionCd = async (set: any) => { + try { + const rvbsn = await getFetch("common", "/addr/sarea"); + const rvbsnCd = rvbsn.map((item: any) => ({ + val: item.rvbsnCd, + text: item.rvbsn, + })); + rvbsnCd.unshift({ val: '0', text: '전체' }) + set((state: comCodeState) => ({ + code: { + ...state.code, + rvbsnCd: rvbsnCd, + }, + })); + } catch (error) { + console.error('권역 데이터를 가져오는데 실패했습니다:', error); + } +}; + +// 중권역 API 호출 함수 추가 +export const getMiddleRegionCd = async (set: any) => { + try { + const mdmbsn = await getFetch("common", "/addr/midArea"); + const mdmbsnCd = mdmbsn.map((item: any) => ({ + val: item.rvbsnCd, + text: item.mdmbsn, + })); + mdmbsnCd.unshift({ val: '0', text: '전체' }) + set((state: comCodeState) => ({ + code: { + ...state.code, + mdmbsnCd: mdmbsnCd, + }, + })); + } catch (error) { + console.error('중권역 데이터를 가져오는데 실패했습니다:', error); + } +}; + + +// 단위유역 API 호출 함수 추가 +export const getUnitWaterShedCd = async (set: any) => { + try { + const lgbsn = await getFetch("common", "/addr/largeArea"); + const lgbsnCd = lgbsn.map((item: any) => ({ + val: item.rvbsnCd, + text: item.lgbsn, + })); + lgbsnCd.unshift({ val: '0', text: '전체' }) + set((state: comCodeState) => ({ + code: { + ...state.code, + lgbsnCd: lgbsnCd, + }, + })); + } catch (error) { + console.error('대영역 데이터를 가져오는데 실패했습니다:', error); + } +}; + + + +// 환경청 데이터 +export const getEnvPartCd = async (set: any) => { + try { + const lclgvrnmnt = await getFetch('common', '/addr/govArea'); + const lclgvrnmntCd = lclgvrnmnt.map((item: any) => ({ + val: item.lclgvrnmntCd, + text: item.lclgvrnmntNm, + adstDliStrCd: item.adstDliStrCd || '' + + })); + lclgvrnmntCd.unshift({ val: '0', text: '전체', adstDliStrCd: '' }) + set((state: comCodeState) => ({ + code: { + ...state.code, + lclgvrnmntCd: lclgvrnmntCd, + }, + })); + } catch (error) { + console.error('환경청 데이터를 가져오는데 실패했습니다:', error); + } +}; + + + +export const comCodeStateExport: comCodeState = { + code: { + isInit: false, + rainFcltCd: rainFcltCd, + bdstUsgCd: bdstUsgCd, + chBuCd: chBuCd, + orgwtKndCd: orgwtKndCd, + cityCd: [], + sggCd: [], + rvbsnCd: [], + mdmbsnCd: [], + lgbsnCd: [], + lclgvrnmntCd: [], + pplQulity:pplQulity, + pplLenght:pplLenght, + yn: yn, + dmdUszCd:dmdUszCd, + buildingType:buildingType, + ruseMngMtd:ruseMngMtd, + }, +}; + +export type comCodeActions = { + codeInit: () => void; +}; + + + + +export const codeActionExport: (set: any) => comCodeActions = (set: any) => { + return { + codeInit: async () => { + const sgg = await getFetch("common", "/addr/sgg"); + + const sggCd = sgg.map((item: any) => ({ + val: item.adstDstrCd, + text: item.gugun, + })); + + const city = await getFetch("common", "/addr/city"); + const cityCd = city.map((item: any) => ({ + val: item.adstDstrCd, + text: item.sido, + })); + + return set((state: comCodeState) => ({ + code: { + ...state.code, + sggCd: sggCd, + cityCd: cityCd, + }, + })); + }, + + divCityCd: async (cd: string) => { + set((state: comCodeState) => ({ + code: { + ...state.code, + sggCd: state.code.sggCd.filter((item) => { return item.val.toString().slice(0, 2) === cd }), + }, + })); + }, + }; +}; diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts new file mode 100644 index 000000000..8f05eca09 --- /dev/null +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -0,0 +1,122 @@ +import { + fetchType, + getFetch, + postFectch, + putFectch, +} from "@/components/lib/api"; +import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; +import type { conCostFeeType, rateFeeType } from "./type"; +export type comFcltState = { + searchFclts: pubHasuType; + demandCompList: DemandCompType[]; + dmdIsSucees: string; + checkDemand: DemandCompType; + constCostRes:conCostFeeType[], + rateFeeRes:rateFeeType[], + +}; +export type comFcltActions = { + getSwtrmtFcltNm: (searchNm: string) => void; + getDemandCompList: (searchNm: string, cnt: number) => void; + regDemandCompany: (data: DemandCompType) => void; + updateDemandCompany: (data: DemandCompType) => void; + getConstCostFee: (year: string) => void; + getRateFee: (year: string) => void; +}; + +export const searchActions: (set: any) => comFcltActions = (set: any) => { + return { + getSwtrmtFcltNm: async (searchNm: string) => { + const fcltNm = await getFetch( + "common", + `/prcsFclt/info/1/11/${searchNm}` + ); + const searchFclts: pubHasuType[] = fcltNm.map((item: any) => ({ + bscFctCd: item.bscFctCd, + sido: item.sido, + gugun: item.gugun, + bscFctNm: item.bscFctNm, + addr: item.addr, + fctCpc: item.fctCpc, + })); + set((state: comFcltState) => ({ + ...state, + searchFclts: searchFclts, + })); + }, + getDemandCompList: async (searchNm: string, cnt: number) => { + const coList = await getFetch( + fetchType.WR, + `/reuseFc/request/${searchNm}/${cnt}` + ); + + console.log(coList); + set((state: comFcltState) => { + return (state.demandCompList = [...coList]); + }); + }, + regDemandCompany: async (data: DemandCompType) => { + try { + const response = await postFectch( + "/reuseFc/request", + data, + fetchType.WR + ); + let message = ""; + if (response.status === 200) { + message = "success"; + } else { + message = "fail"; + } + set((state: comFcltState) => { + state.dmdIsSucees = message; + return state; + }); + } catch (err) { + console.log(err); + } + }, + updateDemandCompany: async (data: DemandCompType) => { + try { + const response = await putFectch("/reuseFc/request", data); + + let message = ""; + if (response.status === 200) { + message = "success"; + } else { + message = "fail"; + } + + set((state: comFcltState) => { + state.dmdIsSucees = message; + return state; + }); + } catch (err) { + console.log(err); + } + }, + getRateFee: async (year: string) => { + try { + const url = `/prcsFclt/CstRt/${year}`; + const rateFeeRes = await getFetch(fetchType.COMMON, url); + + set((state: comFcltState) => { + return state.rateFeeRes = rateFeeRes;; + }); + } catch (e) { + console.log(" error ", e); + } + },getConstCostFee: async (year: string) => { + try { + const url = `/prcsFclt/CstRuse/${year}`; + const constCost = await getFetch(fetchType.COMMON, url); + set((state: comFcltState) => { + return state.constCostRes = constCost;; + }); + }catch (e) { + } + + } + }; +}; diff --git a/apps/wr/app/store/common/searchCode/type.tsx b/apps/wr/app/store/common/searchCode/type.tsx new file mode 100644 index 000000000..0026bd785 --- /dev/null +++ b/apps/wr/app/store/common/searchCode/type.tsx @@ -0,0 +1,32 @@ +export type conCostFeeType= { + yr: string, + fctCpc: string, + fctCd: string, + stdCost: string + } + + + + export type rateFeeType = { + yr: string, + incExpCd: string, + stdCost: number, + rt: number + } + + + + export type ContCostMap = { + gubun: string; + "500": number; + "1000": number; + "2000": number; + "3000": number; + "5000": number; + "10000": number; + "20000": number; + "30000": number; + "50000": number; + "100000": number; + [key: string]: any; + }; \ No newline at end of file diff --git a/apps/wr/app/store/common/type/header.tsx b/apps/wr/app/store/common/type/header.tsx new file mode 100644 index 000000000..f064b7580 --- /dev/null +++ b/apps/wr/app/store/common/type/header.tsx @@ -0,0 +1,27 @@ +export type CommonTableProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; + row?: number; + rowSpan?: number; + colSpan?: number; + visible?: boolean; +}; + +export type TableUpperProps = { + id: string; + title: string; + upName?: string; + upSequnce?: number; + edit?:boolean; + visible?:boolean; + rowSpan?: number; + colSpan?: number; + type?: string; + selectData ?:string; // type 이 seelct 일경우 selectarray 정하기 위해 + row?:number; + mergeCell?:boolean // 셀을 합칠경우 + readonly?:boolean + }; + diff --git a/apps/wr/app/store/index.tsx b/apps/wr/app/store/index.tsx index 1b759c30e..c1f7fa2fa 100644 --- a/apps/wr/app/store/index.tsx +++ b/apps/wr/app/store/index.tsx @@ -1,4 +1,3 @@ -'use client' import { type ReactNode, createContext, useContext, useRef } from 'react' import { useStore } from 'zustand' @@ -15,13 +14,11 @@ export interface StoreProviderProps { children: ReactNode } -export const StoreProvider = ({ +export const StoreProvider = ({ children, }: StoreProviderProps) => { - const storeRef = useRef<StoreApi>(null) - if (!storeRef.current) { - storeRef.current = StoreZus() - } + const storeRef = useRef<StoreApi>(StoreZus()) + return ( <StoreContext.Provider value={storeRef.current}> {children} 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..7b43e4b00 --- /dev/null +++ b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx @@ -0,0 +1,199 @@ +import type { CommonTableProps, TableUpperProps } from "../../common/type/header"; + + + +export const coRvwHeadListExport: CommonTableProps[]= [ + { + id: "cityText", + title: "시도", + visible: true, + }, + { + id: "sggText", + title: "시군구", + visible: true, + }, + { + id: "dmdCoNm", + title: "회사명", + visible: true, + }, + { + id: "daddr", + 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, + }, +]; + +export const constCostHeadListExport: CommonTableProps[] = [ + { + id: "gubun", + title: "구분", + visible: true, + }, + { + id: "500", + title: "500", + visible: true, + }, + { + id: "1000", + title: "1,000", + visible: true, + }, + { + id: "2000", + title: "2,000", + visible: true, + }, + { + id: "3000", //바꾸기 + title: "3,000", + visible: true, + }, + { + id: "5000", //바꾸기 + title: "5,000", + visible: true, + }, + { + id: "10000", // + title: "10,000", + visible: true, + }, + { + id: "20000", // + title: "20,000", + visible: true, + }, + { + id: "30000", // + title: "30,000", + visible: true, + }, + { + id: "50000", // + title: "50,000", + visible: true, + }, + { + id: "100000", //바꾸기 + title: "100,000", + visible: true, + }, +]; + +export const rateFeeHeadListExport: CommonTableProps[] = [ + { + id: "gubun", + title: "공사비/구분", + visible: true, + }, + { + id: "BD", + title: "기본조사설계비", + visible: true, + }, + { + id: "WD", + title: "실시설계비", + visible: true, + }, + { + id: "SE", + title: "공사감리비", + visible: true, + }, + { + id: "CE", + title: "시설부대비", + visible: true, + }, +]; + +export const usageProcData = [ + { usage: "청소", process: "총인처리시설+염소소독" }, + { usage: "화장실용수", process: "총인처리시설+염소소독" }, + { usage: "세척", process: "총인처리시설+UV소독" }, + { usage: "살수", process: "총인처리시설+UV소독" }, + { usage: "조경", process: "총인처리시설+UV소독" }, + { usage: "농업", process: "총인처리시설+UV소독" }, + { usage: "하천", process: "총인처리시설+UV소독" }, + { usage: "지하수", process: "전처리시설+RO" }, + { usage: "공업", process: "전처리시설+RO" }, +]; 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..89836957b --- /dev/null +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -0,0 +1,183 @@ +import { constCostHeadListExport, coRvwHeadListExport, coRvwNodeListExport, rateFeeHeadListExport } from "./headList"; +import type { CommonTableProps } from "../../common/type/header"; +import type { DedMatUpdateType, DmdCoReqType } from "./type"; +import { fetchType, getFetch, putFectch } from "@/components/lib/api"; +import toast from "react-hot-toast/headless"; + +const coRvwHeadList = coRvwHeadListExport; +const coRvwNodeList = coRvwNodeListExport; +const rateFeeHeadList =rateFeeHeadListExport; +const constCostHeadList=constCostHeadListExport +export type coRvwState = { + coRvw: { + isInit: boolean; + coMatRvwRes: DmdCoReqType[]; + coRvwHeadList: CommonTableProps[]; + coRvwNodeList: CommonTableProps[]; + rateFeeHeadList: CommonTableProps[]; + constCostHeadList: CommonTableProps[]; + }; +}; + +export const coRvwStateExport: coRvwState = { + coRvw: { + isInit: false, + coMatRvwRes: [], + coRvwHeadList: coRvwHeadList, + coRvwNodeList: coRvwNodeList, + rateFeeHeadList:rateFeeHeadList, + constCostHeadList:constCostHeadList + }, +}; + +export type coRvwType = coRvwState & coRvwActions; + +type coRvwActions = { + coMatRvwInit: ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + ) => void; + coMatRvwUpdate: ( + splyCd: string, + dmdCd: string, + data: DedMatUpdateType + ) => void; + coMatRvwSearch: ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string, + dmdCoNm: string, + ) => void; + coMatRvwInqua: ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string + ) => void; +}; + +const coMatInit = async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, +) => { + const url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + const coMatRvwRes = await getFetch(fetchType.WR, url); + + return coMatRvwRes; +}; + +export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { + return { + coMatRvwInit: async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + ) => { + const coMatRvwRes = await coMatInit(pageNo, pageCnt, startYear, endYear); + + return set((state: coRvwState) => ({ + ...state, + coRvw: { + ...state.coRvw, + init: false, + coMatRvwRes: coMatRvwRes, + }, + })); + }, + coMatRvwUpdate: async ( + splyCd: string, + dmdCd: string, + data: DedMatUpdateType + ) => { + try { + const isUpdate = await putFectch( + `/reuseFc/request/cst/${splyCd}/${dmdCd}`, + data + ); + + const coMatRvwRes = coMatInit( + 1, + 10, + new Date().getFullYear().toString(), + new Date().getFullYear().toString(), + ); + return set((state: coRvwState) => ({ + ...state, + coRvw: { + ...state.coRvw, + init: false, + coMatRvwRes: coMatRvwRes, + }, + })); + } catch (e) { + toast.error("매칭 검토 저장 오류입니다."); + } + }, + coMatRvwSearch: async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string, + dmdCoNm: string, + ) => { + let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + + if (sggCd.length > 0) { + url += `/${sggCd}`; + } else { + url += `/0`; + } + if (dmdCoNm?.length > 0) { + url += `/${dmdCoNm}`; + } else { + url += `/0`; + } + + console.log(' url ', url) + const coMatRvwRes = await getFetch(fetchType.WR, url); + + return set((state: coRvwState) => ({ + ...state, + coRvw: { + ...state.coRvw, + coMatRvwRes: coMatRvwRes, + }, + })); + }, + coMatRvwInqua: async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string + ) => { + try { + let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + if (sggCd.length > 0) { + url += `/${sggCd}/0`; + }else if(sggCd ==='0'){ + url += `/0/0`; + } + const coMatRvwRes = await getFetch(fetchType.WR, url); + return set((state: coRvwState) => ({ + ...state, + coRvw: { + ...state.coRvw, + coMatRvwRes: coMatRvwRes, + }, + })); + } catch (e) { + console.log(" error ", e); + } + } + }; +}; diff --git a/apps/wr/app/store/matDmd/coMatRvw/type.tsx b/apps/wr/app/store/matDmd/coMatRvw/type.tsx new file mode 100644 index 000000000..9f146c0fb --- /dev/null +++ b/apps/wr/app/store/matDmd/coMatRvw/type.tsx @@ -0,0 +1,173 @@ +import { z } from "zod"; + + const DemandInfoSchema = z.object({ + dmdInfoSn: z.number(), + dmdInfoFk: z.string(), + dmndTmng: z.coerce.date(), + dmndQty: z.number(), + wntUntprc: z.number(), + grssClfg: z.number(), + combResdch: z.number(), + trbt: z.number(), + blgcOxygDmndQnt: z.number(), + smll: z.number(), + crmty: z.number().nullish(), + tn: z.number(), + tph: z.number(), + ph: z.number(), + clIonQnt: z.number(), + cdtnv: z.number(), + rmrk: z.string(), + useUsgCd: z.string(), + mtchnCmptnYmd: z.string().nullish(), + rvwCmptnYmd: z.string().nullish() + }); + + const DemandMatchingSchema = z.object({ + dmdInfoFk: z.string().nullish(), + splyFcltFk: z.string().nullish(), + splyFcltFlg: z.string().nullish(), + pbnstRuseFcltBizFk: z.number().nullish(), + splyBgngDay: z.coerce.date().nullish(), + splyEndDay: z.coerce.date().nullish(), + splyCpct: z.number().nullish(), + splyFmYn: z.string().nullish(), + useUsgCd: z.string().nullish(), + dmdInfoSn: z.number().nullish(), + splyCndtList:z.array(z.string().nullish() ).nullish() + }); + + const DemandCoSchema = z.object({ + dmdInfoPk: z.string(), + dmdCoNm: z.string(), + ctpvCd: z.string(), + sggCd: z.string(), + daddr: z.string(), + splyBgngDay: z.coerce.date(), + splyEndDay: z.coerce.date(), + splyCpct: z.number(), + splyFmYn: z.string().nullish(), + telno: z.string(), + useUsgCd: z.string().nullish(), + regDt: z.coerce.date().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.coerce.date().nullish(), + mdfr: z.string().nullish(), + dmdAplyPswd: z.string(), + demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임 + demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish()// 안쓰는 거임 + }); + + const DemandCstSchema = z.object({ + dmdInfoSn: z.number(), + usgPrcsProcs: z.string().nullish(), + cstrnCst: z.number(), + fcltSbsdCstrnCst: z.number(), + prcsFcltCpctCstrnCst: z.number() + }); + + export const DmdCoReqSchema = z.object({ + cityText:z.string().nullish(), + sggText:z.string().nullish(), + dmdInfoSn: z.number(), + dmdInfoFk: z.string(), + dmndTmng: z.coerce.date(), + dmndQty: z.number(), + wntUntprc: z.number().nullish(), + grssClfg: z.number().nullish(), + combResdch: z.number().nullish(), + trbt: z.number().nullish(), + blgcOxygDmndQnt: z.number(), + smll: z.number().nullish(), + crmty: z.number().nullish(), + tn: z.number().nullish(), + tph: z.number().nullish(), + ph: z.number().nullish(), + clIonQnt: z.number().nullish(), + cdtnv: z.number(), + rmrk: z.string().nullish(), + useUsgCd: z.string().nullish(), + mtchnCmptnYmd: z.string().nullish(), + rvwCmptnYmd: z.string().nullish(), + demandCo: DemandCoSchema.nullish(), + demandMatching: DemandMatchingSchema.nullish(), + demandCst: DemandCstSchema, + ruseUtztnFcltNm:z.string().nullish(), + totalConstCost:z.number().nullish(), + + }); + + +export type DmdCoReqType = z.infer<typeof DmdCoReqSchema>; +export type DmdMatTableType = { + cityText:string, + sggText:string, + dmdCoNm:string, + daddr:string, + telno:string, + regDt:string, + splyFcltFlg:string +} + + +export type DedMatUpdateType = +{ + dmdInfoSn: number; + usgPrcsProcs: string; + cstrnCst: number; + fcltSbsdCstrnCst: number; + prcsFcltCpctCstrnCst: number; + rgtr: string; +} + + +export type RvwHistTableType = { + cityText:string, + sggText:string, + dmdCoNm:string, + daddr:string, + telno:string, + regDt:string, + rvwCmptnYmd:string, + mtchnCmptnYmd:string, + ruseUtztnFcltNm:string, + dmndQty:string, + useUsgCd:string, + splyFcltFlg:string + completeYn:boolean + dmdInfoSn :string + +} + +export type RvwHistTableType = { + cityText:string, + sggText:string, + dmdCoNm:string, + daddr:string, + telno:string, + regDt:string, + rvwCmptnYmd:string, + mtchnCmptnYmd:string, + ruseUtztnFcltNm:string, + dmndQty:string, + useUsgCd:string, + splyFcltFlg:string + completeYn:boolean + dmdInfoSn :string + +} + + + +export type conCostFeeType= { + yr: string, + fctCpc: string, + fctCd: string, + stdCost: string +} +export type rateFeeType = { + yr: string, + incExpCd: string, + stdCost: number, + rt: number +} \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/coSttn/excel.tsx b/apps/wr/app/store/matDmd/coSttn/excel.tsx new file mode 100644 index 000000000..39d669664 --- /dev/null +++ b/apps/wr/app/store/matDmd/coSttn/excel.tsx @@ -0,0 +1,17 @@ +import type { customCoSttnBdst } from "."; + +export const excelCoSttnParsing = (data: customCoSttnBdst[]): string[][] => { + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + String(item.ctpvCd), + String(item.sggCd), + item.dmdCoNm ?? "", + item.daddr ?? "", + String(item.dmdInfoPk ?? ""), + item.telno ?? "" + ]); + }); + return excelExportData; +}; diff --git a/apps/wr/app/store/matDmd/coSttn/headList.tsx b/apps/wr/app/store/matDmd/coSttn/headList.tsx new file mode 100644 index 000000000..80fed0d76 --- /dev/null +++ b/apps/wr/app/store/matDmd/coSttn/headList.tsx @@ -0,0 +1,98 @@ +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { z } from "zod"; + +export const sttnHeadListType = z.array( + z.object({ + id: z.string(), + title: z.string(), + visible: z.boolean(), + }) +); + +export const coRegExcel = z.object({ + file: z.any() +}); + +export const coSttHeadListExport: z.infer<typeof sttnHeadListType> = [ + { + id: "cityText", + title: "시도", + visible: true, + }, + { + id: "sggText", + title: "시군구", + visible: true, + }, + { + id: "dmdCoNm", + title: "회사명", + visible: true, + }, + { + id: "daddr", + title: "주소", + visible: true, + }, + { + id: "dmdInfoPk", + title: "사업자번호", + visible: true, + }, + { + id: "telno", + title: "전화번호", + visible: true, + }, +]; + +export const coSttNodeListExport: CommonTableProps[] = [ + { + id: "cityText", + title: "시도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggText", + title: "시군구", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "dmdCoNm", + title: "회사명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "daddr", + title: "주소", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "dmdInfoPk", + title: "사업자번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "telno", + title: "전화번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, +]; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/coSttn/index.tsx b/apps/wr/app/store/matDmd/coSttn/index.tsx new file mode 100644 index 000000000..0de37bbfc --- /dev/null +++ b/apps/wr/app/store/matDmd/coSttn/index.tsx @@ -0,0 +1,123 @@ +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; +import { coSttHeadListExport, coSttNodeListExport } from "./headList"; +import { excelCoSttnParsing } from "./excel"; +import type { DemandCompType } from "@common/business_components/ui"; + +// 테이블 헤드 리스트 +const coSttNodeList = coSttNodeListExport; +const coSttHeadList = coSttHeadListExport; +const coSttnRes: DemandCompType[] = []; + + +export type coSttnState = { + coSttn: { + coSttnRes: DemandCompType[]; + coSttHeadList: CommonTableProps[]; + coSttNodeList: CommonTableProps[]; + excelList: []; + isInit: boolean; + coSttnResCount: number; + }; +}; + +type coSttnActions = { + coSttnInit:(pageNo:string, pageCnt:string, con: string) => void; + coSttnSearch: (pageNo:string, pageCnt:string, con: string) => void; + coSttnTxtSearch:(pageNo:string, pageCnt:string, con: string,txt:string) => void; +}; + +export type coSttnType = coSttnState & coSttnActions; + +export const coSttnStateExport: coSttnState = { + coSttn: { + coSttHeadList: coSttHeadList, + coSttNodeList: coSttNodeList, + coSttnRes: coSttnRes, + excelList: [], + isInit: false, + coSttnResCount: 0, + }, +}; + + +export const coSttnActionsExport: (set: any) => coSttnActions = ( + set: any +) => { + return { + coSttnInit: async (pageNo:string, pageCnt:string, con: string) => { + try { + + const coSttnRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}` + ); + + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + coSttnRes: coSttnRes.data, + isInit: false, + coSttnResCount: coSttnRes.count, + }, + }; + }); + } catch (e) { + console.log(e) + } + }, + coSttnSearch: async (pageNo:string, pageCnt:string, con: string) => { + try { + const coSttnRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}` + ); + if (Number(pageCnt) >= 10000) { + const coExcel = excelCoSttnParsing(coSttnRes.data); + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + excelList: coExcel, + isInit: false, + }, + }; + }); + } else + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + coSttnRes: coSttnRes.data, + isInit: false, + coSttnResCount : coSttnRes.count, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + coSttnTxtSearch: async (pageNo:string, pageCnt:string, con: string, txt: string) => { + try { + const coSttnRes = await getFetch( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}/${txt}`) + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + coSttnRes: coSttnRes, + isInit: false, + }}}) + } catch (erTxt) { + console.log("error", erTxt); + } + } + } +} \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/drgtSttn/headList.tsx b/apps/wr/app/store/matDmd/drgtSttn/headList.tsx new file mode 100644 index 000000000..40eae47e3 --- /dev/null +++ b/apps/wr/app/store/matDmd/drgtSttn/headList.tsx @@ -0,0 +1,102 @@ +import type { CommonTableProps } from "../../common/type/header"; + +export const drgtHeadListExport: CommonTableProps[] = [ + { + id: "ctpvCd", + title: "시도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggCd", + title: "시군구", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "haengjung", + title: "행정동", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "normal", + title: "정상(횟수)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "attention", + title: "관심(횟수)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "caution", + title: "주의(횟수)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "warning", + title: "관심+주의(횟수)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "ranking", + title: "가뭄순위", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, +]; + +export const drgtDetailHeadListExport: CommonTableProps[] = [ + { + id: "year", + title: "년", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "month", + title: "월", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + },{ + id: "day", + 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/drgtSttn/index.tsx b/apps/wr/app/store/matDmd/drgtSttn/index.tsx new file mode 100644 index 000000000..edbbc2f28 --- /dev/null +++ b/apps/wr/app/store/matDmd/drgtSttn/index.tsx @@ -0,0 +1,27 @@ +import type { CommonTableProps } from "../../common/type/header"; +import { drgtHeadListExport } from "./headList"; + +const drgtHeadList = drgtHeadListExport; + +export type drgtState = { + drgt:{ + drgtHeadList: CommonTableProps[]; + } +} + +type drgtActions = { +}; + +export type drgtType = drgtState & drgtActions; + +export const drgtStateExport: drgtState = { + drgt: { + drgtHeadList: drgtHeadList, + } +} + +export const drgtActionsExport: (set: any) => drgtActions = ( + set: any +) => { + return {}; +}; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/index.tsx b/apps/wr/app/store/matDmd/index.tsx index 2ed933348..ed8ecd919 100644 --- a/apps/wr/app/store/matDmd/index.tsx +++ b/apps/wr/app/store/matDmd/index.tsx @@ -1,4 +1,4 @@ -import type { selectArray } from "../rainReuseFacilityInsert"; +import type { SelectDataType } from "@common/business_components/ui"; @@ -23,10 +23,10 @@ const selectSgg = [ export type matDmdState ={ dmd:{ isInit: boolean; - selectSido:selectArray[]; - selectSgg:selectArray[]; - selectUsg:selectArray[]; - selectYN:selectArray[]; + selectSido:SelectDataType[]; + selectSgg:SelectDataType[]; + selectUsg:SelectDataType[]; + selectYN:SelectDataType[]; }} type matDmdActions = { decrementList: () => void diff --git a/apps/wr/app/store/matDmd/ldrmntCoSttn/excel.tsx b/apps/wr/app/store/matDmd/ldrmntCoSttn/excel.tsx new file mode 100644 index 000000000..dc0601127 --- /dev/null +++ b/apps/wr/app/store/matDmd/ldrmntCoSttn/excel.tsx @@ -0,0 +1,75 @@ +import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; + +type nameMap = { + [key: string]: string; +} + +export const names: nameMap = { + "시도": "ctpvCd", + "시군구명": "sggCd", + "관리기관": "mngInst", + "회사명": "dmdCoNm", + "공장구분": "fctrySe", + "단지명": "cmpxNm", + "설립구분": "fndnSe", + "입주형태": "mvnShp", + "보유구분": "hldSe", + "최초승인일": "frstAprvYmd", + "최초등록일": "frstRegYmd", + "등록구분": "regSe", + "등록일": "regYmd", + "전화번호": "telno", + "남자종업원": "maleEmpCnt", + "여자종업원": "fmaleEmpCnt", + "외국인남자종업원": "frgnrMaleEmpCnt", + "외국인여자종업원": "frgnrFmaleEmpCnt", + "종업원합계": "totalEmpCnt", + "생산품": "prdctnItem", + "원자재": "splyMtrl", + "공장규모": "fctryScl", + "용도지역": "usgRgn", + "지목": "ldcg", + "용지면적": "usgRgnArea", + "제조시설면적": "mnftrFcltArea", + "부대시설면적": "sbsdFcltArea", + "건축면적": "archArea", + "지식산업센터명": "knwlIndstCntrNm", + "대표업종": "rprsTpbiz", + "업종명": "tpbizNm", + "업종코드": "tpbizCd", + "차수": "cycl", + "법인주소": "corpAddr", + "필지수": "esntlIdx", + "공장주소": "fctryAddr", + "공장주소_지번": "fctryLotnoAddr", + "공장관리번호": "fctryMngNo", +} + + +export const excelPbnstSttnParsing = (data: any[]): string[][] => { + + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + item.rvbsn, + String(item.lgbsn), + item.mdmbsn ?? "", + item.rgnNm ?? "", + String(item.sido ?? ""), + String(item.gugun ?? ""), + String(item.bscFctNm ?? ""), + String(item.efflWqltStndrdNm ?? ""), + String(item.fctCpc ?? ""), + String(item.tel ?? ""), + String(item.ruseUtztnFcltNm ?? ""), + String(item.fcltCpct ?? ""), + String(item.splyExtraCpct ?? ""), + String(item.ruseCpct ?? ""), + String(item.usageTotal ?? ""), + String(item.inUsage ?? ""), + String(item.outUsage ?? ""), + ]) + }) + return excelExportData; +}; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx b/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx new file mode 100644 index 000000000..7a1695039 --- /dev/null +++ b/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx @@ -0,0 +1,404 @@ +import { z } from "zod"; +import type { CommonTableProps } from "../../common/type/header"; +import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; + +export const ldrmntZod = z.object({ + fctryMngSn: z.string().default(''), + ctpvCd: z.string().default(''), + sggCd: z.string().default(''), + mngInstNm: z.string().default('').nullish(), + dmdCoNm: z.string().default(''), + fctrySe: z.string().default(''), + cmpxNm: z.string().default(''), + fndnSe: z.string().default(''), + mvnShp: z.string().default(''), + hldSe: z.string().default(''), + frstAprvYmd: z.string().default(''), + frstRegYmd: z.string().default(''), + regSe: z.string().default(''), + regYmd: z.string().default(''), + telno: z.string().default(''), + maleEmpCnt: z.number().default(0), + fmaleEmpCnt: z.number().default(0), + frgnrMaleEmpCnt: z.number().default(0), + frgnrFmaleEmpCnt: z.number().default(0), + totalEmpCnt: z.number().default(0), + prdctnItem: z.string().default(''), + splyMtrl: z.string().default(''), + fctryScl: z.string().default(''), + usgRgn: z.string().default(''), + ldcg: z.string().default(''), + usgRgnArea: z.number().default(0), + mnftrFcltArea: z.number().default(0), + sbsdFcltArea: z.number().default(0), + archArea: z.number().default(0), + knwlIndstCntrNm: z.string().default(''), + rprsTpbiz: z.string().default(''), + tpbizNm: z.string().default(''), + tpbizCd: z.string().default(''), + cycl: z.number().default(0), + corpAddr: z.string().default(''), + esntlIdx: z.number().default(0), + fctryAddr: z.string().default(''), + fctryLotnoAddr: z.string().default(''), + fctryMngNo: z.string().default(''), + rgtr:z.string().default(TEST_UUID).nullish() +}) + +export const ldrmntHeadListExport: CommonTableProps[] = [ + { + id: "ctpvCd", + title: "시도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggCd", + title: "시군구", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "dmdCoNm", + title: "회사명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctryAddr", + title: "공장주소", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctryMngNo", + title: "공장관리번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "telno", + title: "전화번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, +] + +export const ldrmntNodeListExport: CommonTableProps[] = [ + { + id: "ctpvCd", + title: "시도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggCd", + title: "시군구", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "mngInstNm", + title: "관리기관", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "dmdCoNm", + title: "회사명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctrySe", + title: "공장구분", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "cmpxNm", + title: "단지명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fndnSe", + title: "설립구분", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "mvnShp", + title: "입주형태", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "hldSe", + title: "보유구분", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "frstAprvYmd", + title: "최초승인일", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "frstRegYmd", + title: "최초등록일", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "regSe", + title: "등록구분", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "regYmd", + title: "등록일", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "telno", + title: "전화번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "maleEmpCnt", + title: "남자종업원", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fmaleEmpCnt", + title: "여자종업원", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "frgnrMaleEmpCnt", + title: "외국인남자종업원", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "frgnrFmaleEmpCnt", + title: "외국인여자종업원", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "totalEmpCnt", + title: "종업원합계", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "prdctnItem", + title: "생산품", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "splyMtrl", + title: "원자재", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctryScl", + title: "공장규모", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "usgRgn", + title: "용도지역", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "ldcg", + title: "지목", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "usgRgnArea", + title: "용지면적", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "mnftrFcltArea", + title: "제조시설면적", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sbsdFcltArea", + title: "부대시설면적", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "archArea", + title: "건축면적", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "knwlIndstCntrNm", + title: "지식산업센터명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "rprsTpbiz", + title: "대표업종", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "tpbizNm", + title: "업종명", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "tpbizCd", + title: "업종코드", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "cycl", + title: "차수", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "corpAddr", + title: "법인주소", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "esntlIdx", + title: "필지수", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctryAddr", + title: "공장주소", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctryLotnoAddr", + title: "공장주소_지번", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fctryMngNo", + title: "공장관리번호", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, +]; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx b/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx new file mode 100644 index 000000000..d1176528a --- /dev/null +++ b/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx @@ -0,0 +1,177 @@ +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { deleteFectch, fetchType, getFetch, postFectch, putFectch } from "@/components/lib/api"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import { z } from "zod"; +import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList"; +import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; + + + +// 테이블 헤드 리스트 +const ldrmntHeadList = ldrmntHeadListExport; +const ldrmntNodeList = ldrmntNodeListExport; + +export type customLdrmnt = z.infer<typeof ldrmntZod>; + +const ldrmntRes: customLdrmnt[] = []; + +export type ldrmntState = { + ldrmnt: { + ldrmntHeadList: CommonTableProps[]; + ldrmntNodeList: CommonTableProps[]; + excelList: ExcelType[]; + isInit: boolean; + ldrmntRes: customLdrmnt[]; + }; +}; + +type ExcelType = { + [key: string]: string | number | undefined; +}; + +const excelDownList: ExcelType[] = []; + +type ldrmntActions = { + ldrmntRegData: (data: any[]) => void, + ldrmntInit: (con: string) => void + ldrmntSearch: (page: number, sggCd: string, coNm: string) => void; + ldrmntUpdate: (ldrmntRes: customLdrmnt) => void; + ldrmntDelete: () => void; +}; + +export type ldrmntType = ldrmntState & ldrmntActions; + +export const ldrmntStateExport: ldrmntState = { + ldrmnt: { + ldrmntHeadList: ldrmntHeadList, + ldrmntNodeList: ldrmntNodeList, + excelList: [], + isInit: false, + ldrmntRes: ldrmntRes, + }, +}; + +export const ldrmntActionsExport: (set: any) => ldrmntActions = ( + set: any +) => { + return { + ldrmntInit: async (con: string) => { + try { + const ldrmntRes = await getFetch( + fetchType.WR, + `/reuseFc/request/temp/1/20/${con}` + ); + set((state: ldrmntState) => { + return { + ...state, + ldrmnt: { + ...state.ldrmnt, + ldrmntRes: ldrmntRes, + isInit: false, + }, + } + }); + } catch (e) { + console.log(e) + } + }, + ldrmntRegData: async (data) => { + try { + const response = await postFectch( + "/reuseFc/request/temp", + data, + fetchType.WR + ); + + console.log('response ', response) + + if (response.status === 200) { + ToastFormUi({ + body: "잠재수요처 등록이 완료되었습니다. ", + title: "잠재수요처 등록", + }); + + } + } catch (e) { + console.log(e) + ToastFormUi({ + body: "잠재수요처 등록이 실패하였습니다. ", + title: "잠재수요처 등록", + }); + + } + }, + ldrmntSearch: async (page: number, sggCd: string, coNm: string) => { + let searchUrl = ""; + + if (sggCd !== "") { + if (coNm !== "") { + searchUrl = `/reuseFc/request/temp/${page}/20/${sggCd}/${coNm}`; + } else { + searchUrl = `/reuseFc/request/temp/${page}/20/${sggCd}`; + } + } + + try { + const ldrmntRes = await getFetch( + fetchType.WR, + searchUrl + ); + + ldrmntRes.forEach((item: customLdrmnt) => { + excelDownList.push({ + ctpvCd: item.ctpvCd, + sggCd: item.sggCd, + dmdCoNm: item.dmdCoNm, + fctryAddr: item.fctryAddr, + fctryMngNo: item.fctryMngNo, + telno: item.telno, + }) + }) + + set((state: ldrmntState) => { + return { + ...state, + ldrmnt: { + ...state.ldrmnt, + ldrmntRes: ldrmntRes, + excelList: excelDownList, + }, + } + }) + } catch (e) { + console.log(e) + } + }, + ldrmntUpdate: async (ldrmntRes: customLdrmnt) => { + try { + ldrmntRes.rgtr = TEST_UUID; + const response = await putFectch( + `/reuseFc/request/temp`, + ldrmntRes + ); + + return set((state: ldrmntState) => ({ + ...state, + ldrmnt: { + ...state.ldrmnt, + ldrmntRes: ldrmntRes, + }, + })); + + } catch (error) { + console.error("Error ldrmntUpdate:", error); + } + }, + ldrmntDelete: async () => { + try { + const response = await deleteFectch( + `/reuseFc/request/temp`, + ); + + } catch (error) { + console.error("Error ldrmntDelete:", error); + } + }, + } +} \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx new file mode 100644 index 000000000..125866601 --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx @@ -0,0 +1,355 @@ +import type { SelectDataType } from "@common/business_components/ui"; +import type { TableUpperProps } from "../../common/type/header"; + +//★☆★☆ 여기서부터 수정하기 ★☆★☆ +export const ruseCaseHeadListExport: TableUpperProps[] = [ + { + id: "a", + title: "사업명", + visible: true, + row: 0, + rowSpan: 2, + }, + { + id: "b", + title: "사업기간", + visible: true, + row: 0, + rowSpan: 2, + }, + { + id: "c", + title: "공급처", + visible: true, + row: 0, + colSpan: 3, + }, + { + id: "d", + title: "시설종류", + visible: true, + row: 1, + }, + { + id: "e", + title: "공급처명", + visible: true, + row: 1, + }, + { + id: "f", + title: "시설용량(m³/일)", + visible: true, + row: 1, + }, + { + id: "g", + title: "수요처", + visible: true, + row: 0, + colSpan: 2, + }, + { + id: "h", + title: "수요처명", + visible: true, + row: 1, + }, + { + id: "i", + title: "주소", + visible: true, + row: 1, + }, + { + id: "q", + title: "재이용시설", + visible: true, + row: 0, + colSpan: 5, + }, + { + id: "q2", + title: "시설용량(m³/일)", + visible: true, + row: 1, + }, + { + id: "q3", + title: "관로(km)", + visible: true, + row: 1, + }, + { + id: "q4", + title: "용도", + visible: true, + row: 1, + }, + { + id: "q5", + title: "공급량(m³/일)", + visible: true, + row: 1, + }, + { + id: "q6", + title: "처리공법", + visible: true, + row: 1, + }, + { + id: "k", + title: "보증수질", + visible: true, + row: 0, + colSpan: 13, + }, + { + id: "k1", + title: "용도별", + visible: true, + row: 1, + }, + { + id: "k2", + title: "총대장균군수​(개/100ml)", + visible: true, + row: 1, + }, + { + id: "k3", + title: "결합잔류염소(mg/L)", + visible: true, + row: 1, + }, + { + id: "k4", + title: "탁도(NTU)", + visible: true, + row: 1, + }, + { + id: "k5", + title: "생물학적​산소요구량(BOD)(mg/L)​", + visible: true, + row: 1, + }, + { + id: "k6", + title: "냄새​", + visible: true, + row: 1, + }, + { + id: "k7", + title: "색도​", + visible: true, + row: 1, + }, + { + id: "k8", + title: "총질소(T-N)(mg/L)​", + visible: true, + row: 1, + }, + { + id: "k9", + title: "총인(T-P)(mg/L)", + visible: true, + row: 1, + }, + { + id: "k10", + title: "수소이온농도(PH)​", + visible: true, + row: 1, + }, + { + id: "k11", + title: "수소이온농도(PH)​", + visible: true, + row: 1, + }, + { + id: "k12", + title: "전기전도도​(㎲/cm", + visible: true, + row: 1, + }, + { + id: "k13", + title: "비고​", + visible: true, + row: 1, + }, + { + id: "h", + title: "소요사업비(백만원)", + visible: true, + row: 0, + colSpan: 14, + }, + { + id: "h1", + title: "총", + visible: true, + row: 1, + }, + { + id: "h2", + title: "토목재이용시설", + visible: true, + row: 1, + }, + { + id: "h3", + title: "토목관로", + visible: true, + row: 1, + }, + { + id: "h4", + title: "건축", + visible: true, + row: 1, + }, + { + id: "h5", + title: "기계", + visible: true, + row: 1, + }, + { + id: "h6", + title: "전기・계측", + visible: true, + row: 1, + }, + { + id: "h7", + title: "GIS・DB", + visible: true, + row: 1, + }, + { + id: "h8", + title: "CCTV", + visible: true, + row: 1, + }, + { + id: "h9", + title: "시공및준공", + visible: true, + row: 1, + }, + { + id: "h10", + title: "페기물", + visible: true, + row: 1, + }, + { + id: "h11", + title: "설계비", + visible: true, + row: 1, + }, + { + id: "h12", + title: "감리비", + visible: true, + row: 1, + }, + { + id: "h13", + title: "시설부대비", + visible: true, + row: 1, + }, + { + id: "h14", + title: "기타", + visible: true, + row: 1, + }, + { + id: "i", + title: "자원조달계획(백만원)", + visible: true, + row: 0, + colSpan: 4, + }, + { + id: "i1", + title: "총", + visible: true, + row: 1, + }, + { + id: "i2", + title: "국비", + visible: true, + row: 1, + }, + { + id: "i3", + title: "지방비", + visible: true, + row: 1, + }, + { + id: "i4", + title: "원인자부담", + visible: true, + row: 1, + }, + { + id: "j", + title: "사업비정산(백만원)", + visible: true, + row: 0, + colSpan: 5, + }, + { + id: "j1", + title: "총", + visible: true, + row: 1, + }, + { + id: "j2", + title: "국비", + visible: true, + row: 1, + }, + { + id: "j3", + title: "지방비", + visible: true, + row: 1, + }, + { + id: "j4", + title: "원인자부담", + visible: true, + row: 1, + }, + { + id: "j5", + title: "국고비율", + visible: true, + row: 1, + }, +]; + +export const ruseUtzType: SelectDataType[] = [ + { text: "조경용수", val: "조경용수" }, + { text: "청소화장실용수", val: "청소화장실용수" }, + { text: "세척살수용수", val: "세척살수용수" }, + { text: "농업용수", val: "농업용수" }, + { text: "공업용수", val: "공업용수" }, + { text: "친수용수", val: "친수용수" }, + { text: "기타", val: "기타" }, +]; diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx new file mode 100644 index 000000000..c54b0db1a --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -0,0 +1,24 @@ +import type { TableUpperProps } from "../../common/type/header"; +import { ruseCaseHeadListExport } from "./headList"; + +export type ruseCaseState = { + ruseCase: { + ruseHeadList:TableUpperProps[]; + }; + }; +type ruseCaseActions = { +}; +export type ruseCaseType = ruseCaseState & ruseCaseActions; + +export const ruseCaseStateExport: ruseCaseState = { + ruseCase: { + ruseHeadList:ruseCaseHeadListExport, + }, +}; +export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( + set: any +) => { + return { + + }; +}; 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..90c793c2f --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx @@ -0,0 +1,71 @@ +import type { TableUpperProps } from "../../common/type/header"; + + +export const ruseHstryHeadListExport:TableUpperProps[]= [ + { + id: "cityText", + title: "시도", + visible: true, + }, + { + id: "sggText", + title: "시군구", + visible: true, + }, + { + id: "dmdCoNm", + title: "회사명", + visible: true, + }, + { + id: "daddr", + title: "공장주소", + visible: true, + }, + { + id: "telno", + title: "전화번호", + visible: true, + }, + { + id: "regDt", //바꾸기 + title: "신청일자", + visible: true, + }, + { + id: "rvwCmptnYmd", //바꾸기 + title: "검토일자", + visible: true, + }, + { + id: "mtchnCmptnYmd", //바꾸기 + title: "매칭 완료일자", + visible: true, + }, + { + id: "ruseUtztnFcltNm", //바꾸기 + title: "공급처", + visible: true, + }, + { + id: "dmndQty", //바꾸기 + title: "요구수량(M3/d)", + visible: true, + }, + { + id: "useUsgCd", //바꾸기 + title: "사용용도", + visible: true, + }, + { + id: "splyFcltFlg", //바꾸기 + title: "진행상태", + visible: true, + }, + { + id: "completeYn", //바꾸기 + title: "매칭완료 여부", + visible: true, + }, +]; + 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..3338bc467 --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx @@ -0,0 +1,237 @@ +import type { z } from "zod"; +import type { sttnHeadListType } from "../coSttn/headList"; +import type { + CommonTableProps, + TableUpperProps, +} from "../../common/type/header"; +import { ruseHstryHeadListExport } from "./headList"; +import { fetchType, getFetch, postFectch, putFectch } from "@/components/lib/api"; +import type { DmdCoReqType } from "../coMatRvw/type"; +import toast from "react-hot-toast"; +import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; + +const ruseHistHeadList = ruseHstryHeadListExport; + +export type ruseHstryState = { + ruseHist: { + isInit: boolean; + rvwHistRes: DmdCoReqType[]; + ruseHistHeadList: TableUpperProps[]; + }; +}; + +export const ruseHstryStateExport: ruseHstryState = { + ruseHist: { + isInit: false, + rvwHistRes: [], + ruseHistHeadList: ruseHistHeadList, + }, +}; + +export type ruseHstryType = ruseHstryState & ruseHstryActions; + +// +export const addRgtrAndMdfrToDemandObj = (data: DmdCoReqType) => { + // 최상위 demand 객체 + // demandCo가 있을 경우 + if (data.demandCo) { + data.demandCo.rgtr = TEST_UUID; + data.demandCo.mdfr = TEST_UUID; + } + + + return data; +}; + + + +type ruseHstryActions = { + ruseRvwHistInit: ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd?: string + ) => void; + ruseRvwCalUpdate: (data: DmdCoReqType) => void; + ruseRvwSearch: ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string, + dmdCoNm: string, + ruseUtztnFcltNm: string + ) => void; + ruseRvwInqua: ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string + ) => void; + ruseRvwComplete:( data: Number[]) => void; +}; + +const irvwHistInit = async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, +) => { + let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + + // console.log("sggCd ", sggCd); + // if (sggCd) { + // url += `/${sggCd}`; + // } + + const rvwHistRes = await getFetch(fetchType.WR, url); + + return rvwHistRes; +}; + +export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( + set: any +) => { + return { + ruseRvwHistInit: async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd?: string + ) => { + const rvwHistRes = await irvwHistInit( + pageNo, + pageCnt, + startYear, + endYear, + // sggCd + ); + + return set((state: ruseHstryState) => ({ + ...state, + ruseHist: { + ...state.ruseHist, + init: false, + rvwHistRes: rvwHistRes, + }, + })); + }, + ruseRvwCalUpdate: async (data: DmdCoReqType) => { + try { + + const updatedData = addRgtrAndMdfrToDemandObj(data); + const update = await putFectch( + `/reuseFc/request/cst`, + updatedData + ); + if (update.status === 200) { + toast.success("매칭 계산 저장이 완료입니다."); + const rvwHistRes = await irvwHistInit( + 1, + 10, + new Date().getFullYear().toString(), + new Date().getFullYear().toString(), + ); + return set((state: ruseHstryState) => ({ + ...state, + ruseHist: { + ...state.ruseHist, + init: false, + rvwHistRes: rvwHistRes, + }, + })); + } + } catch (e) { + toast.error("매칭 계산 저장이 오류입니다."); + console.log(e); + } + }, + ruseRvwSearch: async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string, + dmdCoNm: string, + ruseUtztnFcltNm: string + ) => { + let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + + if (sggCd.length > 0) { + url += `/${sggCd}`; + } else { + url += `/0`; + } + if (dmdCoNm?.length > 0) { + url += `/${dmdCoNm}`; + } else { + url += `/0`; + } + if (ruseUtztnFcltNm?.length > 0) { + url += `/${ruseUtztnFcltNm}`; + } else { + url += `/0`; + } + const rvwHistRes = await getFetch(fetchType.WR, url); + + return set((state: ruseHstryState) => ({ + ...state, + ruseHist: { + ...state.ruseHist, + rvwHistRes: rvwHistRes, + }, + })); + }, + ruseRvwInqua: async ( + pageNo: number, + pageCnt: number, + startYear: string, + endYear: string, + sggCd: string + ) => { + try { + let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + if (sggCd.length > 0) { + url += `/${sggCd}/0`; + } + const rvwHistRes = await getFetch(fetchType.WR, url); + return set((state: ruseHstryState) => ({ + ...state, + ruseHist: { + ...state.ruseHist, + rvwHistRes: rvwHistRes, + }, + })); + } catch (e) { + console.log(" error ", e); + } + }, + ruseRvwComplete: async (data: Number[]) => { + try { + const complet = await postFectch( + `/reuseFc/request/completeCheck`, + data, + fetchType.WR + ); + if (complet.status === 200) { + toast.success("완료 처리가 완료되었습니다."); + const rvwHistRes = await irvwHistInit( + 1, + 10, + new Date().getFullYear().toString(), + new Date().getFullYear().toString(), + ); + } + } catch (e) { + console.log(" error ", e); + toast.error("완료 처리 중 오류가 발생했습니다."); + } + }, + }; +}; + + + diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx new file mode 100644 index 000000000..5754c02fc --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx @@ -0,0 +1,137 @@ +import type { SelectDataType } from "@common/business_components"; +import type { rainChkZodType } from "."; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { buildingType } from "../../../common/code/index"; +export const useTypes = [ + "조경용수", + "청소화장실용수", + "세척살수용수", + "농업용수", + "공업용수", + "친수용수", + "기타", +]; + +export type Buildingtype = { + bdstInfoPk: number; + ctpvCd: 1; + sggCd: 1001; + daddr: "string3"; + bdstNm: "string"; + frstArchPrmsnDt: "2025-03-25"; + archArea: 0; + regDt: "2025-04-16"; + rgtr: null; + mdfcnDt: "2025-04-16"; + mdfr: null; +}; + +export const buildingToExcelType = ( + buildings: Buildingtype[], + sidoCode: SelectDataType[], + sggCdoe: SelectDataType[], +) => { + const excelData: string[][] = []; + buildings.forEach((building) => { + useTypes.forEach((useType) => { + excelData.push([ + building.bdstInfoPk.toString(), + sidoCode.find((sido) => sido.val === building.ctpvCd.toString()) + ?.text || "", + sggCdoe.find((sido) => sido.val === building.sggCd.toString()) + ?.text || "", + building.daddr, + building.bdstNm, + "", + "", + "", + "", + useType, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ]); + }); + }); + return excelData; + +}; + +export const excelToRainChkData = (excelData: any, year: string) => { + const rainChkgdData: rainChkZodType[] = excelData.map((item: any) => { + if (item["점검일"] !== undefined) { + const checkYmd = item["점검일"].replace(/\./g, "") + let vltnDt = item["과태료 부과일(위반일)"] + if (vltnDt !== undefined) { + vltnDt = vltnDt ? vltnDt.replace(/\./g, "") : ""; + } + const makeGuidDetail = Array.from({ length: 12 }, (_, i) => { + let month = `${i + 1}`; + const splyData = item[`보급_${month}월`]; + const ruseData = item[`빗물_${month}월`]; + if (month.length < 2) month = "0" + month; + return { + utztnUsgCd: item["이용용도"], + ym: year + month, + chckYmd: checkYmd, + splyUswtrUtztnQnt: Number(splyData ?? 0), + ruseFcltUtztnQnt: Number(ruseData ?? 0), + flg: "RNB", + }; + }); + + return { + + bdstRuseFcltFk: Number(item["NO"]), + chckYmd: checkYmd, + spwtPositn: item["유량측정방법"] ?? "", + rgtr: TEST_UUID, + oilqMeasMthd: item["운영관리형태"] ?? "", + anlOperCst: Number(item["연간운영비(원/년)"]) ?? 0, + operMngCd: item["유량측정방법"] ?? "", + guideDetails: makeGuidDetail, + rainyDetails: [ + { + utztnUsgCd: item["이용용도"] ?? "", + ph: Number(item["PH"] ?? 0), + trbt: Number(item["탁도"] ?? 0), + grssClfg: Number(item["총대장균군수"] ?? 0), + etc: item["기타"] ?? "", // 필드 누락 방지 + vltnDt: vltnDt ?? "", + vltnMttr: item["위반사항"] ?? "", + levyAmt: Number(item["과태료부과금액(천원)"] ?? 0), + rgtr: TEST_UUID, + }, + ], + }; + } + }).filter((el: any) => el !== undefined); + return rainChkgdData; +}; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx new file mode 100644 index 000000000..3cc75d70c --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -0,0 +1,223 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { z } from "zod"; + +export const chkgdRegExcel = z.object({ + bdstInfoPk: z.number().default(111111111), + chckYr: z + .string() + .default("") + .refine((year) => year.length === 0 || /^\d{4}$/.test(year), { + message: "4자리 연도만 입력하세요", + }) + .refine( + (year) => { + if (year.length === 0) return true; // 빈 문자열은 통과 + const numYear = Number(year); + return !isNaN(numYear) && numYear <= new Date().getFullYear(); + }, + { message: "현재 년도보다 높은 수를 입력할 수 없습니다." } + ), + file: z.instanceof(File).optional(), +}); + +export const guideDetail = z.object({ + chkgdnFk: z.number().nullable(), + utztnUsgCd: z.string(), + chckYmd: z.string(), + ym: z.string(), + splyUswtrUtztnQnt: z.number(), + ruseFcltUtztnQnt: z.number(), + flg: z.string(), +}); +export const rainChkZod = z.object({ + chkgdnPk: z.number().nullable(), + bdstRuseFcltFk: z.number(), + chckYmd: z.string(), + cityText:z.string().nullish(), + sggText:z.string().nullish(), + prcsCpct: z.number().nullable(), + spwtPositn: z.string().nullable(), + regDt: z.string().nullable(), + rgtr: z.string().nullable(), + mdfcnDt: z.string().nullable(), + mdfr: z.string().nullable(), + flg: z.string().nullable(), + guideDetails: z.array(guideDetail), + building: z + .object({ + bdstInfoPk: z.number(), + ctpvCd: z.number(), + sggCd: z.number(), + daddr: z.string().nullable(), + bdstNm: z.string(), + frstArchPrmsnDt: z.string().nullable(), + archArea: z.number(), + regDt: z.string().nullable(), + rgtr: z.string().nullable(), + mdfcnDt: z.string().nullable(), + mdfr: z.string().nullable(), + }) + .nullable(), + rainyDetails: z.array( + z.object({ + chkgdnFk: z.number().nullable(), + utztnUsgCd: z.string(), + ph: z.number().nullable(), + trbt: z.number().nullable(), + grssClfg: z.number().nullable(), + etc: z.string().nullable(), + vltnDt: z.string().nullable(), + vltnMttr: z.string().nullable(), + levyAmt: z.number().nullable(), + regDt: z.string().nullable(), + rgtr: z.string().nullable(), + mdfcnDt: z.string().nullable(), + mdfr: z.string().nullable(), + }) + ), +}); + +export type chHeadType = { + headNm: string; + text: string; + length: number; + rows?: number; +}; + +export type splRuseWaterType = { + utztnUsgCd: string; + splTotal: number; + spl01: number; + spl02: number; + spl03: number; + spl04: number; + spl05: number; + spl06: number; + spl07: number; + spl08: number; + spl09: number; + spl10: number; + spl11: number; + spl12: number; + ruseTotal: number; + ruse01: number; + ruse02: number; + ruse03: number; + ruse04: number; + ruse05: number; + ruse06: number; + ruse07: number; + ruse08: number; + ruse09: number; + ruse10: number; + ruse11: number; + ruse12: number; +}; + +export const rainHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "ruseWater", text: "연간보급수 이용량(m³)", length: 13 }, + { headNm: "detailQulity", text: "분기", length: 11 }, + { headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 }, +]; + +export const rainChkgdnHeadListExport: TableUpperProps[] = [ + { id: "order", title: "순번", mergeCell: true }, + { id: "checkBox", title: "선택", mergeCell: true }, + { id: "cityText", title: "시도", mergeCell: true }, + { id: "sggText", title: "시군구", mergeCell: true }, + { id: "bdstNm", title: "시설명", mergeCell: true }, + { id: "operMngCd", title: "운영관리형태", mergeCell: true }, + { id: "oilqMeasMthd", title: "유량측정방법", mergeCell: true }, + { id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true }, + { id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true }, + { id: "chckYmd", title: "점검일", mergeCell: true }, + { id: "utztnUsgCd", title: "이용용도" }, + { id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, + { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" }, + { + id: "ph", + title: "PH", + upName: "useQuilty", + upSequnce: 1, + type: "number", + edit: true, + }, + { + id: "trbt", + title: "탁도", + upName: "useQuilty", + upSequnce: 2, + type: "number", + edit: true, + }, + { + id: "grssClfg", + title: "총대장균군수", + upName: "useQuilty", + upSequnce: 3, + type: "number", + edit: true, + }, + + { + id: "vltnDt", + title: "부과일(위반일)", + upName: "feeHist", + upSequnce: 1, + type: "text", + edit: true, + }, + { + id: "vltnMttr", + title: "위반사항", + upName: "feeHist", + upSequnce: 2, + type: "text", + edit: true, + }, + { + id: "levyAmt", + title: "부과금액(천원)", + upName: "feeHist", + upSequnce: 3, + type: "number", + edit: true, + }, +]; + +export const ruseHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + { id: "ruseTotal", title: "계", upName: "ruseWater", upSequnce: 1 }, + { id: "ruse01", title: "1월", upName: "ruseWater", upSequnce: 2 , edit: true }, + { id: "ruse02", title: "2월", upName: "ruseWater", upSequnce: 3 , edit: true }, + { id: "ruse03", title: "3월", upName: "ruseWater", upSequnce: 4 , edit: true }, + { id: "ruse04", title: "4월", upName: "ruseWater", upSequnce: 5 , edit: true }, + { id: "ruse05", title: "5월", upName: "ruseWater", upSequnce: 6 , edit: true }, + { id: "ruse06", title: "6월", upName: "ruseWater", upSequnce: 7 , edit: true }, + { id: "ruse07", title: "7월", upName: "ruseWater", upSequnce: 8 , edit: true }, + { id: "ruse08", title: "8월", upName: "ruseWater", upSequnce: 9 , edit: true }, + { id: "ruse09", title: "9월", upName: "ruseWater", upSequnce: 10, edit: true }, + { id: "ruse10", title: "10월", upName: "ruseWater", upSequnce: 11 , edit: true }, + { id: "ruse11", title: "11월", upName: "ruseWater", upSequnce: 12 , edit: true }, + { id: "ruse12", title: "12월", upName: "ruseWater", upSequnce: 13 , edit: true }, +]; + +export const splHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + { id: "splTotal", title: "계", upName: "splWater", upSequnce: 1}, + { id: "spl01", title: "1월", upName: "splWater", upSequnce: 2, edit: true }, + { id: "spl02", title: "2월", upName: "splWater", upSequnce: 3, edit: true }, + { id: "spl03", title: "3월", upName: "splWater", upSequnce: 4, edit: true }, + { id: "spl04", title: "4월", upName: "splWater", upSequnce: 5, edit: true }, + { id: "spl05", title: "5월", upName: "splWater", upSequnce: 6, edit: true }, + { id: "spl06", title: "6월", upName: "splWater", upSequnce: 7, edit: true }, + { id: "spl07", title: "7월", upName: "splWater", upSequnce: 8, edit: true }, + { id: "spl08", title: "8월", upName: "splWater", upSequnce: 9, edit: true }, + { id: "spl09", title: "9월", upName: "splWater", upSequnce: 10, edit: true }, + { id: "spl10", title: "10월", upName: "splWater", upSequnce: 11, edit: true }, + { id: "spl11", title: "11월", upName: "splWater", upSequnce: 12, edit: true }, + { id: "spl12", title: "12월", upName: "splWater", upSequnce: 13, edit: true }, +]; + diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx new file mode 100644 index 000000000..ff612a0fb --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -0,0 +1,271 @@ +import { buildingType } from "@/app/store/common/code"; +import { + TEST_UUID, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + deleteFectch, + fetchType, + getFetch, + postFectch, + putFectch, +} from "@/components/lib/api"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import type { SelectDataType } from "@common/business_components/ui"; +import { z } from "zod"; +import { buildingToExcelType } from "./excel"; +import { + rainChkgdnHeadListExport, + rainChkZod, + rainHeadNmExport, + ruseHeadListExport, + splHeadListExport, + type chHeadType, +} from "./headList"; +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export type rainChkZodType = z.infer<typeof rainChkZod>; +const fcltDiv = [ + { val: "RST", text: "중수도시설" }, + { val: "RNB", text: "빗물이용시설" }, +]; + +const rainHeadNm = rainHeadNmExport; +const rainChkgdnHeadList = rainChkgdnHeadListExport; +const ruseHeadList = ruseHeadListExport; +const splHeadList = splHeadListExport; + +const rainChk: rainChkZodType[] = []; + +export type rainChkgdnState = { + rainChkgdn: { + rainChkgdnHeadList: TableUpperProps[]; + rainChk: rainChkZodType[]; + rainHeadNm: chHeadType[]; + ruseHeadList: TableUpperProps[]; + splHeadList: TableUpperProps[]; + isInit: boolean; + isModalClose: boolean; + fcltDiv: SelectDataType[]; + excelList: []; + isSttn: boolean; + }; +}; + +type rainChkgdnActions = { + rainChkInit: (sggCd: string, searchTxt: string) => void; + rainChkDetilUpdate(rainChk: rainChkZodType[]): Promise<void>; + rainChkGuidUpdate(rainChk: rainChkZodType): Promise<void>; + rainChkExcelDownload( + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ): Promise<void>; + rainChkExcelDataUPload(excelData: rainChkZodType[]): Promise<void>; + rainChkDelete(chkgdnPks: number[]): Promise<void>; + rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit :boolean) => void; +}; + +export type rainChkgdnType = rainChkgdnState & rainChkgdnActions; + +export const rainChkgdnStateExport: rainChkgdnState = { + rainChkgdn: { + rainChkgdnHeadList: rainChkgdnHeadList, + rainHeadNm: rainHeadNm, + rainChk: rainChk, + splHeadList: splHeadList, + ruseHeadList: ruseHeadList, + isInit: false, + isModalClose: false, + excelList: [], + fcltDiv: fcltDiv, + isSttn: false, + }, +}; + +const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => { + const chData = data.map((item) => { + item.mdfr = TEST_UUID; + item.rgtr = TEST_UUID; + if (type === "rainyDetails") { + item.rainyDetails.forEach((item) => { + item.mdfr = TEST_UUID; + item.rgtr = TEST_UUID; + }); + } else { + } + return item; + }); + + return chData; +}; + +const initFetch = async (txt: string | undefined, sgg: string | undefined) => { + const rainRes = await getFetch( + "wr", + `/reuseChk/rainy` + (txt ? `/${txt}` : "2024") + (sgg ? `/${sgg}` : "/0") + ); + return rainRes; +}; + +export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( + set: any +) => { + return { + rainChkExcelDownload: async ( + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ) => { + // 로그인한 아이디의 시도 or 시군구로 들어가야하는데 현재는 그냥 0으로 하드코딩 + const data = await getFetch( + fetchType.WR, + `/reuseFc/buildingFlg/${buildingType.RNB}/41` + ); + const excelList = buildingToExcelType(data, sidoCode, sggCode); + set((state: rainChkgdnState) => ({ + rainChkgdn: { + ...state.rainChkgdn, + excelList: excelList, + }, + })); + }, + rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => { + try { + const res = await postFectch("/reuseChk/rainy", excelData); + if (res.ok) { + ToastFormUi({ + body: "지도점검 등록이 완료되었습니다. ", + title: "지도점검 등록", + }); + } + const rainChkRes = await initFetch("2024", "0"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + } catch (error) { + console.log(error); + ToastFormUi({ + body: "지도점검 등록이 실패되었습니다.", + title: "지도점검 등록", + }); + } + }, + rainChkInit: async (searchTxt: string, sggCd: string) => { + try { + const rainChkRes = await initFetch(searchTxt, sggCd); + + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + } catch (error) { + console.log(error); + } + }, + rainChkDetilUpdate: async (rainChk: rainChkZodType[]) => { + try { + const chData = await makeMdfrChk(rainChk, "rain", "rainDetil"); + + const res = await postFectch("/reuseChk/rainy", chData); + if (res.ok) { + ToastFormUi({ + body: "지도점검 보급수 및 밋물이용량 수정이 완료되었습니다. ", + title: "지도점검 수정", + }); + } + const rainChkRes = await initFetch("2024", "0"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + } catch (error) { + console.log(error); + alert("수정에 실패하였습니다."); + } + }, + rainChkGuidUpdate: async (rainChk: rainChkZodType) => { + try { + //const chData = await makeMdfrChk(rainChk, "guideDetails", "rainDetil"); + const res = await putFectch("/reuseChk/rainy", rainChk); + if (res.ok) { + ToastFormUi({ + body: "지도점검 수질 및 과태료 수정이 완료되었습니다. ", + title: "지도점검 수정", + }); + } + const rainChkRes = await initFetch("2024", "0"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + } catch (error) { + console.log(error); + } + }, + rainChkDelete: async (chkgdnPks: number[]) => { + try { + const res = await deleteFectch(`/reuseChk/rainy`, chkgdnPks); + + if (res) { + ToastFormUi({ + body: "삭제가 완료되었습니다. ", + title: "지도점검 삭제", + }); + } + const rainChkRes = await initFetch("2024", "0"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + } catch (error) { + ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" }); + } + }, + rainChkHeadChange: async (rainChkgdn: rainChkgdnState , isEdit :boolean) => { + try { + const rainChkgdnHeadList = rainChkgdn.rainChkgdn.rainChkgdnHeadList; + const splHeadList = rainChkgdn.rainChkgdn.splHeadList; + const ruseHeadList = rainChkgdn.rainChkgdn.ruseHeadList; + + rainChkgdnHeadList.forEach(( head) => { + if(head.edit!== undefined) + head.edit=isEdit; + }); + splHeadList.forEach((head) => { + if(head.edit!== undefined) + head.edit=isEdit; + }); + ruseHeadList.forEach((head) => { + if(head.edit!== undefined) + head.edit=isEdit; + }); + + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChkgdnHeadList: rainChkgdnHeadList, + splHeadList:splHeadList, + ruseHeadList:ruseHeadList + }, + })); + } catch (error) { + console.log(error); + } + }, + }; +}; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx new file mode 100644 index 000000000..1364d6660 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx @@ -0,0 +1,237 @@ +import type { SelectDataType } from "@common/business_components/ui"; +import { useTypes, type Buildingtype } from "../rainChkgdn/excel"; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { rswtChkZodType } from "."; + +export const rswtMakeExcelSample = ( + buildings: Buildingtype[], + sidoCode: SelectDataType[], + sggCdoe: SelectDataType[] +) => { + const excelSite = { + guideDetail: [] as string[][], + polutions: [] as string[][], + }; + //첫번째 시트 데이터 이용량 현황 양식만들기 + buildings.forEach((building) => { + useTypes.forEach((useType) => { + excelSite.guideDetail.push([ + building.bdstInfoPk.toString(), + sidoCode.find((sido) => sido.val === building.ctpvCd.toString()) + ?.text || "", + sggCdoe.find((sido) => sido.val === building.sggCd.toString())?.text || + "", + building.bdstNm, + building.daddr, + "", + "", + "", + "", + "", + "", + useType, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ]); + }); + }); + + //두번째 시트 양식 만들기 오염도 + buildings.forEach((building) => { + useTypes.forEach((useType) => { + excelSite.polutions.push([ + building.bdstInfoPk.toString(), + sidoCode.find((sido) => sido.val === building.ctpvCd.toString()) + ?.text || "", + sggCdoe.find((sido) => sido.val === building.sggCd.toString())?.text || + "", + building.bdstNm, + building.daddr, + "", + "", + "", + "", + "", + useType, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ]); + }); + }); + + return excelSite; +}; + +export const excelToRswtGuideData = (excelData: any, year: string) => { + const rainChkgdData: rswtChkZodType[] = excelData + .map((item: any) => { + if (item["점검일"] !== undefined) { + + const checkYmd = item["점검일"].replace(/\./g, "") + + const makeGuidDetail = Array.from({ length: 12 }, (_, i) => { + let month = `${i + 1}`; + const splyData = item[`대체수_${month}월`]; + const rswtData = item[`중수_${month}월`]; + if (month.length < 2) month = "0" + month; + return { + ym: year + month, + chckYmd: checkYmd, + splyUswtrUtztnQnt: splyData !== "" ? splyData : 0, + ruseFcltUtztnQnt: rswtData !== "" ? rswtData : 0, + }; + }); + + return { + bdstRuseFcltFk: Number(item["NO"]), + operMngCd: item["운영관리형태"], + anlOperCst: Number(item["연간운영비(원/년)"]) ?? 0, + prcsCpct: item["처리용량(m³)"], + spwtPositn: item["채수지점"], + chckYmd:checkYmd, + utztnUsgCd: item["이용용도"], + guideDetails: makeGuidDetail, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + + }; + } + }) + .filter((el: any) => el !== undefined); + return rainChkgdData; +}; + +export const excelToRswtPolution = (excelData: any, year: string) => { + const rainChkgdData: rswtChkZodType[] = excelData + .map((item: any) => { + if (item["점검일"] !== undefined) { + + const checkYmd = item["점검일"].replace(/\./g, "") + let vltnDt = item["과태료 부과일(위반일)"] + if (vltnDt !== undefined) { + vltnDt = vltnDt .replace(/\./g, "") + } + + + const makePoluttion = Array.from({ length: 4 }, (_, i) => { + let quarter = `${i+1}`; + const grssClfgUswtr = item[`${quarter}분기_총대장균군수`]; + const combResdch = item[`${quarter}분기_결합잔류염소`]; + const trbt = item[`${quarter}분기_탁도`]; + const bod = item[`${quarter}분기_BOD`]; + const smll = item[`${quarter}분기_냄새`]; + const crmty = item[`${quarter}분기_색도`]; + const tph = item[`${quarter}분기_T-P`]; + const tn = item[`${quarter}분기_T-N`]; + const ph = item[`${quarter}분기_PH`]; + const clIonQnt = item[`${quarter}분기_염화물`]; + + return { + + node: quarter, + chckYmd: checkYmd, + grssClfgUswtr: grssClfgUswtr !== "" ? grssClfgUswtr : 0, + combResdch: combResdch !== "" ? combResdch : 0, + trbt: trbt !== "" ? trbt : 0, + bod: bod !== "" ? bod : 0, + smll: smll !== "" ? smll : 0, + crmty: crmty !== "" ? crmty : 0, + tph: tph !== "" ? tph : 0, + tn: tn !== "" ? tn : 0, + ph: ph !== "" ? ph : 0, + clIonQnt: clIonQnt !== "" ? clIonQnt : 0, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + }; + }); + + return { + bdstRuseFcltFk: Number(item["NO"]), + chckYmd: checkYmd, + utztnUsgCd: item["이용용도"], + operMngCd: item["운영관리형태"], + anlOperCst: Number(item["연간운영비(원/년)"]) ?? 0, + prcsCpct: item["처리용량(m³)"], + spwtPositn: item["채수지점"], + rswtrPollutions: makePoluttion, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + rswtrDetails: [ + { + utztnUsgCd: item["이용용도"], + vltnDt:vltnDt, + vltnMttr: item["위반사항"], + levyAmt: Number(item["과태료 부과금액(천원)"]) ?? 0, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + }, + ], + }; + } + }) + .filter((el: any) => el !== undefined); + return rainChkgdData; +}; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx new file mode 100644 index 000000000..0e690dbb9 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx @@ -0,0 +1,231 @@ +import { z } from "zod"; +import { guideDetail, type chHeadType } from "../rainChkgdn/headList"; +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export type polutionType = { + chkgdnDtlFk: number; + utztnUsgCd: string; + node: number; + grssClfgUswtr: number; + combResdch: number; + trbt: number; + bod: number; + smll: number; + crmty: number; + tph: number; + tn: number; + ph: number; + clIonQnt: number; + edit?:boolean +}; +const reuseBuildingZod = z.object({ + bdstRuseFcltPk: z.number().nullish(), + bdstInfoFk: z.number().nullish(), + instlCmptnDt: z.string().nullable(), // Assuming it's a date string or null + instlCst: z.number(), + operMngCd: z.string().nullable(), + instlCstSprtYn: z.string().nullable(), // Assuming 'Y'/'N' or similar, or null + instlCstSprtAmt: z.number(), + grntchBdstCertYn: z.string().nullable(), + grntchBdstCertYr: z.string().nullable(), // Could be z.number().nullable() if it's strictly a year number + grntchBdstCertNo: z.string().nullable(), + grntchBdstCertGrd: z.string().nullable(), + crgRdctYn: z.string().nullable(), + crgRdctBss: z.string().nullable(), + crgRdctAmt: z.number(), + ruseFcltFlg: z.string().nullable(), + bdstUsgCd: z.number(), + lawTrgtYn: z.string().nullable(), // Assuming 'Y'/'N' or similar, or null + rgtr: z.string(), // Assuming this is a UUID + mdfr: z.string(), // Assuming this is a UUID +}); + +const buildingZod = z.object({ + bdstInfoPk: z.number().nullable(), + ctpvCd: z.number(), + sggCd: z.number(), + daddr: z.string().nullable(), + bdstNm: z.string(), + frstArchPrmsnDt: z.string().nullable(), // Assuming it's a date string or null + archArea: z.number(), + rgtr: z.string(), // Assuming this is a UUID + mdfr: z.string(), // Assuming this is a UUID +}); + +export const rswtrPolutionsZode = z.object({ + chkgdnDtlFk: z.string().nullish(), + utztnUsgCd: z.string().nullish(), + node: z.string().nullish(), + grssClfgUswtr: z.number(), + combResdch: z.number(), + trbt: z.number(), + bod: z.number(), + smll: z.number(), + crmty: z.number(), + tph: z.number(), + tn: z.number(), + ph: z.number(), + clIonQnt: z.number(), + regDt: z.string().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.string().nullish(), + mdfr: z.string().nullish(), + edit:z.boolean().nullish() +}); + +export const rswtrDetailsZod = z.object({ + chkgdnDtlPk: z.string().nullish(), + chkgdnFk: z.string().nullish(), + utztnUsgCd: z.string(), + vltnDt: z.string(), + vltnMttr: z.string(), + levyAmt: z.number(), + regDt: z.string().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.string().nullish(), + mdfr: z.string().nullish(), +}) + +export const rswtChkgdZod = z.object({ + chkgdnPk: z.number().nullish(), + bdstRuseFcltFk: z.number().nullish(), + chckYmd: z.string(), + prcsCpct: z.number(), + dtfcltCpct: z.number(), + spwtPositn: z.string(), + regDt: z.string().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.string().nullish(), + mdfr: z.string().nullish(), + anlOperCst:z.number(), + oilqMeasMthd: z.string().nullish(), + utztnUsgCd: z.string(), + building: buildingZod, + guideDetails: z.array(guideDetail), + reuseBuilding: reuseBuildingZod, + rswtrDetails: z.array( + rswtrDetailsZod + ), + rswtrPollutions: z.array(rswtrPolutionsZode), +}); + +export const rswtHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "연간 평균 이용수질", length: 10 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, +]; + +export const rswtChkHeadListExport: TableUpperProps[] = [ + { id: "order", title: "순번", mergeCell: true }, + { id: "checkBox", title: "선택", mergeCell: true }, + { id: "cityText", title: "시도" ,mergeCell: true }, + { id: "sggText", title: "시군구",mergeCell: true }, + { id: "bdstNm", title: "시설명" ,mergeCell: true}, + { id: "operMngCd", title: "운영관리형태" ,mergeCell: true}, + { id: "anlOperCst", title: "연간운영비(원/년)" ,mergeCell: true}, + { id: "prcsCpct", title: "처리용량(m³)" ,mergeCell: true}, + { id: "spwtPositn", title: "채수지점" ,mergeCell: true}, + { id: "chckYmd", title: "점검일" ,mergeCell: true}, + { id: "utztnUsgCd", title: "이용용도" }, + { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)" ,type: "number"}, + { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number"}, + + { + id: "grssClfgUswtr", + title: "총대장균군수", + upName: "useQuilty", + upSequnce: 1, + type: "number" + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "useQuilty", + upSequnce: 2, + type: "number" + }, + { id: "trbt", title: "탁도", upName: "useQuilty", upSequnce: 3 ,type: "number"}, + { id: "bod", title: "BOD", upName: "useQuilty", upSequnce: 4 ,type: "number"}, + { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 ,type: "number"}, + { id: "crmty", title: "색도", upName: "useQuilty", upSequnce: 6 ,type: "number"}, + { id: "tn", title: "T-N", upName: "useQuilty", upSequnce: 7 ,type: "number"}, + { id: "tph", title: "T-P", upName: "useQuilty", upSequnce: 8 ,type: "number"}, + { id: "ph", title: "PH", upName: "useQuilty", upSequnce: 9,type: "number" }, + { id: "clIonQnt", title: "염화물", upName: "useQuilty", upSequnce: 10 ,type: "number"}, + + { + id: "vltnDt", + title: "부과일(위반일)", + upName: "feeHist", + upSequnce: 1, + type: "text", + edit:true + }, + { + id: "vltnMttr", + title: "위반사항", + upName: "feeHist", + upSequnce: 2, + type: "text", + edit:true + }, + { + id: "levyAmt", + title: "부과금액(천원)", + upName: "feeHist", + upSequnce: 3, + type: "number", + edit:true + }]; + + + export const rswtDetailHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + + { + id: "grssClfgUswtr", + title: "총대장균군수", + upName: "detailQulity", + upSequnce: 2, + edit: true, + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "detailQulity", + upSequnce: 3, + edit: true, + }, + { + id: "trbt", + title: "탁도", + upName: "detailQulity", + upSequnce: 4, + edit: true, + }, + { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5, edit: true }, + { + id: "smll", + title: "냄새", + upName: "detailQulity", + upSequnce: 6, + edit: true, + }, + { + id: "crmty", + title: "색도", + upName: "detailQulity", + upSequnce: 7, + edit: true, + }, + { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8, edit: true }, + { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9, edit: true }, + { id: "ph", title: "PH", upName: "detailQulity", upSequnce: 10, edit: true }, + { + id: "clIonQnt", + title: "염화물", + upName: "detailQulity", + upSequnce: 11, + edit: true, + }, + ]; + \ 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 new file mode 100644 index 000000000..eb0010047 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -0,0 +1,299 @@ +import { + TEST_UUID, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { + SelectDataType, + SheetParameter, +} from "@common/business_components"; +import { + deleteFectch, + fetchType, + getFetch, + postFectch, + putFectch, +} from "@/components/lib/api"; +import { buildingType } from "@/app/store/common/code"; +import { rswtMakeExcelSample } from "./excel"; +import { + rswtChkgdZod, + rswtChkHeadListExport, + rswtDetailHeadListExport, + rswtHeadNmExport, + rswtrDetailsZod, +} from "./headList"; +import { z } from "zod"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import { type chHeadType } from "../rainChkgdn/headList"; +import type { TableUpperProps } from "@/app/store/common/type/header"; + +const rswtChkHeadList = rswtChkHeadListExport; +const rswtDetailHeadList = rswtDetailHeadListExport; +const rswtHeadNm = rswtHeadNmExport; +export type rswtChkZodType = z.infer<typeof rswtChkgdZod>; +export type rswtrChkDetailsZodType = z.infer<typeof rswtrDetailsZod>; +export type rswtChkgdnState = { + rswtChkgdn: { + rswtChkHeadList: TableUpperProps[]; + rswtDetailHeadList: TableUpperProps[]; + + rswtHeadNm: chHeadType[]; + excelList: SheetParameter[]; + rswtChkRes: rswtChkZodType[]; + rswtChkPolGuide: rswtChkZodType[]; + isInit: boolean; + isSttn:boolean + }; +}; + +type rswtChkgdnActions = { + rswtChkgdnInit: (year: string, sggCd: string) => void; + rswtrChkExcelDownload( + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ): Promise<void>; + rswtChkExceDataUpload: (excelData: rswtChkZodType[]) => void; + rswtChkDelete: (chkgdnPks: number[]) => void; + rswtChkDetilsUpdate: (rswtChkDetails: rswtrChkDetailsZodType[]) => void; + rswtChkPolAndGuide: (chkGdnPk: number) => void; + rswtChkPolInit: () => void; + rwstPolAndGuideUpdate: (rswtChk: rswtChkZodType[]) => void; + rswtChkHeadChange: (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => void; +}; + +export type rswtChkgdnType = rswtChkgdnState & rswtChkgdnActions; + +export const rswtChkgdnStateExport: rswtChkgdnState = { + rswtChkgdn: { + rswtChkHeadList: rswtChkHeadList, + rswtDetailHeadList: rswtDetailHeadList, + rswtHeadNm: rswtHeadNm, + excelList: [{ + data: [[]], headerNum: 0, headerRange: "A1:V3", + sheetName: "", + startCol: '1' + }], + rswtChkRes: [], + rswtChkPolGuide: [], + isInit: false, + isSttn:false, + }, +}; + +const initFetch = async (year: string | undefined, sgg: string | undefined) => { + const rswtChkRes = await getFetch( + "wr", + `/reuseChk/reuseWater/` + + (year ? `${year}` : "2024") + + (sgg ? `/${sgg}` : "/0") + ); + return rswtChkRes; +}; + +const makeMdfrChk = (data: rswtChkZodType[] | rswtrChkDetailsZodType[]) => { + const chData = data.map((item) => { + item.mdfr = TEST_UUID; + item.rgtr = TEST_UUID; + if ("rswtrPollutions" in item) { + item?.rswtrPollutions.forEach((pollution) => { + pollution.mdfr = TEST_UUID; + pollution.rgtr = TEST_UUID; + }); + } + return item; + }); + return chData; +}; + +export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( + set: any +) => { + return { + rswtrChkExcelDownload: async ( + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ) => { + const data = await getFetch( + fetchType.WR, + `/reuseFc/buildingFlg/${buildingType.RST}/41` + ); + const excelList: any = []; + const exceldata = rswtMakeExcelSample(data, sidoCode, sggCode); + excelList.push({ + data: exceldata.guideDetail, + headerNum: 0, + headerRange: "A1:AI1", + }); + excelList.push({ + data: exceldata.polutions, + headerNum: 0, + headerRange: "A1:AZ1", + }); + + return set((state: rswtChkgdnState) => ({ + rswtChkgdn: { + ...state.rswtChkgdn, + excelList: excelList, + }, + })); + }, + rswtChkgdnInit: async (year: string, sggCd: string) => { + try { + const rswtChkRes = await initFetch("2024", sggCd); + + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes, + isInit: false, + }, + })); + } catch (error) { + console.log(error); + } + }, + rswtChkExceDataUpload: async (excelData: rswtChkZodType[]) => { + try { + const res = await postFectch("/reuseChk/reuseWater", excelData); + if (res.ok) { + ToastFormUi({ + body: "지도점검 등록이 완료되었습니다.", + title: "지도점검 등록", + }); + } + // const rainChkRes = await initFetch('2024', '0'); + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + }, + })); + } catch (error) { + console.log(error); + } + }, + rswtChkDetilsUpdate: async (rswtChk: rswtrChkDetailsZodType[]) => { + try { + const chData = await makeMdfrChk(rswtChk); + + + const res = await putFectch("/reuseChk/reuseWater", chData); + if (res.ok) { + ToastFormUi({ + body: "지도점검 과태료 수정 수정이 완료되었습니다. ", + title: "지도점검 수정", + }); + } + const rswtChkRes = await initFetch("2024", "0"); + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes, + }, + })); + } catch (error) { + console.log(error); + alert("수정에 실패하였습니다."); + } + }, + rswtChkDelete: async (chkgdnPks: number[]) => { + try { + const res = await deleteFectch(`/reuseChk/reuseWater`, chkgdnPks); + + if (res.ok) { + ToastFormUi({ + body: "삭제가 완료되었습니다. ", + title: "지도점검 삭제", + }); + } + const rswtChkRes = await initFetch("2024", "0"); + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes, + }, + })); + } catch (error) { + ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" }); + } + }, + rswtChkPolAndGuide: async (chkGdnPk: number) => { + try { + const rswtChkPolGuide = await getFetch( + "wr", + `/reuseChk/reuseWater/${chkGdnPk}` + ); + + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkPolGuide: rswtChkPolGuide, + }, + })); + } catch (error) {} + }, + rswtChkPolInit: () => { + try { + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkPolGuide: undefined, + }, + })); + } catch (error) {} + }, + rwstPolAndGuideUpdate: async (rswtChk: rswtChkZodType[]) => { + try { + const chData = await makeMdfrChk(rswtChk); + const res = await putFectch( + `/reuseChk/reuseWater/${rswtChk[0]?.chkgdnPk}`, + chData[0] + ); + if (res.ok) { + ToastFormUi({ + body: "지도점검 연간 수질 및 보급수/중수도 수정이 완료되었습니다. ", + title: "지도점검 수정", + }); + } + const rswtChkRes = await initFetch("2024", "0"); + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes, + }, + })); + } catch (error) { + console.log(error); + ToastFormUi({ + body: "지도점검 연간 수질 및 보급수/중수도 수정이 실패하였습니다.. ", + title: "지도점검 수정", + }); + } + }, + rswtChkHeadChange: async (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => { + try { + const rswtChkHeadList = rswtChkgdn.rswtChkgdn.rswtChkHeadList; + const rswtDetailHeadList = rswtChkgdn.rswtChkgdn.rswtDetailHeadList; + rswtChkHeadList.forEach((head) => { + if (head.edit !== undefined) head.edit = isEdit; + }); + rswtDetailHeadList.forEach((head) => { + if (head.edit !== undefined) head.edit = isEdit; + }); + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkHeadList: rswtChkHeadList, + rswtDetailHeadList: rswtDetailHeadList, + }, + })); + } catch (e) {} + }, + }; +}; 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..d8160cab9 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/excel.tsx @@ -0,0 +1,55 @@ +import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; + +type NameMap = { + [key: string]: string; +}; + + const nameMap:NameMap = { + "세척수": "wshwtr", + "냉각수": "coolUswtr", + "청소수": "cleanUswtr", + "식수대": "drwtUswtr", + "희석용수": "dltnUswtr", + "중수도": "ithlRswtr", + "기타": "ithlEtcUswtr", + "청소화장실용수": "cleanToilUswtr", + "세척살수용수": "resn", + "조경용수": "lndwtr", + "친수용수": "hydUswtr", + "지하수충전": "ugwtChr", + "농업용수": "farmUswtr", + "하천등유지용수": "rvrKeepUswtr", + "공업용수": "indstUswtr" + }; + + +export const excelToPbnPrfData = (excelData: JSON[], year: string,pbnstRuseFcltFk:string) => { + // 월별 데이터 하나로 병합 + const merged = {} as any; + excelData.forEach((item: any) => { + const usageNm = item["용수명"] + const key = nameMap[usageNm as any]; + 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); + const data = pbnPrfData.map((item) => { + + return({ + ...(item as object), + pbnstRuseFcltFk:pbnstRuseFcltFk, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + }) + }) + return data; +}; \ 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..544f07516 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/headList.tsx @@ -0,0 +1,38 @@ +import { z } from "zod"; +import type { TableUpperProps } from "../../common/type/header"; + +export const pbnPrfRegExcelZod = z.object({ + ym: z.string().default(""), + bscFctNm: z.string().default(""), + pbnstRuseFcltFk:z.string(), + pbnstRuseFcltNm: z.string().default(""), + file: z.instanceof(File).optional(), +}); + +export const pbnstPrfmncHeadListExport: TableUpperProps[] = [ + { id: "sido", title: "시도" }, + { id: "gugun", title: "시군구" }, + { id: "bscFctNm", title: "시설명" }, + { id: "ruseUtztnFcltNm", title: "재이용시설명" }, + { id: "pbnstRuseFcltPk", title: "재이용시설코드" }, +]; + +export const monthHeadListExport: TableUpperProps[] = [ + { id: "용수명", title: "용수명" }, + { id: "total", title: "계" }, + { id: "1월", title: "1월" ,edit: true }, + { id: "2월", title: "2월" ,edit: true }, + { id: "3월", title: "3월" ,edit: true }, + { id: "4월", title: "4월" ,edit: true }, + { id: "5월", title: "5월" ,edit: true }, + { id: "6월", title: "6월" ,edit: true }, + { id: "7월", title: "7월" ,edit: true }, + { id: "8월", title: "8월" ,edit: true }, + { id: "9월", title: "9월" ,edit: true }, + { id: "10월", title: "10월" ,edit: true }, + { id: "11월", title: "11월" ,edit: true }, + { id: "12월", title: "12월" ,edit: true }, +]; + + + 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..87e827f60 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx @@ -0,0 +1,111 @@ +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; +import { pbnstPrfmncHeadListExport, monthHeadListExport } from "./headList"; +import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; +import type { FcPrfType, PrfType } from "./type"; +import type { TableUpperProps } from "../../common/type/header"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import toast from "react-hot-toast"; + +const pbnstPrfmncHeadList = pbnstPrfmncHeadListExport; +const monthHeadList = monthHeadListExport; + +export type pbnstPrfmncState = { + prfmnc: { + isInit: boolean; + pbnstPrfmncHeadList: TableUpperProps[]; + monthHeadList: TableUpperProps[]; + prfmncRes: FcPrfType[]; + 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; + prfmnRegData: (data: PrfType) => void; +}; + +const checkWrRegList = async () => { + const wrRegList = await getFetch("wr", `/reuseFc/pubDetailList`); + 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?.fcltCds, + fetchType.COMMON + ); + + + const findData = rainRes.data.map((item: pubHasuType) => { + const data = checkWrRegListRes?.fcPrfList.find( + (fclt: FcPrfType) => + fclt.pbnstRuseFcltPk.substring(0, 11) === item?.bscFctCd + ); + if(data !==undefined){ + return( { + + sido: item.sido, + gugun: item.gugun, + bscFctNm: item.bscFctNm, + bscFctCd: item.bscFctCd, + ruseUtztnFcltNm: data.ruseUtztnFcltNm, + pbnstRuseFcltPk: data.pbnstRuseFcltPk, + prfList: data.prfList, + }); + } + }); + + return findData; +}; +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); + } + }, + prfmnRegData: async (data: PrfType) => { + try { + const ressponse = await postFectch( + "/reuseFc/pubFcPrf", + data, + fetchType.WR + ); + + + if (ressponse.status === 200) { + toast.success("하수재이용 시설 실적 등록이 완료되었습니다."); + } + } catch (e) { + console.log(e); + toast.error("하수재이용 시설 실적 등록이 실패하였습니다."); + } + }, + }; +}; diff --git a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/type.tsx b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/type.tsx new file mode 100644 index 000000000..f6d5ff43f --- /dev/null +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/type.tsx @@ -0,0 +1,346 @@ +import { z } from "zod"; +import type { TableUpperProps } from "../../common/type/header"; +import { fcOperSchema } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; + +export const PrfSchema = z.object({ + pbnstRuseFcltFk: z.string(), + ym: z.string(), // 예: "202310" + wshwtr: z.number(), + coolUswtr: z.number(), + cleanUswtr: z.number(), + drwtUswtr: z.number(), + dltnUswtr: z.number(), + ithlRswtr: z.number(), + ithlEtcUswtr: z.number(), + cleanToilUswtr: z.number(), + resn: z.number(), + lndwtr: z.number(), + hydUswtr: z.number(), + rvrKeepUswtr: z.number(), + farmUswtr: z.number(), + ugwtChr: z.number(), + indstUswtr: z.number(), + regDt: z.date().default(new Date()).nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.date().default(new Date()).nullish(), + mdfr: z.string().nullish(), +}); + +export const FcPrfSchema = z.object({ + pbnstRuseFcltPk: z.string(), + ruseUtztnFcltNm: z.string(), + fcltCpct: z.number(), + ruseCpct: z.number(), + splyExtraCpct: z.number(), + regDt: z.date().default(new Date()), + rgtr: z.string(), + mdfcnDt: z.date().default(new Date()), + mdfr: z.string(), + prfList: z.array(PrfSchema), + operList:z.array(fcOperSchema), + bscFctNm: z.string().nullish(), +}); + +export type FcPrfType = z.infer<typeof FcPrfSchema>; +export type PrfType = z.infer<typeof PrfSchema>; + +export const initValInUsage = [ + { + usage: "wshwtr", + 용수명: "세척수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "coolUswtr", + 용수명: "냉각수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "cleanUswtr", + 용수명: "청소수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "drwtUswtr", + 용수명: "식수대", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "dltnUswtr", + 용수명: "희석용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "ithlRswtr", + 용수명: "중수도", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "ithlEtcUswtr", + 용수명: "기타", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, +]; + +export const initValOutUsage = [ + { + usage: "resn", + 용수명: "세척살수용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "cleanToilUswtr", + 용수명: "청소화장실용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "lndwtr", + 용수명: "조경용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "hydUswtr", + 용수명: "친수용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "ugwtChr", + 용수명: "지하수충전", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + { + usage: "farmUswtr", + 용수명: "농업용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + + { + usage: "rvrKeepUswtr", + 용수명: "하천등유지용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, + + { + usage: "indstUswtr", + 용수명: "공업용수", + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, + }, +]; + +export const subTotalObj = { + usage: "subtotal", + 용수명: "소계", + edit: false, + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, +}; + +export const totalObj = { + usage: "total", + 용수명: "총합계", + edit: false, + total: 0, + "1월": 0, + "2월": 0, + "3월": 0, + "4월": 0, + "5월": 0, + "6월": 0, + "7월": 0, + "8월": 0, + "9월": 0, + "10월": 0, + "11월": 0, + "12월": 0, +}; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/excel.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/excel.tsx new file mode 100644 index 000000000..78e0b878f --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/excel.tsx @@ -0,0 +1,41 @@ +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; + +type rainSttnExcelType = { + bdstInfoPk: number; + ctpvCd: 1; + sggCd: 1001; + daddr: "string3"; + bdstNm: "string"; + frstArchPrmsnDt: "2025-03-25"; + archArea: 0; + regDt: "2025-04-16"; + rgtr: null; + mdfcnDt: "2025-04-16"; + mdfr: null; +}; +export const buildingExcelRainSttn = (excelList: any[]) => { + const excelData: string[][] = []; + excelList.forEach((item, index) => { + excelData.push([ + String(item.cityText) ?? "", + String(item.sggText) ?? "", + String(item.bdstNm) ?? "", + item.operMngCd || String(item.operMngCd)===null? String(item.operMngCd) : "", + String(item.oilqMeasMthd) ?? "", + String(item.anlOperCst) ?? "", + String(item.dtfcltCpct) ?? "", + String(item.chckYmd) ?? "", + String(item.utztnUsgCd) ?? "", + String(item.totalSplyUswtrUtztnQnt) ?? "", + String(item.totalRuseFcltUtztnQnt) ?? "", + String(item.ph) ?? "", + String(item.trbt) ?? "", + String(item.grssClfg) ?? "", + String(item.vltnDt) ?? "", + String(item.vltnMttr) ?? "", + String(item.levyAmt) ?? "", + ]); + }); + + return excelData; +}; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx new file mode 100644 index 000000000..f112db0bd --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx @@ -0,0 +1,163 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export const rainChkgdnHeadSttnExport: TableUpperProps[] = [ + { id: 'order', title: "순번", mergeCell: true }, + { id: "cityText", title: "시도", mergeCell: true }, + { id: "sggText", title: "시군구", mergeCell: true }, + { id: "bdstNm", title: "시설명", mergeCell: true }, + { id: "operMngCd", title: "운영관리형태", mergeCell: true }, + { id: "oilqMeasMthd", title: "유량측정방법", mergeCell: true }, + { id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true }, + { id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true }, + { id: "chckYmd", title: "점검일", mergeCell: true }, + { id: "utztnUsgCd", title: "이용용도" }, + { id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, + { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" }, + { + id: "ph", + title: "PH", + upName: "useQuilty", + upSequnce: 1, + type: "number", + }, + { + id: "trbt", + title: "탁도", + upName: "useQuilty", + upSequnce: 2, + type: "number", + }, + { + id: "grssClfg", + title: "총대장균군수", + upName: "useQuilty", + upSequnce: 3, + type: "number", + }, + + { + id: "vltnDt", + title: "부과일(위반일)", + upName: "feeHist", + upSequnce: 1, + type: "text", + }, + { + id: "vltnMttr", + title: "위반사항", + upName: "feeHist", + upSequnce: 2, + type: "text", + }, + { + id: "levyAmt", + title: "부과금액(천원)", + upName: "feeHist", + upSequnce: 3, + type: "number", + }, +]; + +export const ruseSttnHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + { id: "ruseTotal", title: "계", upName: "ruseWater", upSequnce: 1 }, + { id: "ruse01", title: "1월", upName: "ruseWater", upSequnce: 2 }, + { id: "ruse02", title: "2월", upName: "ruseWater", upSequnce: 3 }, + { id: "ruse03", title: "3월", upName: "ruseWater", upSequnce: 4 }, + { id: "ruse04", title: "4월", upName: "ruseWater", upSequnce: 5 }, + { id: "ruse05", title: "5월", upName: "ruseWater", upSequnce: 6 }, + { id: "ruse06", title: "6월", upName: "ruseWater", upSequnce: 7 }, + { id: "ruse07", title: "7월", upName: "ruseWater", upSequnce: 8 }, + { id: "ruse08", title: "8월", upName: "ruseWater", upSequnce: 9 }, + { id: "ruse09", title: "9월", upName: "ruseWater", upSequnce: 10 }, + { id: "ruse10", title: "10월", upName: "ruseWater", upSequnce: 11 }, + { id: "ruse11", title: "11월", upName: "ruseWater", upSequnce: 12 }, + { id: "ruse12", title: "12월", upName: "ruseWater", upSequnce: 13 }, +]; + +export const splSttnHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + { id: "splTotal", title: "계", upName: "splWater", upSequnce: 1 }, + { id: "spl01", title: "1월", upName: "splWater", upSequnce: 2 }, + { id: "spl02", title: "2월", upName: "splWater", upSequnce: 3 }, + { id: "spl03", title: "3월", upName: "splWater", upSequnce: 4 }, + { id: "spl04", title: "4월", upName: "splWater", upSequnce: 5 }, + { id: "spl05", title: "5월", upName: "splWater", upSequnce: 6 }, + { id: "spl06", title: "6월", upName: "splWater", upSequnce: 7 }, + { id: "spl07", title: "7월", upName: "splWater", upSequnce: 8 }, + { id: "spl08", title: "8월", upName: "splWater", upSequnce: 9 }, + { id: "spl09", title: "9월", upName: "splWater", upSequnce: 10 }, + { + id: "spl10", + title: "10월", + upName: "splWater", + upSequnce: 11, + }, + { + id: "spl11", + title: "11월", + upName: "splWater", + upSequnce: 12, + }, + { + id: "spl12", + title: "12월", + upName: "splWater", + upSequnce: 13, + }, +]; + +export const rswtDetailHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + + { + id: "grssClfgUswtr", + title: "총대장균군수", + upName: "detailQulity", + upSequnce: 2, + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "detailQulity", + upSequnce: 3, + }, + { + id: "trbt", + title: "탁도", + upName: "detailQulity", + upSequnce: 4, + }, + { + id: "bod", + title: "BOD", + upName: "detailQulity", + upSequnce: 5, + }, + { + id: "smll", + title: "냄새", + upName: "detailQulity", + upSequnce: 6, + }, + { + id: "crmty", + title: "색도", + upName: "detailQulity", + upSequnce: 7, + }, + { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8}, + { + id: "tph", + title: "T-P", + upName: "detailQulity", + upSequnce: 9, + }, + { id: "ph", title: "PH", upName: "detailQulity", upSequnce: 10 }, + { + id: "clIonQnt", + title: "염화물", + upName: "detailQulity", + upSequnce: 11, + }, +]; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx new file mode 100644 index 000000000..9fd948856 --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -0,0 +1,129 @@ +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { type TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { getFetch } from "@/components/lib/api"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import type { SelectDataType } from "@common/business_components/ui"; +import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; +import { rainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { buildingExcelRainSttn } from "./excel"; + +const fcltDiv = [ + { val: "RST", text: "중수도시설" }, + { val: "RNB", text: "빗물이용시설" }, +]; + + +const rainChkgdnHeadList = rainChkgdnHeadSttnExport; +const ruseHeadList = ruseSttnHeadListExport; +const splHeadList = splSttnHeadListExport; +const rainHeadNm = rainHeadNmExport + +const rainChk: rainChkZodType[] = []; + +export type rainChkSttnState = { + rainChkSttn: { + rainChkRes: rainChkZodType[]; + fcltDiv: SelectDataType[]; + rainChkgdnHeadList: TableUpperProps[]; + rainHeadNm:chHeadType[]; + ruseHeadList: TableUpperProps[]; + splHeadList: TableUpperProps[]; + sttnExcelList:[] + isInit: boolean; + isSttn:boolean; + }; +}; + +type rainChkSttnActions = { + rainChkSttnInit: ( sggCd: string) => void; + rainChkSttnSearch: (searchTxt: string, sggCd: string) => void; + isRainSttn:(isSttn:boolean) => void; + getRainSttnExcel:(excelList:rainChkZodType[]) => void; +}; + +export type rainChkSttnType = rainChkSttnState & rainChkSttnActions; + +export const rainChkSttnStateExport: rainChkSttnState = { + rainChkSttn: { + rainChkRes: [], + fcltDiv: fcltDiv, + rainChkgdnHeadList: rainChkgdnHeadList, + ruseHeadList: ruseHeadList, + splHeadList: splHeadList, + rainHeadNm:rainHeadNm, + isInit: false, + sttnExcelList:[], + isSttn:false, + }, +}; + +const initFetch = async (txt: string | undefined, sgg: string | undefined) => { + const rainRes = await getFetch( + "wr", + `/reuseChk/rainy/` + (txt ? `${txt}` : "2024") + (sgg ? `/${sgg}` : "/0") + ); + return rainRes; +}; +export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( + set: any +) => { + return { + rainChkSttnInit: async ( sggCd: string) => { + try { + const rainChkRes = await initFetch(undefined, sggCd); + + return set((state: rainChkSttnState) => ({ + ...state, + rainChkSttn: { + ...state.rainChkSttn, + rainChkRes:rainChkRes + }, + })); + } catch (error) { + console.log(error); + } + }, + rainChkSttnSearch: async (searchTxt: string, sggCd: string) => { + try { + const rainChkRes = await initFetch(searchTxt, sggCd); + + return set((state: rainChkSttnState) => ({ + ...state, + rainChkSttn: { + ...state.rainChkSttn, + rainChkRes:rainChkRes + }, + })); + } catch (error) { + console.log(error); + } + },isRainSttn :(isSttn:boolean) =>{ + try { + return set((state: rainChkSttnState) => ({ + ...state, + rainChkSttn: { + ...state.rainChkSttn, + isSttn:isSttn + }, + })); + } catch (error) { + console.log(error); + } + },getRainSttnExcel: async (excelList:rainChkZodType[]) => { + try { + + const excelData = buildingExcelRainSttn(excelList) + + return set((state: rainChkSttnState) => ({ + ...state, + rainChkSttn: { + ...state.rainChkSttn, + sttnExcelList:excelData + }, + })); + } catch (error) { + console.log(error); + } + } + }; +}; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx new file mode 100644 index 000000000..ff104bc91 --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx @@ -0,0 +1,34 @@ + +export const buildingExcelRswtSttn = (excelList: any[]) => { + const excelData: string[][] = []; + excelList.forEach((item, index) => { + excelData.push([ + String(item.cityText) ?? "", + String(item.sggText) ?? "", + String(item.bdstNm) ?? "", + item.operMngCd===null ? "":item.operMngCd, + String(item.anlOperCst) ?? "", + String(item.prcsCpct) ?? "", + item.spwtPositn===null ? "":item.spwtPositn, + item.chckYmd ?? "", + String(item.utztnUsgCd) ?? "", + String(item.splyUswtrUtztnQnt) ?? "", + String(item.ruseFcltUtztnQnt) ?? "", + String(item.grssClfgUswtr) ?? "", + String(item.combResdch) ?? "", + String(item.trbt) ?? "", + String(item.bod) ?? "", + String(item.smll) ?? "", + String(item.tn) ?? "", + String(item.tph) ?? "", + String(item.ph) ?? "", + String(item.clIonQnt) ?? "", + String(item.vltnDt) ?? "", + String(item.vltnMttr) ?? "", + String(item.levyAmt) ?? "", + ]); + }); + + return excelData; + }; + \ No newline at end of file diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx new file mode 100644 index 000000000..f67e98101 --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx @@ -0,0 +1,142 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export const rswtChkSttnHeadListExport: TableUpperProps[] = [ + { id: "order", title: "순번", rowSpan: true }, + { id: "cityText", title: "시도", rowSpan: true }, + { id: "sggText", title: "시군구", rowSpan: true }, + { id: "bdstNm", title: "시설명", rowSpan: true }, + { id: "operMngCd", title: "운영관리형태", rowSpan: true }, + { id: "anlOperCst", title: "연간운영비(원/년)", rowSpan: true }, + { id: "prcsCpct", title: "처리용량(m³)", rowSpan: true }, + { id: "spwtPositn", title: "채수지점", rowSpan: true }, + { id: "chckYmd", title: "점검일", rowSpan: true }, + { id: "utztnUsgCd", title: "이용용도" }, + { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)", type: "number" }, + { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number" }, + + { + id: "grssClfgUswtr", + title: "총대장균군수", + upName: "useQuilty", + upSequnce: 1, + type: "number", + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "useQuilty", + upSequnce: 2, + type: "number", + }, + { + id: "trbt", + title: "탁도", + upName: "useQuilty", + upSequnce: 3, + type: "number", + }, + { + id: "bod", + title: "BOD", + upName: "useQuilty", + upSequnce: 4, + type: "number", + }, + { + id: "smll", + title: "냄새", + upName: "useQuilty", + upSequnce: 5, + type: "number", + }, + { + id: "crmty", + title: "색도", + upName: "useQuilty", + upSequnce: 6, + type: "number", + }, + { id: "tn", title: "T-N", upName: "useQuilty", upSequnce: 7, type: "number" }, + { + id: "tph", + title: "T-P", + upName: "useQuilty", + upSequnce: 8, + type: "number", + }, + { id: "ph", title: "PH", upName: "useQuilty", upSequnce: 9, type: "number" }, + { + id: "clIonQnt", + title: "염화물", + upName: "useQuilty", + upSequnce: 10, + type: "number", + }, + + { + id: "vltnDt", + title: "부과일(위반일)", + upName: "feeHist", + upSequnce: 1, + type: "text", + }, + { + id: "vltnMttr", + title: "위반사항", + upName: "feeHist", + upSequnce: 2, + type: "text", + }, + { + id: "levyAmt", + title: "부과금액(천원)", + upName: "feeHist", + upSequnce: 3, + type: "number", + }, +]; + +export const rswtDetailSttnHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + + { + id: "grssClfgUswtr", + title: "총대장균군수", + upName: "detailQulity", + upSequnce: 2, + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "detailQulity", + upSequnce: 3, + }, + { + id: "trbt", + title: "탁도", + upName: "detailQulity", + upSequnce: 4, + }, + { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5 }, + { + id: "smll", + title: "냄새", + upName: "detailQulity", + upSequnce: 6, + }, + { + id: "crmty", + title: "색도", + upName: "detailQulity", + upSequnce: 7, + }, + { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8}, + { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9 }, + { id: "ph", title: "PH", upName: "detailQulity", upSequnce: 10 }, + { + id: "clIonQnt", + title: "염화물", + upName: "detailQulity", + upSequnce: 11, + }, +]; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx new file mode 100644 index 000000000..b985cd944 --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx @@ -0,0 +1,107 @@ +import { rswtHeadNmExport } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList"; +import { rswtChkSttnHeadListExport, rswtDetailSttnHeadListExport } from "./headList"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { getFetch } from "@/components/lib/api"; +import { buildingExcelRswtSttn } from "./excel"; + +const rswtChkHeadList = rswtChkSttnHeadListExport; +const rswtDetailHeadList = rswtDetailSttnHeadListExport; +const rswtHeadNm = rswtHeadNmExport; + + +export type rswtChkSttnState = { + rswtChkSttn: { + rswtChkSttnHeadList: TableUpperProps[]; + rswtDetailSttnHeadList: TableUpperProps[]; + rswtSttnHeadNm: chHeadType[]; + sttnExcelList: []; + rswtChkRes: rswtChkZodType[]; + isInit: boolean; + isSttn: boolean; + }; +}; + +type rswtChkSttnActions = { + rswtChkSttnInit: ( sggCd: string) => void; + isRswtSttn: (isSttn:boolean) => void; + getRswtSttnExcel:(excelList:rswtChkZodType[]) => void; + +}; + +export type rswtChkSttnType = rswtChkSttnState & rswtChkSttnActions; + +export const rswtChkStateExport: rswtChkSttnState = { + rswtChkSttn: { + rswtChkSttnHeadList: rswtChkHeadList, + rswtDetailSttnHeadList: rswtDetailHeadList, + rswtSttnHeadNm: rswtHeadNm, + sttnExcelList: [], + rswtChkRes: [], + isInit: false, + isSttn:false, + }, +}; + +const initFetch = async (year: string | undefined, sgg: string | undefined) => { + const rswtChkRes = await getFetch( + "wr", + `/reuseChk/reuseWater/` + + (year ? `${year}` : "2024") + + (sgg ? `/${sgg}` : "/0") + ); + return rswtChkRes; +}; + + +export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( + set: any +) => { + return { + rswtChkSttnInit: async ( sggCd: string) => { + try { + const rswtChkRes = await initFetch("2024", sggCd); + return set((state: rswtChkSttnState) => ({ + ...state, + rswtChkSttn: { + ...state.rswtChkSttn, + rswtChkRes: rswtChkRes, + isInit: false, + }, + })); + } catch (error) { + console.log(error); + } + },isRswtSttn :(isSttn:boolean) =>{ + try { + return set((state: rswtChkSttnState) => ({ + ...state, + rswtChkSttn: { + ...state.rswtChkSttn, + isSttn:isSttn + }, + })); + } catch (error) { + console.log(error); + } + },getRswtSttnExcel: async (excelList:rswtChkZodType []) => { + try { + + const excelData = buildingExcelRswtSttn(excelList) + + return set((state: rswtChkSttnState) => ({ + ...state, + rswtChkSttn: { + ...state.rswtChkSttn, + sttnExcelList:excelData + }, + })); + } catch (error) { + console.log(error); + } + } + + + }; +}; diff --git a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/excel.tsx b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/excel.tsx new file mode 100644 index 000000000..113a35289 --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/excel.tsx @@ -0,0 +1,24 @@ +export const excelPrfmncSttnParsing = ( + data: any[] +): string[][] => { + const excelExportData: string[][] = []; + data.forEach((item) => { + excelExportData.push([ + item.용수명, + item.total, + item["1월"], + item["2월"], + item["3월"], + item["4월"], + item["5월"], + item["6월"], + item["7월"], + item["8월"], + item["9월"], + item["10월"], + item["11월"], + item["12월"], + ]); + }); + return excelExportData; +}; \ No newline at end of file diff --git a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/headList.tsx new file mode 100644 index 000000000..d04ef7b20 --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/headList.tsx @@ -0,0 +1,59 @@ +import { z } from "zod"; +import type { TableUpperProps } from "../../ruseFcltMng/fcltReg/rainReg"; + +export const pbnstPrfmncSttnHeadListExport: TableUpperProps[] = [ + { + id: "number", + title: "No", + rowSpan: 2, + row: 0 + }, + { + id: "sido", + title: "시도", + rowSpan: 2, + row: 0 + }, + { + id: "gugun", + title: "시군구", + rowSpan: 2, + row: 0 + }, + { + id: "bscFctNm", + title: "시설명", + rowSpan: 2, + row: 0 + }, + { + id: "ruseUtztnFcltNm", + title: "재이용시설명", + rowSpan: 2, + row: 0 + }, + +] + +export const pbnstYearHeadListExport: TableUpperProps[] = [ + { + title: "년도", + colSpan: 3, + row: 0 + }, + { + id: "total", + title: "계", + row: 1 + }, + { + id: "inUsage", + title: "장내용수", + row: 1 + }, + { + id: "outUsage", + title: "장외용수", + row: 1 + }, +] diff --git a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx new file mode 100644 index 000000000..b513085ae --- /dev/null +++ b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx @@ -0,0 +1,216 @@ +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; +import type { TableUpperProps } from "../../common/type/header"; +import { pbnstPrfmncSttnHeadListExport, pbnstYearHeadListExport } from "./headList"; +import type { FcPrfType, PrfType } from "../../prfmncReg/pbnstPrfmncReg/type"; +import type { pubHasuType, selectLabel } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; +import type { ReturnType } from "@common/business_components"; + +type pbnstPrfmncSttnActions = { + tableHeadMaker: (startYear: number, endYear: number) => void; + pbnstPrfmncInit: (page: string, sggOrCity: string) => void; + pbnstPrfmncSearch: (returnType: ReturnType, swtrmtFcltNm: string, fromDate: string, toDate: string) => void; +} + +const pbnstPrfmncRes: PrfType[] = []; +const prfmncRes: FcPrfType[] = []; + +export type pbnstPrfmncSttnState = { + pbnstPrfmncSttn: { + pbnstPrfHeadList: TableUpperProps[]; + pbnstPrfmncRes: PrfType[]; + prfmncRes: any[]; + isInit: boolean; + rawData: any[]; + selectYearList: selectLabel[] + } +} + +export type pbnstPrfmncSttnType = pbnstPrfmncSttnState & pbnstPrfmncSttnActions; + +export const pbnstPrfmncSttnExport: pbnstPrfmncSttnState = { + pbnstPrfmncSttn: { + pbnstPrfHeadList: [...pbnstPrfmncSttnHeadListExport], + pbnstPrfmncRes: pbnstPrfmncRes, + prfmncRes: prfmncRes, + isInit: false, + rawData: [], + selectYearList: [] + } +} + +const checkWrRegList = async () => { + const wrRegList = await getFetch("wr", `/reuseFc/pubDetailList`); + 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?.fcltCds, + fetchType.COMMON + ); + const data = checkWrRegListRes.fcPrfList.map((item: FcPrfType) => { + const findData = rainRes.data.find( + (fclt: pubHasuType) => + fclt.bscFctCd === item.pbnstRuseFcltPk.substring(0, 11) + ); + return { + sido: findData?.sido, + gugun: findData?.gugun, + bscFctNm: findData?.bscFctNm, + ruseUtztnFcltNm: item.ruseUtztnFcltNm, + pbnstRuseFcltPk: item.pbnstRuseFcltPk, + }; + }); + return data; +} + +export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions = ( + set: any +) => { + return { + tableHeadMaker: (startYear: number, endYear: number) => { + let yearHeader: TableUpperProps[] = [...pbnstPrfmncSttnHeadListExport]; + const selectList = [] as unknown as selectLabel[] + for (let i = startYear; i <= endYear; i++) { + const yearHead = JSON.parse(JSON.stringify(pbnstYearHeadListExport)); + if (yearHead) { + yearHead.forEach((item: { id?: string; }) => { + if (item.id) { + item.id = i + item.id; + + } + + }) + if (yearHead[0]) + yearHead[0].title = i + "년도" + } + yearHeader = [...yearHeader, ...yearHead] + selectList.push({ text: i + "년도", val: String(i) }) + } + set((state: pbnstPrfmncSttnState) => { + return { + ...state, + pbnstPrfmncSttn: { + ...state.pbnstPrfmncSttn, + pbnstPrfHeadList: [...yearHeader], + selectYearList: selectList + }, + }; + }); + }, + pbnstPrfmncInit: async (page: string, sggOrCity: string) => { + try { + const prfmncRes = await hasuListFetch(page, sggOrCity); + return set((state: pbnstPrfmncSttnState) => ({ + ...state, + pbnstPrfmncSttn: { + ...state.pbnstPrfmncSttn, + prfmncRes: prfmncRes, + isInit: true, + }, + })); + } catch (e) { + console.log(e) + } + }, + pbnstPrfmncSearch: async (returnType: ReturnType, swtrmtFcltNm: string, fromDate: string, toDate: string) => { + let searchUrl = ""; + if (returnType.sigunValue !== "0") { + if (swtrmtFcltNm !== "") { + searchUrl = `/prcsFclt/info/1/20/${returnType.partValue}/${returnType.sigunValue}/0/1/SW${'/' + swtrmtFcltNm}`; + } else { + searchUrl = `/prcsFclt/info/1/20/${returnType.partValue}/${returnType.sigunValue}/0/1/SW`; + } + } else { + if (swtrmtFcltNm !== "") { + searchUrl = `/prcsFclt/info/1/20/${returnType.partValue}/${returnType.sidoValue}/0/1/SW${'/' + swtrmtFcltNm}`; + } else { + searchUrl = `/prcsFclt/info/1/20/${returnType.partValue}/${returnType.sidoValue}/0/1/SW`; + } + } + + const pbnstRes = await getFetch( + fetchType.COMMON, + searchUrl + ); + + const bscFctCdList = pbnstRes.map((item: any) => { + return item.bscFctCd; + }) + const res = await postFectch( + `/reuseFc/pubDetailList/${fromDate}/${toDate}`, + bscFctCdList, + `wr`, + ) + let inUsageSum = 0; + let outUsageSum = 0; + let usageSum = 0; + let year = ""; + const yearsData = [] + + res.data.forEach((item) => { + if (item.prfList.length === 0) { + const year = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출 + const yearVal = { year: year, pbnstRuseFcltPk: item.pbnstRuseFcltPk ?? "", ruseUtztnFcltNm : item.ruseUtztnFcltNm ?? "" }; + yearVal[year + "inUsage"] = 0; + yearVal[year + "outUsage"] = 0; + yearVal[year + "total"] = 0; + yearsData.push(yearVal); + } + item.prfList.forEach((acc) => { + year = acc.ym?.substring(0, 4) ?? ""; + inUsageSum = + (acc.wshwtr ?? 0) + + (acc.coolUswtr ?? 0) + + (acc.cleanUswtr ?? 0) + + (acc.drwtUswtr ?? 0) + + (acc.dltnUswtr ?? 0) + + (acc.ithlRswtr ?? 0) + + (acc.ithlEtcUswtr ?? 0) + + (acc.cleanToilUswtr ?? 0) + + (acc.resn ?? 0); + outUsageSum = + (acc.lndwtr ?? 0) + + (acc.hydUswtr ?? 0) + + (acc.ugwtChr ?? 0) + + (acc.farmUswtr ?? 0) + + (acc.rvrKeepUswtr ?? 0) + + (acc.indstUswtr ?? 0); + const yearData = yearsData.find( + (yearItem) => + yearItem.year === year && + yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltPk + ); + + if (yearData) { + yearData[year + "inUsage"] += inUsageSum; + yearData[year + "outUsage"] += outUsageSum; + yearData[year + "total"] += (inUsageSum + outUsageSum); + } else { + const yearVal = { year: year, pbnstRuseFcltPk: acc.pbnstRuseFcltFk, ruseUtztnFcltNm : item.ruseUtztnFcltNm ?? "" }; + yearVal[year + "inUsage"] = inUsageSum; + yearVal[year + "outUsage"] = outUsageSum; + yearVal[year + "total"] = inUsageSum + outUsageSum; + yearsData.push(yearVal); + } + }); + }); + const resData: any[] = [] + pbnstRes.forEach((acc) => { + let data = yearsData.find((item) => item.pbnstRuseFcltPk.substring(0, 11) === acc.bscFctCd) + if (data) { + resData.push({ ...acc, ...data }) + } + }) + return set((state: pbnstPrfmncSttnState) => ({ + ...state, + pbnstPrfmncSttn: { + ...state.pbnstPrfmncSttn, + prfmncRes: resData, + rawData: res.data + }, + })); + } + } +} \ No newline at end of file diff --git a/apps/wr/app/store/publicReuseFaciltyInsert/index.tsx b/apps/wr/app/store/publicReuseFaciltyInsert/index.tsx deleted file mode 100644 index 131132b85..000000000 --- a/apps/wr/app/store/publicReuseFaciltyInsert/index.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import type { TableUpperProps } from "../rainReuseFacilityInsert"; - -export type TableProps = { - id: string; - title: string; -} - -export type selectLabel={ - text:string, - val:string -} - - -type publicReuseFacilityData = { - sido: string, - sigungo: string, - facilityName: string, - location: string, - locationgubun: string, - facilityCapacity: string, - yn?: string, - authoDay?: string, - area?: string, - reuse?: string, - register?: string - - -} -type pubMngFacData = { - mng: string, - subCo: string, - startDay: string, - endDay: string, -} -const selectLabel:selectLabel[]=[ - {text:'test',val:'1'} -] - - -const pubFacHeadList:TableProps[] = [ - { id: "sido", title: "시도"}, - { id: "sigungo", title: "시군구"}, - { id: "facilityName", title: "시설명"}, - { id: "location", title: "위치"}, - { id: "locationgubun", title: "지역구분"}, - { id: "facilityCapacity", title: "시설용량"}, - { id: "yn", title: '재이용여부' }, - { id: "authoDay", title: '등록여부'}]; - - - const pubMngUpHeadList:TableUpperProps[] = [ - { id: "mng", title: "운영방식"}, - { id: "subCo", title: "대행업체"}, - { id: "startDay", title: "시작일", upName:'upChangeRe' , upSequnce:1 }, - { id: "endDay", title: "종료일", upName:'upChangeRe' , upSequnce:2 }, - ]; - const date = new Date() - const pubMngFacData:pubMngFacData[] = [ - { mng:"자체운영", subCo:"-",startDay: '2024-11-11' ,endDay:'2024-11-20'}, - { mng:"자체운영", subCo:"한국수자원공사",startDay: '2022-11-11' ,endDay:'2024-09-13'}, - { mng:"자체운영", subCo:"스타벅스",startDay: '2023-11-11' ,endDay:'2024-02-19'}, - ]; - const publicReuseFacilityData:publicReuseFacilityData[] = [ - { - sido: "서울", - sigungo: "강남구", - facilityName: "난지", - location: "경기도 고양시 덕양구 대차로 4가", - locationgubun: "500", - facilityCapacity: "800,000", - reuse: "Y", - register: "Y" - }, - { - sido: "서울", - sigungo: "강남구", - facilityName: "난지", - location: "경기도 고양시 덕양구 대차로 4가", - locationgubun: "500", - facilityCapacity: "800,000", - reuse: "Y", - register: "Y" - }, - { - sido: "서울", - sigungo: "강남구", - facilityName: "난지", - location: "경기도 고양시 덕양구 대차로 4가", - locationgubun: "500", - facilityCapacity: "800,000", - yn: "Y", - authoDay: "2012", - area:"123" - }, -] - - -const erctUswtrCd:selectLabel[]=[ - {text:'처리장연계',val:'1_erct'}, - {text:'자체/연계',val:'2_erct'}, - {text:'자체처리',val:'3_erct'}, -] - -const cyclCd :selectLabel[]= [ - { text: "1차", val: "1_cyc" }, - { text: "2차", val: "2_cyc" }, - ]; - const seCd :selectLabel[] = [ - { text: "신규", val: "1_Se" }, - { text: "진행", val: "2_Se" }, - ]; - const buseCd :selectLabel[]= [ - { text: "재정", val: "1_bus" }, - { text: "만루", val: "2_bus" }, - ]; - const rprcsCd :selectLabel[] = [ - { text: "물리적처리", val: "1_rprc" }, - { text: "생물학적처리", val: "2_rprc" }, - ]; - const buseMet :selectLabel[] = [ - { text: "재정", val: "1_busemet" }, - { text: "민간투자", val: "2_busemet" }, - { text: "자체", val: "3_busemet" }, - ]; - const rprcLoCd :selectLabel[] = [ - { text: "내부", val: "1_rprc" }, - { text: "외부", val: "2_rprc" }, - ]; - -export type pubFacState = {pubFac:{ - isInit: boolean; - pubFacHeadList: TableProps[]; - pubMngUpHeadList:TableUpperProps[] - publicReuseFacilityData: publicReuseFacilityData[]; - selectLabel:selectLabel[]; - pubMngFacData:pubMngFacData[]; - erctUswtrCd:selectLabel[]; - cyclCd:selectLabel[]; - seCd:selectLabel[]; - buseCd :selectLabel[] - rprcsCd :selectLabel[] - buseMet :selectLabel[] - rprcLoCd:selectLabel[] -}} -type pubFacActions = { - decrementList: () => void -} - -export type pubFacType = pubFacState & pubFacActions; - -export const pubFacStateExport:pubFacState= {pubFac:{ - isInit:false, - pubFacHeadList: pubFacHeadList, - pubMngUpHeadList:pubMngUpHeadList, - publicReuseFacilityData: publicReuseFacilityData, - selectLabel:selectLabel, - pubMngFacData:pubMngFacData, - erctUswtrCd:erctUswtrCd, - cyclCd:cyclCd, - seCd:seCd, - buseCd:buseCd, - rprcsCd:rprcsCd, - buseMet:buseMet, - rprcLoCd:rprcLoCd -}} -export const pubFacActionsExport:(set:any)=>pubFacActions=(set: any) => { - return { - decrementList:()=>set( - (state:pubFacState) => { - return ( - {pubFac:{ - pubFacheadList: state.pubFac.pubFacHeadList, - publicReuseFacilityUper: - state.pubFac.publicReuseFacilityData.slice(0, state.pubFac.publicReuseFacilityData.length - 1) - }})} - ) - } -} diff --git a/apps/wr/app/store/rainReuseFacilityInsert/index.tsx b/apps/wr/app/store/rainReuseFacilityInsert/index.tsx deleted file mode 100644 index 0fd5194f9..000000000 --- a/apps/wr/app/store/rainReuseFacilityInsert/index.tsx +++ /dev/null @@ -1,134 +0,0 @@ - -export type TableUpperProps = { - id: string; - title: string; - upName?: string; - upSequnce?: number; -} -export type rainFacOfType = { - sido: string, - sigungo: string, - facilityName: string, - location: string, - locationgubun: string, - firstConstructionPermission:string, - facilityCapacity: string, - buildingArea:string, - yn?: string, - permissionDay:string; - authoDay?: string, - area?: string, - reuse?: string, - register?: string, - legalTargetYn:string, -} - -export const upHeadList:TableUpperProps[] = [ - { id: "sido", title: "시도"}, - { id: "sigungo", title: "시군구"}, - { id: "facilityName", title: "시설명"}, - { id: "location", title: "위치"}, - { id: "locationgubun", title: "지역구분"}, - { id: "facilityCapacity", title: "시설용량"}, - { id: "yn", title: '여부', upName:'upChangeRe' , upSequnce:1 }, - { id: "authoDay", title: '허가일',upName:'upChangeRe', upSequnce:2 }, - { id: "area", title: '면적' ,upName:'upChangeRe',upSequnce:3 }]; - -const rainOfObject:rainFacOfType[] = [ - { - sido: "서울", - sigungo: "종로구", - facilityName: "그랑서울", - location: "종로 33", - locationgubun: "업무시설", - firstConstructionPermission : '2024-10-10', - buildingArea: "50", - facilityCapacity: "800,000", - reuse: "Y", - permissionDay:'2024-10-10', - register: "Y", - legalTargetYn:"Y" - }, - { - sido: "서울", - sigungo: "종로구", - facilityName: "그랑서울", - location: "종로 33", - locationgubun: "업무시설", - firstConstructionPermission : '2024-10-10', - buildingArea: "50", - facilityCapacity: "800,000", - reuse: "Y", - permissionDay:'2024-10-10', - register: "Y", - legalTargetYn:"Y" - }, - { - sido: "서울", - sigungo: "종로구", - facilityName: "그랑서울", - location: "종로 33", - locationgubun: "업무시설", - firstConstructionPermission : '2024-10-10', - buildingArea: "50", - facilityCapacity: "800,000", - reuse: "Y", - permissionDay:'2024-10-10', - register: "Y", - legalTargetYn:"Y" - }, -] - - -export type selectArray ={ - text:string, - val:string - -} - -export const selectClass ="w-full h-full text-2xl"; -const selectHomeWater = [ - { text: '집수면', val: "1" }, - { text: "종류", val: "2" }, - ]; - - const selectYN= [ - { text: 'Y', val: "1" }, - { text: 'N', val: "2" }, - ]; - -export type rainFacState ={ rain:{ - isInit: boolean; - upHeadList: TableUpperProps[]; - rainOfObject: rainFacOfType[]; - selectHomeWater:selectArray[]; - selectYN:selectArray[]; -}} -type rainFacSActions = { - decrementList: () => void -} - -export type rainFacType = rainFacState & rainFacSActions; - -export const rainFacStateExport:rainFacState= {rain:{ - isInit:false, - upHeadList: upHeadList, - rainOfObject: rainOfObject, - selectHomeWater:selectHomeWater, - selectYN:selectYN - - -}} -export const rainFacActionsExport:(set:any)=>rainFacSActions=(set: any) => { - return { - decrementList:()=>set( - (state:rainFacState) => { - return ( - {rain:{ - upHeadList: state.rain.upHeadList, - rainOfObject: - state.rain.rainOfObject.slice(0, state.rain.rainOfObject.length - 1) - }})} - ) - } -} diff --git a/apps/wr/app/store/rswtrReuseFaciltyInsert/index.tsx b/apps/wr/app/store/rswtrReuseFaciltyInsert/index.tsx deleted file mode 100644 index b2621631b..000000000 --- a/apps/wr/app/store/rswtrReuseFaciltyInsert/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { upHeadList, type rainFacOfType, type selectArray, type TableUpperProps } from "../rainReuseFacilityInsert"; - - -const rswtOfObject:rainFacOfType[] = [ - { - sido: "서울", - sigungo: "종로구", - facilityName: "그랑서울", - location: "종로 33", - locationgubun: "업무시설", - firstConstructionPermission : '2024-10-10', - buildingArea: "50", - facilityCapacity: "800,000", - reuse: "Y", - permissionDay:'2024-10-10', - register: "Y", - legalTargetYn:"Y" - }, - { - sido: "서울", - sigungo: "종로구", - facilityName: "그랑서울", - location: "종로 33", - locationgubun: "업무시설", - firstConstructionPermission : '2024-10-10', - buildingArea: "50", - facilityCapacity: "800,000", - reuse: "Y", - permissionDay:'2024-10-10', - register: "Y", - legalTargetYn:"Y" - }, - { - sido: "서울", - sigungo: "종로구", - facilityName: "그랑서울", - location: "종로 33", - locationgubun: "업무시설", - firstConstructionPermission : '2024-10-10', - buildingArea: "50", - facilityCapacity: "800,000", - reuse: "Y", - permissionDay:'2024-10-10', - register: "Y", - legalTargetYn:"Y" - }, -] - - -const selectHomeWater = [ - { text: '집수면', val: "1" }, - { text: "종류", val: "2" }, - ]; - - const selectYN= [ - { text: 'Y', val: "1" }, - { text: 'N', val: "2" }, - ]; - -export type rswtFacState ={ rswt:{ - isInit: boolean; - upHeadList: TableUpperProps[]; - rswtOfObject: rainFacOfType[]; - selectHomeWater:selectArray[]; - selectYN:selectArray[]; -}} -type rswtFacSActions = { - decrementList: () => void -} - -export type rswtFacType = rswtFacState & rswtFacSActions; - -export const rswtFacStateExport:rswtFacState= {rswt:{ - isInit:false, - upHeadList: upHeadList, - rswtOfObject: rswtOfObject, - selectHomeWater:selectHomeWater, - selectYN:selectYN - - -}} -export const rswtFacActionsExport:(set:any)=>rswtFacSActions=(set: any) => { - return { - decrementList:()=>set( - (state:rswtFacState) => { - return ( - {rain:{ - upHeadList: state.rswt.upHeadList, - rainOfObject: - state.rswt.rswtOfObject.slice(0, state.rswt.rswtOfObject.length - 1) - }})} - ) - } -} diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx new file mode 100644 index 000000000..931cd1305 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx @@ -0,0 +1,200 @@ +import { getFetch } from "@/components/lib/api"; +import { TEST_UUID, type customRainBdst } from "../rainReg"; +import type { customHtdflBdst } from "../htdflReg"; +import type { customRswtBdst } from "../rswtReg"; +import type { TableUpperProps } from "@/app/store/common/type/header"; + + + const moUpHeadList: TableUpperProps[] = [ + { id: "cityText", title: "시도" }, + { id: "sggText", title: "시군구" }, + { id: "bdstNm", title: "시설명" }, + { id: "daddr", title: "위치" }, + { id: "frstArchPrmsnDt", title: "최초건축허가일" }, + { id: "archArea", title: "건축면적", }, + ]; + + + const bdstChgHeadList: TableUpperProps[] = [ + { id: "bdstChgNm", title: "구분" }, + { id: "archChgPrmsnDt", title: "허가일" }, + { id: "archChgArea", title: "변경면적" }, + + ]; + + + +const bdstBase:any[] = []; + + +const isRainBdst = (data: any): data is customRainBdst=> { + return data.reuseBuilding?.[0]?.rainyObj !== undefined; +} + +const isHtdflBdst = (data: any): data is customHtdflBdst=> { + return data.reuseBuilding?.[0]?.htdflObj !== undefined; +} + +const isRswtBdst = (data: any): data is customRswtBdst =>{ + return data.reuseBuilding?.[0]?.rswtrObj !== undefined; +} + + + + export const makeRstr =(data:customRainBdst|customHtdflBdst|customRswtBdst) =>{ + data.rgtr = TEST_UUID; + + let chData + if(isRainBdst(data)){ + chData = data.reuseBuilding.map((item) => ({ + ...item, + rgtr : TEST_UUID, + rainyObj:{ + ...item.rainyObj, + rgtr:TEST_UUID + } + })); + + data.reuseBuilding=chData; + }else if(isHtdflBdst(data)){ + chData = data.reuseBuilding.map((item) => ({ + ...item, + rgtr : TEST_UUID, + htdflObj:{ + ...item.htdflObj, + rgtr:TEST_UUID + } + })); + + data.reuseBuilding=chData; + }else if (isRswtBdst(data)){ + chData = data.reuseBuilding.map((item) => ({ + ...item, + rgtr : TEST_UUID, + rswtrObj:{ + ...item.rswtrObj, + rgtr:TEST_UUID + } + })); + data.reuseBuilding=chData; + } + + if(data?.chgBuilding.length>0){ + const chRgtr = data.chgBuilding.map((item) =>({...item, rgtr:TEST_UUID, mdfr: item.mdfr ?? null })) + data.chgBuilding =chRgtr; + } + return data; + + } + + export const makeMdfr =(data:customRainBdst|customHtdflBdst|customRswtBdst) =>{ + data.mdfr = TEST_UUID; + let chData + if(isRainBdst(data)){ + chData = data.reuseBuilding.map((item) => ({ + ...item, + mdfr : TEST_UUID, + rainyObj:{ + ...item.rainyObj, + mdfr:TEST_UUID + } + })); + if(data?.chgBuilding.length>0){ + const chRgtr = data.chgBuilding.map((item) =>({...item, rgtr:TEST_UUID, mdfr: item.mdfr ?? null })) + data.chgBuilding =chRgtr; + } + + data.reuseBuilding=chData + }else if(isHtdflBdst(data)){ + chData = data.reuseBuilding.map((item) => ({ + ...item, + mdfr : TEST_UUID, + htdflObj:{ + ...item.htdflObj, + mdfr:TEST_UUID + } + })); + data.reuseBuilding=chData; + }else if (isRswtBdst(data)){ + chData = data.reuseBuilding.map((item) => ({ + ...item, + mdfr : TEST_UUID, + rswtrObj:{ + ...item.rswtrObj, + mdfr:TEST_UUID + } + })); + data.reuseBuilding=chData; + } + + if(data?.chgBuilding.length>0){ + const chMdfr = data.chgBuilding.map((item) =>({...item, mdfr:TEST_UUID, archChgArea: item.archChgArea ?? '' })) + data.chgBuilding =chMdfr; + } + + return data; + + } + +export type bdstFulType = bdstState & bdstActions; + + +export type bdstState = { + bdst: { + bdstBase: any[]; + moUpHeadList:TableUpperProps[] + bdstChgHeadList:TableUpperProps[] + }; +}; + +type bdstActions = { + findBdst: (sggCd:string, searchTxt:string, flag:string) => void ; + }; + + + const findBdstInfo = async(sggCd:string, searchTxt:string ,flg:string)=>{ + return await getFetch('wr', `/reuseFc/building/${flg}/${sggCd}`+(searchTxt?`/${searchTxt}`:'' )) + + } + +export const bdstStateExport: bdstState = { + bdst: { + bdstBase: bdstBase, // Updated to match the array type + moUpHeadList:moUpHeadList, + bdstChgHeadList:bdstChgHeadList, + }, +}; + +export const bdstActionsExport: (set: any) => bdstActions = ( + set: any +) => { + return { + + findBdst: async(sggCd:string, searchTxt:string ,flg:string ) =>{ + try{ + + const bdst = await findBdstInfo(sggCd, searchTxt, flg); + const makeBdst = bdst.map((item:any) => { + return { + ...item, + reuseBuilding:[] + }; + }) + return set((state:bdstState) =>({ + ...state, + bdst:{ + ...state.bdst, + bdstBase:makeBdst + } + + })) + }catch(error1){ + console.log("error ", error1) + + }finally{ + + } + }, + + }; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx new file mode 100644 index 000000000..0a51ebb5d --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx @@ -0,0 +1,244 @@ +import { getFetch, postFectch, putFectch } from "@/components/lib/api"; +import { upHeadList, type chgBuilding} from "../rainReg"; + +import { z } from "zod"; +import { makeMdfr, makeRstr } from "../bdstReg"; +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export const hdtflZod = z.object({ + bdstInfoPk: z.number().nullish(), + ctpvCd: z.number().default(0), + sggCd: z.number().default(0), + cityText: z.string().nullish(), + sggText: z.string().nullish(), + daddr: z.string().default(" "), + bdstNm: z.string().default(" "), + frstArchPrmsnDt: z.date().default(() => new Date()), + archChgPrmsnDt: z.date().default(() => new Date()).nullish() , + archArea: z.number().default(0), + archChgArea: z.number().nullish().default(0), + rgtr:z.string().nullish().default(null), + mdfr:z.string().nullish().default(null), + reuseBuilding: z + .array( + z.object({ + bdstRuseFcltPk: z.number().nullish(), + bdstInfoFk: z.number().nullish(), + instlCmptnDt: z.coerce.date().default(() => new Date()), + instlCst: z.number().nullish().default(0), + operMngCd: z.string().nullish().default(""), + anlOperCst: z.number().default(0), + instlCstSprtYn: z.enum(["Y", "N"]).nullish(), + instlCstSprtAmt: z.number().nullish().default(0), + grntchBdstCertNo: z.string().nullish().default(""), + grntchBdstCertGrd: z.string().nullish().default(""), + ruseFcltFlg: z.string().nullish().default(""), + bdstUsgCd: z.number().default(0), + lawTrgtYn: z.enum(["Y", "N"]).nullish(), + rgtr:z.string().nullish().default(null), + mdfr:z.string().nullish().default(null), + // htdflObj 매핑 + htdflObj: z + .object({ + htdflFcltPk: z.number().nullish(), + bdstRuseFcltFk: z.number().nullish(), + etgtType: z.string().default(""), + fclCpct: z.number().default(0), + plwtYn: z.enum(["Y", "N"]), + orgwtWtritSpt: z.number().default(0), + orgwtOcrnProcs: z.string().default(""), + wtritEntrcTp: z.number().max(4, "온도는 100이상이 될 수 없습니다.") + .default(0), + amtgnr: z.number().default(0), + prcsProcsYn: z.enum(["Y", "N"]), + prcsFcltCpct: z.number().default(0), + fmYn: z.string().default(""), + utztnCpct: z.number().default(0), + utztnUsg: z.string().default(""), + grssClfg: z.number().default(0), + combResdch: z.number().default(0), + trbt: z.number().default(0), + bod: z.number().default(0), + smll: z.number().default(0), + crmty: z.number().default(0), + tn: z.number().default(0), + tph: z.number().default(0), + ph: z.number().default(0), + clIonQnt: z.number().default(0), + cdtnv: z.number().default(0), + emsnExitTp: z.number().default(0), + rgtr:z.string().nullish().default(null), + mdfr:z.string().nullish().default(null), + }) + .default({ + plwtYn: "N", + prcsProcsYn: "N", + }), + }) + ) + .default([]), + chgBuilding: z + .array( + z.object({ + bdstInfoFk: z.number().nullish(), + archChgPrmsnDt: z.coerce.date() , + archChgArea: z.string().default("-").nullish(), + bdstChgNm: z.string().default(""), + rgtr:z.string().nullish().default(null), + mdfr:z.string().nullish().default(null), + }) + ) + .default([]), +}); + + export type customHtdflBdst = z.infer<typeof hdtflZod> + + +export type htdflFacState = { + htdfl: { + isInit: boolean; + upHeadList: TableUpperProps[]; + htdflRes: customHtdflBdst[]; + }; +}; +type htdflFacSActions = { + initHtdfl: (code: string ) => void; + htdflBdstUpdate:(data: customHtdflBdst, code:string|number) => void; + htdflBdstReg:(data: customHtdflBdst, code:string|number) => void; + getHtdflBdstData:(pageNation:number, code:string|number) =>void; + +}; + +const htdflRes: customHtdflBdst[] = []; + +export type htdflFacType = htdflFacState & htdflFacSActions; + +export const htdflFacStateExport: htdflFacState = { + htdfl: { + isInit: false, + upHeadList: upHeadList, + htdflRes: htdflRes, + }, +}; + + +const initFetch = async (code:string|number) => { + const rswtRes = await getFetch( + "wr", + `/facilityList/1/20/${code}`) + + return rswtRes; +}; +export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( + set: any +) => { + return { + getHtdflBdstData: async (pageNation: number, code: string | number) => { + try { + const rswtRes = await getFetch( + "wr", + `/drain/facilityList/${pageNation}/20/${code}` + ); + console.log(pageNation) + // 여기서 상태 업데이트 + return set((state: htdflFacState) => ({ + ...state, + htdfl: { + ...state.htdfl, + htdflRes: rswtRes, + isInit: false, + }, + })); + } catch (e) { + console.log(e); + } + }, + + + initHtdfl: async (code: string ) => { + try { + const htdflRes = await initFetch(code); + set((state: htdflFacState) => { + return { + ...state, + htdfl: { + ...state.htdfl, + htdflRes: htdflRes, + isInit: false, + }, + }; + }); + } catch (error) { + console.log(error); + } + }, + htdflBdstUpdate: async (data: customHtdflBdst, code:string|number) => { + try { + const chData=await makeMdfr(data) + const res = await putFectch("/drain/facility", data); + if (res.ok) { + alert(" 수정이 완료되었습니다."); + } + + const htdflRes = await initFetch(code); + set((state: htdflFacState) => ({ + ...state, + htdfl: { + ...state.htdfl, + htdflRes: htdflRes, + isInit: false, + }, + })); + } catch (errorUpdate) { + alert( + "업데이트 오류입니다. " + + (errorUpdate instanceof Error ? errorUpdate.message : "") + ); + } + }, + htdflBdstReg: async (data: customHtdflBdst , code:string|number) => { + try { + const chData=await makeRstr(data) + const res = await postFectch("/drain/facility", data); + if (res.ok) { + + if(data.chgBuilding.length > 0){ + await res + .json() + .then((resData: any) => { + const chPK = resData?.data?.bdstInfoPk; + + const makeCh = resData.data.chgBuilding.map( + (item: chgBuilding) => ({ + ...item, + bdstInfoFk: chPK, + }) + ); + const chch = postFectch("/reuseFc/chgBuilding", makeCh); + return chch; + }) + .then((chch: any) => { + if (chch.ok) { + alert("등록이 완료되었습니다."); + } + }); + }else{ + alert("등록이 완료되었습니다."); + + } + } + const htdflRes = await initFetch(code); + set((state: htdflFacState) => ({ + ...state, + htdfl: { + ...state.htdfl, + htdflRes: htdflRes, + isInit: false, + }, + })); + } catch (errorReg) { + alert("등록 오류입니다."); + } + }, + }; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx new file mode 100644 index 000000000..536114f3f --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/headList.tsx @@ -0,0 +1,132 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; + +export const pubFacHeadListExport: TableUpperProps[] = [ + { id: "sido", title: "시도" }, + { id: "gugun", title: "시군구" }, + { id: "bscFctNm", title: "시설명" }, + { id: "addr", title: "위치" }, + { id: "efflWqltStndrdNm", title: "지역구분" }, + { id: "fctCpc", title: "시설용량" }, + { id: "ruse", title: "재이용여부" }, + { id: "rswtrYn", title: "등록여부" }, +]; + +export const pbnstMngUpHeadListExport: TableUpperProps[] = [ + { id: "choose", title: "선택", type: "checkbox" }, + { id: "operMthd", title: "운영방식",type: "select", selectData: "ruseMngMtd" }, + { id: "rprtvBzenty", title: "대행업체", edit: true }, + { + id: "bgngDt", + title: "시작일", + type: "date", + }, + { + id: "endDt", + title: "종료일", + type: "date", + }, +]; + +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; + option?:string +}; + +export const wasteWaterFcltINfo: formDetailType[] = [ + { id: "wwtEmsnFcltPrmsnYn", lable: "폐수배출물신고 허가여부", type: "selectYn" }, + { id: "tmsInstlYn", lable: "Tms설치여부", type: "selectYn" }, + { id: "pbnstDrngInflwExclYn", lable: "공공하수도유입제외 허가여부", type: "selectYn" }, +]; + +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 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 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" ,option:"readOnly" }, +]; + +export const businessSttnInfo: formDetailType[] = [ + { id: "seCd", lable: "구분" }, + { id: "bizMthdCd", lable: "사업방식" }, + { id: "bizCmcnDay", lable: "사업준공일", type: "date" }, + { id: "instlAprvSncnDt", lable: "설치승인인가일", type: "date" }, +]; + +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 new file mode 100644 index 000000000..fb799c26c --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -0,0 +1,404 @@ +import { TEST_UUID } from "../rainReg"; +import { + deleteFectch, + fetchType, + getFetch, + postFectch, + putFectch, +} from "@/components/lib/api"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { + 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"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import toast from "react-hot-toast"; + +const selectLabel: SelectDataType[] = [{ text: "test", val: "1" }]; +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[]; + 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 = { + pubHasuInit: (page: string, sggOrCity: string) => void; + pbnstRegClick: (pubHasuRes: pubHasuType) => void; + pbnstReg: (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; + pubHasuSearch :(page: string, sggOrCity: string , searchTxt:string) => void +}; +export type pubFacType = pubFacState & pubFacActions; + +export const pubFacStateExport: pubFacState = { + pbnst: { + isInit: false, + pubFacHeadList: pubFacHeadList, + pbnstMngUpHeadList: pbnstMngUpHeadList, + pbnstMngNm: pbnstMngNm, + pbnstOrgwtHeadList: pbnstOrgwtHeadList, + pplInstlsHeadLisat: pplInstlsHeadLisat, + pbnstDemandHeadList: pbnstDemandHeadList, + fcOrgwtsHeadList: fcOrgwtsHeadList, + selectLabel: selectLabel, + pbnstResObj: null, // 초기값을 null로 변경 + pbnstHasuRes: [], + pbnstRes: [], // 초기값을 null로 변경 + pbnstBizCycle: [{ text: "없음", val: "1" }], + pbnstMngRegData: [], + pbnsUpdateIndex: {} as pbnstInsertIndex, + dmdComList: [], + }, +}; + +const checkWrRegList = async () => { + const wrRegList = await getFetch("wr", `/reuseFc/pubFc`); + return wrRegList; +}; + +const hasuListFetch = async (page: string, sggOrCity: string, searchTxt?:string) => { + const checkWrRegListRes = await checkWrRegList(); + + console.log(' checkWrRegListRes ', checkWrRegListRes) +let url = `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity? sggOrCity : "11"}/0/1/SW`; + + +const data = checkWrRegListRes.filter((item:string) => item !== "") + console.log(' checkWrRegListRes ' ) +if(searchTxt){ + url= url +`/${searchTxt}` + +} + const rainRes = await postFectch( + url, + data, + fetchType.COMMON + ); + + const hasuList = rainRes.data.map((item: any) => { + return { + ...item, + ruse: item.ruse === false ? "N" : "Y", + }; + }); + + 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 (page: string, sggOrCity: string) => { + try { + const pbnstHasuRes = await hasuListFetch(page, sggOrCity); + + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstHasuRes: pbnstHasuRes, + isInit: false, + }, + })); + } catch (errorHasu) { + console.log(errorHasu); + } + },pubHasuSearch : async(page: string, sggOrCity: string , searchTxt:string)=> { + + try{ + const pbnstHasuRes = await hasuListFetch(page, sggOrCity,searchTxt); + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstHasuRes: pbnstHasuRes, + isInit: false, + }, + })); + } catch (errorHasu) { + console.log(errorHasu); + } + }, + pbnstRegClick: async (pubHasuRes: pubHasuType) => { + try { + const pbnstRes = await initFetch(pubHasuRes.bscFctCd); + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstRes: pbnstRes, + isInit: false, + }, + })); + } catch (errorHasu) { + console.log(errorHasu); + } + }, + + pbnstReg: async (txt: string | undefined) => { + try { + const pbnstRes = await hasuListFetch("11", "11"); + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstRes: pbnstRes, + isInit: false, + }, + })); + } catch (errorHasu) { + console.log(errorHasu); + } + }, + pbnstInsert: async (pbnstRes: customPbnst) => { + try { + + const response = await postFectch( + `/reuseFc/pubFc`, + pbnstRes + ); + if( response.status === 200) { + toast.success(`${pbnstRes.ruseUtztnFcltNm} 하수재이용 시설이 등록되었습니다.`); + } + const pbnstHasuRes = await hasuListFetch('1', '0'); + + + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + pbnstRes: pbnstHasuRes, + 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, + }; + }); + + 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 new file mode 100644 index 000000000..a34985810 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -0,0 +1,244 @@ +import { z } from "zod"; +import { TEST_UUID } from "../rainReg"; + +export type customPbnst = z.infer<typeof pbnstRegZod>; + +export const pbnstReg = [] as customPbnst[]; +export type TableProps = { + id: string; + title: string; +}; + +export type selectLabel = { + text: string; + val: string; +}; + +export type pbnstInsertIndex ={ + mng:number, + biz:number +} + +export type publicReuseFacilityType = { + sido: string; + sigungo: string; + facilityName: string; + location: string; + locationgubun: string; + facilityCapacity: string; + yn?: string; + authoDay?: string; + area?: string; + reuse?: string; + register?: string; + swtrmtFcltFk: string; +}; + +export type ruseFcltType = { + swtrmtFcltFk: string; + pbnstRuseFcltPk: string; + fcltNm: string; + fcltCpct: number; + swtrmtCpct: number; + ruseCpct: number; + 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|'' +} + +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() +}); + + +export type pubHasuType = z.infer<typeof pubHasuZod>; +const zDate = z.preprocess((v) => (v ? new Date(v as string) : undefined), z.date()).nullish() ; +const zDateNullish = zDate.nullish(); +const zDateDefaultNow = zDate.default(new Date()); + +const zNum = z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number()); +const zNumNullish = zNum.nullish(); +const zNumDefaultZero = zNum.default(0).nullish(); + +const pplInstlSchema = z.object({ + ruseFcltBizFk: zNumNullish, + choose: z.boolean().default(false), + pdmt: z.string().default(''), + ppMqlt: z.string().default(""), + cpct: zNumDefaultZero, +}); + +const fcFileSchema = z.object({ + ruseFcltFk: z.string().nullish(), + fileTkn: z.string(), + filePath: z.string(), + fileKnd: z.string(), + regDt: zDateDefaultNow.nullish(), + rgtr: z.string().nullish(), + mdfcnDt: zDateDefaultNow.nullish(), + mdfr: z.string().nullish(), +}); + +export const fcOperSchema = z.object({ + pbnstRuseFcltOperInfoPk: zNumNullish, + pbnstRuseFcltFk: z.string().nullish(), + operMthd: z.string(), + rprtvBzenty: z.string(), + bgngDt: zDateDefaultNow, + endDt: zDateDefaultNow, + regDt: zDateNullish, + rgtr: z.string().default(""), + mdfcnDt: zDateNullish, + mdfr: z.string().default(""), + choose: z.boolean().default(false), +}); + +const fcOrgwtSchema = z.object({ + pbnstRuseFcltBizFk:zNumNullish, + swtrmtFcltFk: z.string().nullish(), + orgwtWtritQnt: zNum, + tmsYn: z.enum(["Y", "N"]).default("N"), + dschgFmYn: z.enum(["Y", "N"]).default("N"), + dtlPstnExpln: z.string().default(''), + regDt: zDateNullish, + rgtr: z.string().default(''), + mdfcnDt: zDateNullish, + mdfr: z.string().default(''), + choose: z.boolean().default(false), +}); + +const dmdColist = z.object({ + dmdInfoPk: z.string().default(''), + dmdCoNm: z.string().default(''), + daddr: z.string(), + useUsgCd: z.string().nullish(), + splyBgngDay: zDateDefaultNow, + splyEndDay: zDateDefaultNow, + splyCpct: zNumDefaultZero, + splyFmYn: z.string().nullish(), + rgtr: z.string().nullish(), + mdfr: z.string().nullish(), + pbnstRuseFcltBizPk: zNumDefaultZero, + pbnstRuseFcltFk: z.string().nullish(), +}); + +export const fcBizSchema = z.object({ + pbnstRuseFcltBizPk: zNumNullish, + pbnstRuseFcltFk: z.string().nullish(), + superkey: z.string().default("").nullish(), + cycl: zNum.default(1), + seCd: z.string().default(""), + bizMthdCd: z.string().default(""), + instlAprvSncnDt: zDateDefaultNow, + bizCmcnDay: zDateDefaultNow, + gvsbs: zNumDefaultZero, + lcep: zNumDefaultZero, + cusrAlotm: zNumDefaultZero, + etc: z.string().default(''), + totalMoney: zNumNullish.default(0), + rprcsFcltCpct: zNumDefaultZero, + majorPrcsMsrcCd: z.string().default(""), + addr: z.string().default(''), + rprcsMthdCd: z.string().default(""), + inoutYn: z.enum(["Y", "N"]).default("Y"), + mainSplyPumpCpct: zNumNullish, + erctWtrOcrnCpct: zNumNullish, + erctUswtrLinkSpt: z.string().nullish(), + erctUswtrFcltCpct: zNumNullish, + erctUswtrPrcsMeth: z.string().nullish(), + erctUswtrPrcsMsrcCd: z.string().nullish(), + wwtEmsnFcltPrmsnYn: z.string().default("Y"), + tmsInstlYn: z.string().default("Y"), + pbnstDrngInflwExclYn: z.string().default("Y"), + bod: zNumDefaultZero, + grssClfg: zNumDefaultZero, + combResdch: zNumDefaultZero, + trbt: zNumDefaultZero, + clIonQnt: zNumDefaultZero, + smll: zNumDefaultZero, + crmty: zNumDefaultZero, + tph: zNumDefaultZero, + tn: zNumDefaultZero, + ph: zNumDefaultZero, + cdtnv: zNumDefaultZero, + designCst: zNumNullish, + cstrnSpvsCst: zNumNullish, + fcltSbsdCst: zNumNullish, + wstCst: zNumNullish, + cnstCmcnCst: zNumNullish, + mntrgEqpmntInstlCst: zNumNullish, + gisDbCst: zNumNullish, + elpwrMtrngCst: zNumNullish, + mchnCst: zNumNullish, + archCst: zNumNullish, + pplCst: zNumNullish, + ruseFcltCst: zNumNullish, + designCstClcln: zNumNullish, + cstrnSpvsCstClcln: zNumNullish, + fcltSbsdCstClcln: zNumNullish, + wstCstClcln: zNumNullish, + cnstCmcnCstClcln: zNumNullish, + mntrgEqpmntInstlCstClcln: zNumDefaultZero, + gisDbCstClcln: zNumNullish, + elpwrMtrngCstClcln: zNumNullish, + mchnCstClcln: zNumNullish, + archCstClcln: zNumNullish, + pplCstClcln: zNumNullish, + ruseFcltCstClcln: zNumNullish, + fcOrgwts: z.array(fcOrgwtSchema).nullish(), + pplInstls: z.array(pplInstlSchema).nullish(), + fcDemands: z.array(dmdColist).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: zNumDefaultZero, + ruseCpct: zNumDefaultZero, + splyExtraCpct: zNumDefaultZero, + regDt: zDateDefaultNow, + rgtr: z.string().default("test"), + mdfcnDt: zDateDefaultNow, + mdfr: z.string().default("test"), + fcFiles: z.array(fcFileSchema).default([]).nullish(), + fcBizs: z.array(fcBizSchema).default([]), + fcOpers: z.array(fcOperSchema).default([]).nullish(), +}); + +export const pbnstRegArrayZod = z.array(pbnstRegZod) + +export type PbnstRegArrrayType = z.infer<typeof pbnstRegArrayZod> + +export type fcBizType = z.infer<typeof fcBizSchema>; +export type fcOrgwtType = z.infer<typeof fcOrgwtSchema>; +export type ppInstlType = z.infer<typeof pplInstlSchema>; +export type operType = z.infer<typeof fcOperSchema>; +export type fcFileType = z.infer<typeof fcFileSchema>; + diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx new file mode 100644 index 000000000..5b3ad6813 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -0,0 +1,272 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { getFetch, postFectch, putFectch } from "@/components/lib/api"; +import { z } from "zod"; +import { makeMdfr, makeRstr } from "../bdstReg"; + +const validationMessages = { + required: (field: string) => `${field}을(를) 입력해주세요.`, + requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`, + yearFormat: '4자리 연도만 입력하세요', +}; + +export const TEST_UUID= '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e' + +const rainyObjSchema = z.object({ + bdstReuseFcltFk: z.number().nullish(), + pntfColKnd: z.string().default(""), + pntfColArea: z.number().default(0), + dtfcltCpct: z.number().default(0), + filtrTrtmfctYn: z.string().default("Y").nullish(), + filtrTrtmfctCpct: z.number().nullish().default(0), + filtrTrtmfctPrs: z.string().default(""), + annRnwtQty: z.number().default(0), + oilqMeasMthd: z.string().default("").nullish(), + rgtr:z.string().nullish(), + mdfr:z.string().nullish(), +}).refine((data) => data.pntfColKnd.length > 0, { + message: validationMessages.requiredSelect("종류"), + path: ["pntfColKnd"], +}).refine((data) => data.pntfColArea > 0, { + message: validationMessages.required("면적(m³)"), + path: ["pntfColArea"], +}); + +const reuseBuildingSchema = z.object({ + bdstRuseFcltPk: z.number().nullish(), + bdstInfoFk:z.number().nullish(), + instlCmptnDt: z.coerce.date().default(() => new Date()), + instlCst: z.number().nullish(), + operMngCd: z.string().optional().default(""), + anlOperCst: z.number().default(0), + instlCstSprtYn: z.string().default("Y"), + instlCstSprtAmt: z.number().nullish(), + grntchBdstCertYn: z.string().default("Y"), + grntchBdstCertYr: z.string().default("") + .refine((year)=> year.length===0 || /^\d{4}$/.test(year),{ + message: validationMessages.yearFormat + }), + grntchBdstCertNo: z.string().default(""), + grntchBdstCertGrd: z.string().default(""), + crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 + crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 + crgRdctAmt: z.number().default(0), // 안쓰는 값 + ruseFcltFlg: z.string().default(""), + bdstUsgCd: z.number().default(0), + lawTrgtYn: z.string().default("Y"), + rgtr:z.string().nullish(), + mdfr:z.string().nullish(), + rainyObj: rainyObjSchema.default({}) +}).refine((data) => data.bdstUsgCd > 0, { + message: validationMessages.requiredSelect("건축물용도"), + path: ["bdstUsgCd"], +}); + +const chgBuildingSchema = z.object({ + bdstInfoFk: z.number().nullish(), + archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}), + archChgArea: z.string().default("-"), + bdstChgNm: z.string().default(""), + rgtr:z.string().nullish(), + mdfr:z.string().nullish(), +}).refine(data => data.bdstChgNm.length > 0, { + message: validationMessages.requiredSelect("구분"), + path: ["bdstChgNm"] +}); + +export const rainZod = z.object({ + bdstInfoPk: z.number().optional(), + ctpvCd: z.number().default(0), + sggCd: z.number().default(0), + daddr: z.string().default(""), + bdstNm: z.string().default(""), + frstArchPrmsnDt: z.date().default(() => new Date()), + archChgPrmsnDt: z.coerce.date().nullish().default(null), + archArea: z.number().default(0), + archChgArea: z.number().default(0), + rgtr:z.string().nullish(), + mdfr:z.string().nullish(), + reuseBuilding: z.array(reuseBuildingSchema).default([]), + chgBuilding: z.array(chgBuildingSchema).default([]) +}) + +export type chgBuilding={ + bdstInfoFk?: number|null, + archChgPrmsnDt?: Date, + archChgArea?: string, + bdstChgNm?: string + rgtr?:string|null; + mdfr?:string|null; +} +export type customRainBdst = z.infer<typeof rainZod> + +export const upHeadList: TableUpperProps[] = [ + { id: "cityText", title: "시도" }, + { id: "sggText", title: "시군구" }, + { id: "bdstNm", title: "시설명" }, + { id: "daddr", title: "위치" }, + { id: "bdsgUsgText", title: "건축물용도" }, + { id: "frstArchPrmsnDt", title: "최초건축허가일" }, + { id: "archArea", title: "건축면적" }, + { id: "lawTrgtYn", title: "법적의무대상여부" }, + { id: "bdstChgYn", title: "건축물변경여부" }, +]; + +const rainRes: customRainBdst[] = []; + + +export const selectClass = "w-full h-full !text-xl"; + +export type rainFacState = { + rain: { + isInit: boolean; + upHeadList: TableUpperProps[]; + rainRes: customRainBdst[]; + }; +}; +type rainFacSActions = { + initRain: (code:string|number) => void; + getRainData:(pageNation:number, code:string|number) =>void; + rainSearch: (txt:string,pageNation:number,code:string|number) => void; + rainBdstReg: (data: customRainBdst, code:string|number) => void; + rainBdstUpdate: (data: customRainBdst, code:string|number) => void; +}; + +export type rainFacType = rainFacState & rainFacSActions; + +export const rainFacStateExport: rainFacState = { + rain: { + isInit: false, + rainRes: rainRes, + upHeadList: upHeadList, + }, +}; + +const initFetch = async (code:string|number) => { + const rainRes =await getFetch("wr", `/reuseFc/rainyInfoList/1/10/${code}`); + return rainRes; +}; +export const rainFacActionsExport: (set: any) => rainFacSActions = ( + set: any +) => { + return { + getRainData: async(pageNation:number, code:string|number) =>{ + try{ + const rainRes =await getFetch("wr", `/reuseFc/rainyInfoList/${pageNation}/10/${code}`); + + + // 여기서 상태 업데이트 + return set((state: rainFacState) => ({ + ...state, + rain: { + ...state.rain, + rainRes: rainRes, + isInit: false, + }, + })); + + }catch(e){ + console.log(e) + + } + }, + + initRain: async (code:string|number) => { + try { + // 파라이터 수정 통합관리자 =0, 시관리자=시코드, 시군구 관리자= 시군구 코드 를 세번째 파라미터로 지정 / 건축물 서치가 있을경우 4번째 파라미터로 던지기 즉 1/20/42(서울시)/영현빌딩 이런식으로) + const rainRes = await initFetch(code); + // 여기서 상태 업데이트 + return set((state: rainFacState) => ({ + ...state, + rain: { + ...state.rain, + rainRes: rainRes, + isInit: false, + }, + })); + } catch (error) { + console.log("error", error); + } + }, + rainSearch: async(txt:string,pageNation:number) =>{ + try{ + + const rainRes = await getFetch('wr' , `/reuseFc/rainyInfoList/${pageNation}/20/41` +(txt? `/${txt}`:'')) + set((state: rainFacState) => { + return { + rain: { + ...state.rain, + rainRes: rainRes, + isInit: false, + }, + }; + })} + catch(erTxt){ + console.log("error", erTxt) + } + }, + rainBdstReg: async (data: customRainBdst,code:string|number) => { + try { + + const test =await makeRstr(data); + const res = await postFectch("/reuseFc/rainyInfo", test); + if(res.ok){ + if(data.chgBuilding.length > 0){ + await res.json().then((resData: any) => { + const chPK = resData?.data?.bdstInfoPk + const makeCh= resData.data.chgBuilding.map((item: chgBuilding) => + ( { + ...item, + bdstInfoFk: chPK, + }) + ); + const chch= postFectch("/reuseFc/chgBuilding", makeCh); + return chch; + }).then((chch: any) => { + if(chch.ok) { + alert("등록이 완료되었습니다."); + } + }) + }else{ + alert("등록이 완료되었습니다."); + } + } + const rainRes = await initFetch(code); + set((state: rainFacState) => ({ + ...state, + rain: { + ...state.rain, + rainRes: rainRes, + isInit: false, + }, + })); + } catch (e) { + console.log("e", e); + alert("등록 오류입니다."); + } finally { + } + }, + rainBdstUpdate: async (data: customRainBdst,code:string|number) => { + try { + const chData =await makeMdfr(data); + const res = await putFectch("/reuseFc/rainyInfo", chData); + console.log('rainUpdate', res); + if(res.ok){ + alert(" 수정이 완료되었습니다.") + } + const rainRes = await initFetch(code); + set((state: rainFacState) => ({ + ...state, + rain: { + ...state.rain, + rainRes: rainRes, + isInit: false, + }, + })); + } catch (e) { + console.log("e", e); + alert("업데이트 오류입니다."); + } finally { + } + } + }} + diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx new file mode 100644 index 000000000..3c2504b9a --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx @@ -0,0 +1,262 @@ +import { getFetch, postFectch, putFectch } from "@/components/lib/api"; +import { upHeadList, type chgBuilding } from "../rainReg"; + +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { z } from "zod"; +import { makeMdfr, makeRstr } from "../bdstReg"; + +export const rswtZod = z.object({ + bdstInfoPk: z.number().nullish(), + ctpvCd: z.number().default(0), + sggCd: z.number().default(0), + daddr: z.string().default(""), + bdstNm: z.string().default(""), + frstArchPrmsnDt: z.date().default(() => new Date()), + archChgPrmsnDt: z.coerce.date().nullish().default(null), + chBuildingYn: z.string().nullish(), + archArea: z.number().default(0), + archChgArea: z.number().default(0), + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), + bdstUsgText: z.string().default(""), + cityText: z.string().default(""), + sggText: z.string().default(""), + + reuseBuilding: z + .array( + z.object({ + bdstRuseFcltPk: z.number().nullish(), + bdstInfoFk: z.number().nullish(), + instlCmptnDt: z.coerce.date().default(() => new Date()), + instlCst: z.number().default(0), + operMngCd: z.string().nullish().default(""), + + anlOperCst: z.number().default(0), + instlCstSprtYn: z.enum(["Y", "N"]), + instlCstSprtAmt: z.number().default(0), + + grntchBdstCertYn: z.enum(["Y", "N"]), + grntchBdstCertYr: z + .string() + .default("") + .refine((year) => year.length === 0 || /^\d{4}$/.test(year), { + message: "4자리 연도만 입력하세요", + }), + grntchBdstCertNo: z.string().default(""), + grntchBdstCertGrd: z.string().default(""), + + crgRdctYn: z.string().default("N").nullish(), // 안쓰는 값 + crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 + crgRdctAmt: z.number().default(0), // 안쓰는 값 + ruseFcltFlg: z.string().default(""), + bdstUsgCd: z.number().default(0), + lawTrgtYn: z.string().default(""), + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), + + rswtrObj: z + .object({ + bdstRuseFcltFk: z.number().nullish(), + wtrUsqty: z.number().default(0), + oblgTrgtWtrUsqty: z.number().nullish().default(0), + orgwtKndCd: z.string().default(""), + filtrTrtmfctCpct: z.number().default(0), + filtrTrtmfctProcs: z.string().default(""), + filtrTrtmfctUsgCd: z.string().default("").nullish(), + rswtrUsqty: z.number().default(0), + altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값 + altUswtrKndCd: z.string().default("").nullish(), //안쓰는값 + altUswtrUtztnQnt: z.number().default(0), //안쓰는값 + altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값 + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), + }) + .default({}), + }) + ) + .default([]), + + chgBuilding: z + .array( + z.object({ + bdstInfoFk: z.number().nullish(), + archChgPrmsnDt: z.coerce.date(), + archChgArea: z.string().default("-"), + bdstChgNm: z.string().default(""), + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), + }) + ) + .default([]), +}); + +export type customRswtBdst = z.infer<typeof rswtZod>; + +const rswtRes: customRswtBdst[] = []; + +export type rswtFacState = { + rswt: { + isInit: boolean; + upHeadList: TableUpperProps[]; + rswtRes: customRswtBdst[]; + }; +}; +type rswtFacSActions = { + initRswt: (code: string | number) => void; + rswtBdstUpdate: (data: customRswtBdst, code: string | number) => void; + getRswtBdstData: (pageNation: number, code: string | number) => void; + rswtBdstSearch: ( + txt: string, + pageNation: number, + code: string | number + ) => void; + rswtBdstReg: (data: customRswtBdst, code: string | number) => void; +}; + +export type rswtFacType = rswtFacState & rswtFacSActions; + +export const rswtFacStateExport: rswtFacState = { + rswt: { + isInit: false, + upHeadList: upHeadList, + rswtRes: rswtRes, + }, +}; + +const initFetch = async (code: string | number) => { + const rswtRes = await getFetch("wr", `/reuseFc/waterreuseFc/1/10/${code}`); + return rswtRes; +}; +export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( + set: any +) => { + return { + rswtBdstSearch: async ( + txt: string, + pageNation: number, + code: string | number + ) => { + try { + const rswtRes = await getFetch( + "wr", + `/reuseFc/waterreuseFc/${pageNation}/20/${code}` + + (txt ? `/${txt}` : "") + ); + set((state: rswtFacState) => { + return { + ...state, + rswt: { + ...state.rswt, + rswtRes: rswtRes, + isInit: false, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + getRswtBdstData: async (pageNation: number, code: string | number) => { + try { + const rswtRes = await getFetch( + "wr", + `/reuseFc/waterreuseFc/${pageNation}/10/${code}` + ); + console.log(pageNation); + // 여기서 상태 업데이트 + return set((state: rswtFacState) => ({ + ...state, + rswt: { + ...state.rswt, + rswtRes: rswtRes, + isInit: false, + }, + })); + } catch (e) { + console.log(e); + } + }, + initRswt: async (code: string | number) => { + try { + const rswtRes = await initFetch(code); + return set((state: rswtFacState) => ({ + ...state, + rswt: { + ...state.rswt, + rswtRes: rswtRes, + isInit: false, + }, + })); + } catch (error) { + console.log(error); + } + }, + rswtBdstUpdate: async (data: customRswtBdst, code: string | number) => { + try { + const chData = await makeMdfr(data); + const res = await putFectch("/reuseFc/waterreuseFc", chData); + if (res.ok) { + alert("등록이 완료되었습니다."); + const rswtRes = await initFetch(code); + set((state: rswtFacState) => ({ + ...state, + rswt: { + ...state.rswt, + rswtRes: rswtRes, + isInit: false, + }, + })); + } + } catch (errorUpdate) { + alert( + "업데이트 오류입니다. " + + (errorUpdate instanceof Error ? errorUpdate.message : "") + ); + } finally { + } + }, + rswtBdstReg: async (data: customRswtBdst, code: string | number) => { + try { + const chData = await makeRstr(data); + const res = await postFectch("/reuseFc/waterreuseFc", chData); + if (res.ok) { + if (data.chgBuilding.length > 0) { + await res + .json() + .then((resData: any) => { + const chPK = resData?.data?.bdstInfoPk; + const makeCh = resData.data.chgBuilding.map( + (item: chgBuilding) => ({ + ...item, + bdstInfoFk: chPK, + }) + ); + const chch = postFectch("/reuseFc/chgBuilding", makeCh); + return chch; + }) + .then((chch: any) => { + if (chch.ok) { + alert("등록이 완료되었습니다."); + } + }); + } else { + alert("등록이 완료되었습니다."); + } + } + const rainRes = await initFetch(code); + set((state: rswtFacState) => ({ + ...state, + rain: { + ...state.rswt, + rswtRes: rswtRes, + isInit: false, + }, + })); + } catch (e) { + console.log("e", e); + alert("등록 오류입니다."); + } finally { + } + }, + }; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel.tsx new file mode 100644 index 000000000..591623911 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel.tsx @@ -0,0 +1,34 @@ +import type { SelectDataType } from "@common/business_components/ui"; +import type { customHtdflBdst } from "../../fcltReg/htdflReg"; + +export const excelHtdflSttnParsing = ( + data: customHtdflBdst[], + sidoCode: SelectDataType[], + sggCode: SelectDataType[] +): string[][] => { + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + sidoCode.find((sido) => sido.val === item.ctpvCd.toString())?.text ?? "", + sggCode.find((sgg) => sgg.val === item.sggCd.toString())?.text ?? "", + item.bdstNm ?? "", + item.daddr ?? "", + String(item.frstArchPrmsnDt ?? ""), + String(item.reuseBuilding?.[0]?.htdflObj.etgtType ?? ""), + String(item.reuseBuilding?.[0]?.htdflObj.fclCpct ?? ""), // fix: added missing parenthesis + String(item.reuseBuilding?.[0]?.htdflObj.plwtYn ? "Y" : "N"), // fix: added missing parenthesis + String(item.reuseBuilding?.[0]?.htdflObj.orgwtWtritSpt ?? ""), // fix: added missing parenthesis + String(item.reuseBuilding?.[0]?.htdflObj.wtritEntrcTp ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj.orgwtOcrnProcs ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.amtgnr ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.prcsProcsYn ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.prcsFcltCpct ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.fmYn ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.utztnCpct ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.utztnUsg ?? ""), // fix: removed as it doesn't exist in the provided context + String(item.reuseBuilding?.[0]?.htdflObj?.emsnExitTp ?? ""), // fix: removed as it doesn't exist in the provided context + ]); + }); + return excelExportData; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/headList.tsx new file mode 100644 index 000000000..756c47b9b --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/headList.tsx @@ -0,0 +1,312 @@ +import type { z } from "zod"; +import type { sttnHeadListType } from "../rainSttn/headList"; +import type { CommonTableProps } from "@/app/store/common/type/header"; + +export const htdflSttHeadListExport: z.infer<typeof sttnHeadListType> = [ + { + id: "cityText", + title: "시도", + visible: true, + }, + { + id: "sggText", + title: "시군구", + visible: true, + }, + { + id: "bdstNm", + title: "발전소명", + visible: true, + }, + { + id: "daddr", + title: "주소", + visible: true, + }, + { + id: "frstArchPrmsnDt", + title: "준공년월", + visible: true, + }, + { + id: "etgtType", + title: "발전유형", + visible: true, + }, + { + id: "fclCpct", + title: "설비용량(KW)", + visible: true, + }, + { + id: "plwtYn", + title: "담수화여부", + visible: true, + }, + { + id: "orgwtWtritSpt", + title: "원수취수지점", + visible: true, + }, + { + id: "wtritEntrcTp", + title: "취수구온도(℃)", + visible: true, + }, + + { + id: "orgwtOcrnProcs", + title: "원수발생공정", + visible: true, + }, + + { + id: "amtgnr", + title: "발생량", + visible: true, + }, + { + id: "prcsProcsYn", + title: "처리공정여부", + visible: true, + }, + { + id: "prcsFcltCpct", + title: "처리시설용량(m³)", + visible: true, + }, + { + id: "fmYn", + title: "유량계여부", + visible: true, + }, + { + id: "utztnCpct", + title: "이용량(m³)", + visible: true, + }, + { + id: "utztnUsg", + title: "이용용도", + visible: true, + }, + + { + id: "emsnExitTp", + title: "배출구온도(℃)", + visible: true, + }, + +]; + + + +// id는 다확인해야함 +export const htdflSttNodeListExport: CommonTableProps[] = [ + { + id: "bdnstInfo", + title: "1.건축물정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 5, + }, + { + id: "htdflFcltInfo", + title: "2.온배수 시설현황", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "htdflMngInfo", + title: "3.온배수운영현황", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 11, + }, + + { + id: "adminDiv", + title: "행정구역", + visible: true, + row: 2, + upName: "bdnstInfo", + rowSpan: 1, + colSpan: 2, + }, + + // 3번째줄 시작 + { + id: "cityText", + title: "시도", + visible: true, + upName: "adminDiv", + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggText", + title: "시군구", + visible: true, + upName: "adminDiv", + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + // 2번째 줄 + // 1번째 줄 건축물정보 아래의 행으로 만들 헤더 + { + id: "bdstNm", + title: "발전소명", + visible: true, + upName: "bdnstInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "daddr", + title: "주소", + visible: true, + upName: "bdnstInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "frstArchPrmsnDt", + title: "준공년월", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "etgtType", + title: "발전유형", + upName: "htdflFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "fclCpct", + title: "설비용량(KW)", + upName: "htdflFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "plwtYn", + title: "담수화여부", + upName: "htdflFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "orgwtWtritSpt", + title: "원수취수지점", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "wtritEntrcTp", + title: "취수구온도(℃)", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "orgwtOcrnProcs", + title: "원수발생공정", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "amtgnr", + title: "발생량", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "prcsProcsYn", + title: "처리공정여부", + upName: "prcsProcs", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "prcsFcltCpct", + title: "처리시설용량(m³)", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "fmYn", + title: "유량계여부", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "utztnCpct", + title: "이용량(m³)", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "utztnUsg", + title: "이용용도", + upName: "htdflMngInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "emsnExitTp", + title: "배출구온도(℃)", + visible: true, + upName: "htdflMngInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + +]; + diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx new file mode 100644 index 000000000..dccc8719c --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx @@ -0,0 +1,121 @@ +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { htdflSttHeadListExport, htdflSttNodeListExport } from "./headList"; +import type { sttnHeadListType } from "../rainSttn/headList"; +import type { z } from "zod"; +import { controlHeadView } from "../rainSttn"; +import type { customHtdflBdst } from "../../fcltReg/htdflReg"; +import { fetchType, getFetch } from "@/components/lib/api"; + +const htdflSttHeadList = htdflSttHeadListExport; +const htdflSttNodeList = htdflSttNodeListExport; + +export type htdflSttnState = { + htdflSttn: { + isInit: boolean; + htdflSttHeadList: z.infer<typeof sttnHeadListType>; + htdflSttNodeList: CommonTableProps[]; + htdflSttnRes: customHtdflBdst[] + excelList: []; + + }; +}; +type htdflSttnActions = { + initHtdfl: (con: string) => void; + htdflSttnSearch: (page: number, total: string, con: string) => void; + htdflChSttnHead: ( + headList: z.infer<typeof sttnHeadListType>, + ) => void; +}; + +export type htdflSttnType = htdflSttnState & htdflSttnActions; + +export const htdflSttnStateExport: htdflSttnState = { + htdflSttn: { + htdflSttHeadList: htdflSttHeadList, + htdflSttNodeList: htdflSttNodeList, + htdflSttnRes: [], + excelList: [], + isInit: false, + }, +}; +const initFetch = async (code: string | number) => { + const htdflRes = await getFetch( + "wr", + `/drain/facilityList/1/20/${code}`) + + return htdflRes; +}; + +export const htdflSttnActionsExport: (set: any) => htdflSttnActions = ( + set: any +) => { + return { + initHtdfl: async (code: string | number) => { + try { + const htdflSttnRes = await initFetch(code); + set((state: htdflSttnState) => { + return { + ...state, + htdflSttn: { + ...state.htdflSttn, + htdflSttnRes: htdflSttnRes, + isInit: false, + }, + }; + }); + } catch (error) { + console.log(error); + } + }, + htdflSttnSearch: async (page: number, total: string, con: string) => { + try { + const htdflSttnRes = await getFetch( + fetchType.WR, + `/drain/facilityList/${page}/${total}/${con}` + ); + if (Number(total) > 9000) { + + + set((state: htdflSttnState) => { + + return { + ...state, + htdflSttn: { + ...state.htdflSttn, + excelList: htdflSttnRes, + }, + }; + }); + } else + set((state: htdflSttnState) => { + return { + ...state, + htdflSttn: { + ...state.htdflSttn, + htdflSttnRes: htdflSttnRes, + isInit: false, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + htdflChSttnHead: async ( + headList: z.infer<typeof sttnHeadListType> + ): Promise<void> => { + + const changeNodeList = controlHeadView(headList, htdflSttNodeList) + + return set((state: htdflSttnState) => ({ + htdflSttn: { + ...state.htdflSttn, + htdflSttHeadList: headList, + htdflSttNodeList: changeNodeList, + }, + })); + }, + + + } +} \ No newline at end of file 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..8a1db05a2 --- /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.rvbsn, + String(item.lgbsn), + item.mdmbsn ?? "", + item.rgnNm ?? "", + String(item.sido ?? ""), + String(item.gugun ?? ""), + String(item.bscFctNm ?? ""), + String(item.efflWqltStndrdNm ?? ""), + String(item.fctCpc ?? ""), + String(item.tel ?? ""), + String(item.ruseUtztnFcltNm ?? ""), + String(item.fcltCpct ?? ""), + String(item.splyExtraCpct ?? ""), + String(item.ruseCpct ?? ""), + String(item.usageTotal ?? ""), + String(item.inUsage ?? ""), + String(item.outUsage ?? ""), + ]) + }) + return excelExportData; +} \ No newline at end of file diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/headList.tsx new file mode 100644 index 000000000..7c2953932 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/headList.tsx @@ -0,0 +1,541 @@ +import type { sttnHeadListType } from "../rainSttn/headList"; +import type { + CommonTableProps, + TableUpperProps, +} from "@/app/store/common/type/header"; +import { z } from "zod"; + +export const pbnSttHeadListExport: z.infer<typeof sttnHeadListType> = [ + { id: "rvbsn", title: "권역", visible: true }, + { + id: "lgbsn", + title: "단위유역", + visible: true, + }, + { + id: "mdmbsn", + title: "중권역", + visible: true, + }, + { id: "rgnNm", title: "환경청", visible: true }, + { + id: "sido", + title: "시도", + visible: true, + }, + { + id: "gugun", + title: "시군구", + visible: true, + }, + { id: "bscFctNm", title: "시설명", visible: true }, + { id: "efflWqltStndrdNm", title: "지역구분", visible: true }, + + { id: "tel", title: "전화번호", visible: true }, + { id: "fctCpc", title: "시설용량(톤/일)", visible: true }, + { id: "ruseUtztnFcltNm", title: "재이용시설명", visible: true }, + + { + id: "fcltCpct", + title: "시설용량(m³/일)", + visible: true, + }, + { + id: "splyExtraCpct", + title: "하수처리량(m³/일)", + visible: true, + }, + { + id: "ruseCpct", + title: "재이용량(m³/일)", + visible: true, + }, + + { + id: "usageTotal", + title: "합계", + visible: true, + }, + { + id: "inUsage", + title: "장내용수", + visible: true, + }, + { + id: "outUsage", + title: "장외용수", + visible: true, + }, + + { + id: "operMthd", + title: "운영방식", + visible: true, + }, + { + id: "rprtvBzenty", + title: "대행업체", + visible: true, + }, + { + id: "bgngDt", + title: "시작일", + visible: true, + }, + { + id: "endDt", + title: "종료일", + visible: true, + }, +]; + +// id는 다확인해야함 +export const pbnstSttNodeListExport: CommonTableProps[] = [ + { + id: "catchment", + title: "유역", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "operMngCd", + title: "환경청", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "adminDiv", + title: "행정구역", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 2, + }, + { + id: "bdnstNm", + title: "시설명", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "locDiv", + title: "지역구분", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "tel", + title: "전화번호", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "fctCpc", + title: "시설용량(톤/일)", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "rusNm", + title: "재이용시설명", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { + id: "rusSup", + title: "재이용수공급가능량", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "yearResult", + title: "연간실적정보(m³)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "mngInfo", + title: "운영정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 4, + }, + { + id: "a", + title: "권역", + upName: "catchment", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "aaa", + title: "단위유역", + upName: "catchment", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "aaaa", + title: "중권역", + upName: "catchment", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "cityText", + title: "시도", + upName: "adminDiv", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggText", + title: "시군구", + upName: "adminDiv", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "fcltCpct", + title: "시설용량(m³/일)", + upName: "rusSup", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "trbt", + title: "하수처리량(m³/일)", + upName: "rusSup", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "ruseCpct", + title: "재이용량(m³/일)", + upName: "rusSup", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "total", + title: "합계", + upName: "yearResult", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "ithlWtr", + title: "장내용수", + upName: "yearResult", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "othlWtr", + title: "장외용수", + upName: "yearResult", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "mngMeth", + title: "운영방식", + upName: "mngInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "rprtvBzenty", + title: "대행업체", + upName: "mngInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "firstDay", + title: "시작일", + upName: "mngInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, + { + id: "LastDay", + title: "종료일", + upName: "mngInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1, + }, +]; + +export const ruseFcltNodeListExport: TableUpperProps[] = [ + { id: "cycl", title: "차수", visible: true, row: 1, rowSpan: 3, colSpan: 1 }, + { id: "seCd", title: "구분", visible: true, row: 1, rowSpan: 3, colSpan: 1 }, + { + id: "busiTotal", + title: "사업개요", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 9, + }, + { + id: "ruseFclt", + title: "재처리시설", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3, + }, + { + id: "mainSplyPumpCpct", + title: "메인공급펌프용량", + visible: true, + row: 1, + rowSpan: 3, + colSpan: 1, + }, + { + id: "buziFee", + title: "사업비(백만원)", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 5, + }, + { + id: "instlAprvSncnDt", + title: "설치승인・인가일", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "bizCmcnDay", + title: "사업준공일", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "rprcsFcltCpct", + title: "시설용량(m³/일)", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "rprcsMthdCd", + title: "재처리방식", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "majorPrcsMsrcCd", + title: "주처리공법", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "inoutYn", + title: "위치처리장(내・외)", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "addr", + title: "위치(주소)", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { id: "gvsbs", title: "국고", visible: true, row: 3, rowSpan: 1, colSpan: 1 }, + { + id: "lcep", + title: "지방비", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "cusrAlotm", + title: "민간투자비", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + { id: "etc", title: "기타", visible: true, row: 3, rowSpan: 1, colSpan: 1 }, + { + id: "totalMoney", + title: "합계", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, +]; + +export const ruseSplyHeadListExport: TableUpperProps[] = [ + { id: "cycl", title: "차수", visible: true }, + { id: "orgwtWtritQnt", title: "원수취수량", visible: true }, + { id: "tmsYn", title: "하수처리량TMS 전후", visible: true }, + { id: "dschgFmYn", title: "하수처리장 방류 유량계 전·후​", visible: true }, + { id: "dtlPstnExpln", title: "상세위치 설명", visible: true }, +]; + +export const rusePplHeadListExport: TableUpperProps[] = [ + { id: "cycl", title: "차수", visible: true }, + { id: "ppMqlt", title: "관재질", type: "select", visible: true }, + { id: "pdmt", title: "관경", type: "select", visible: true }, + { id: "cpct", title: "연장", edit: true, visible: true }, +]; + +export const ruseGuranteeHeadListExport: TableUpperProps[] = [ + { id: "cycl", title: "차수", visible: true }, + { id: "bod", title: "BOD(mg/L)", visible: true }, + { id: "grssClfg", title: "총대장균군수(개/mg)", visible: true }, + { id: "combResdch", title: "결합잔류염소(mh/L)", visible: true }, + { id: "trbt", title: "탁도(NTU)", visible: true }, + { id: "smll", title: "냄새", visible: true }, + { id: "crmty", title: "색도", visible: true }, + { id: "tn", title: "총질소(mg/L)", visible: true }, + { id: "tph", title: "총인(mg/L)", visible: true }, + { id: "ph", title: "수소이온농도", visible: true }, + { id: "clIonQnt", title: "염화물(mg/L)", visible: true }, + { id: "cdtnv", title: "전기전도도(μs/cm)​", visible: true }, +]; + +export const ruseDmdHeadListExport: TableUpperProps[] = [ + { id: "cycl", title: "차수", visible: true }, + { id: "dmdCoNm", title: "수요처명", visible: true }, + { id: "daddr", title: "위치(주소)", visible: true }, + { id: "useUsgCd", title: "사용용도", visible: true }, + { id: "splyBgngDay", title: "공급시작일", visible: true }, + { id: "splyEndDay", title: "공급종료일", visible: true }, + { id: "splyCpct", title: "공급량(m³/일)", visible: true }, + { id: "splyFmYon", title: "공급유량계 유무", visible: true }, +]; + +export const ruseFcltHeadListExport: CommonTableProps[] = [ + { id: "cycl", title: "차수", visible: true}, + { id: "seCd", title: "구분", visible: true}, + { id: "gvsbs", title: "국고", visible: true }, + { + id: "lcep", + title: "지방비", + visible: true, + }, + { + id: "cusrAlotm", + title: "민간투자비", + visible: true, + }, + + { id: "etc", title: "기타", visible: true }, + { + id: "totalMoney", + title: "합계", + visible: true, + }, + { + id: "instlAprvSncnDt", + title: "설치승인・인가일", + visible: true, + }, + { + id: "bizCmcnDay", + title: "사업준공일", + visible: true, + }, + { + id: "rprcsFcltCpct", + title: "시설용량(m³/일)", + visible: true, + }, + { + id: "rprcsMthdCd", + title: "재처리방식", + visible: true, + }, + { + id: "majorPrcsMsrcCd", + title: "주처리공법", + visible: true, + }, + { + id: "inoutYn", + title: "위치처리장(내・외)", + visible: true, + }, + { + id: "addr", + title: "위치(주소)", + visible: true, + }, + { + id: "mainSplyPumpCpct", + title: "메인공급펌프용량", + visible: true, + }, +]; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx new file mode 100644 index 000000000..33bce5520 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx @@ -0,0 +1,322 @@ +import type { CommonTableProps, TableUpperProps } from "@/app/store/common/type/header"; +import type { z } from "zod"; +import { pbnSttHeadListExport, pbnstSttNodeListExport, ruseDmdHeadListExport, ruseFcltHeadListExport, ruseFcltNodeListExport, ruseGuranteeHeadListExport, rusePplHeadListExport, ruseSplyHeadListExport } from "./headList"; +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; +import { controlHeadView } from "../rainSttn"; +import type { sttnHeadListType } from "../rainSttn/headList"; +import type { customPbnst, pubHasuType } from "../../fcltReg/pbnstReg/type"; +import { excelPbnstSttnParsing } from "./excel"; +import type { FcPrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +import type { ReturnType } from "@common/business_components/ui"; +import type { WaterReuseFacility } from "./type"; +import moment from "moment"; + +const pbnstSttHeadList = pbnSttHeadListExport; +const pbnstSttNodeList = pbnstSttNodeListExport; + +export type pbnstSttnState = { + pbnstSttn: { + pbnstSttnRes: WaterReuseFacility[]; + pbnstSearchData: WaterReuseFacility[]; + pbnstSttHeadList: CommonTableProps[]; + pbnstSttNodeList: CommonTableProps[]; + pbnstDetail: customPbnst; + ruseFcltNodeList:TableUpperProps[] + ruseSplyHeadList:TableUpperProps[] + rusePplHeadList:TableUpperProps[] + ruseGuranteeHeadList:TableUpperProps[] + ruseDmdHeadList:TableUpperProps[] + ruseFcltHeadList:TableUpperProps[] + excelList: []; + isInit: boolean; + }; +}; + +type pbnstSttnActions = { + pbnstChSttnHead: (headList: z.infer<typeof sttnHeadListType>) => void; + pbnstSttnSearch: ( + page: string, + total: string, + reurnValue: ReturnType, + searchNm: string | undefined, + year: string + ) => void; + pbnSttnInit: ( + page: string, + total: string, + reurnValue: ReturnType, + year: string + ) => void; + getPbnStnDetail: (pbnstRuseFcltPk: string) => void; + pbnNmSearch: ( + page: string, + total: string, + reurnValue: ReturnType, + searchNm: string | undefined, + year: string + ) => void; +}; + +export type pbnstSttnType = pbnstSttnState & pbnstSttnActions; + +export const pbnstSttnStateExport: pbnstSttnState = { + pbnstSttn: { + pbnstSttHeadList: pbnstSttHeadList, + pbnstSttNodeList: pbnstSttNodeList, + ruseFcltNodeList:ruseFcltNodeListExport, + ruseSplyHeadList:ruseSplyHeadListExport, + rusePplHeadList:rusePplHeadListExport, + ruseGuranteeHeadList:ruseGuranteeHeadListExport, + ruseDmdHeadList:ruseDmdHeadListExport, + ruseFcltHeadList:ruseFcltHeadListExport, + pbnstSttnRes: [], + pbnstDetail: {} as customPbnst, + pbnstSearchData: [], + excelList: [], + isInit: false, + }, +}; + +const hasuListFetch = async ( + page: string, + total: string, + reurnValue: ReturnType, + searchNm: string | undefined, + year: string +) => { + let url = ""; + if (reurnValue.partValue === "A") { + url = `/prcsFclt/info/${page}/${total}/${reurnValue.partValue}/${ + reurnValue.sidoValue ? reurnValue.sidoValue : reurnValue.sigunValue + }/0/1/SW` + } else if (reurnValue.partValue === "R") { + url = `/prcsFclt/info/${page}/${total}/${reurnValue.partValue}/${ + reurnValue.regionValue + ? reurnValue.regionValue + : reurnValue.unitWaterShedValue + ? reurnValue.unitWaterShedValue + : reurnValue.middleRegionValue + }/0/1/SW` + } else { + url = `/prcsFclt/info/${page}/${total}/${reurnValue.envPartValue}/${reurnValue.sidoValue ? reurnValue.sidoValue : reurnValue.sigunValue}/0/1/SW` + + } + if( searchNm&& searchNm?.length>0){ + url+="/"+searchNm + } + const pbnList = await getFetch(fetchType.COMMON, url); + const arrayList = pbnList.map((item: pubHasuType) => item.bscFctCd); + const getRuseList = await postFectch( + `/reuseFc/pubDetailList/${year}`, + arrayList, + fetchType.WR + ); + const checkWrRegListRes = getRuseList.data; + + const data: WaterReuseFacility[] = checkWrRegListRes.map( + (item: FcPrfType) => { + const findData = pbnList.find( + (fclt: pubHasuType) => + fclt.bscFctCd === item.pbnstRuseFcltPk.substring(0, 11) + ); + + const usage = item.prfList.reduce( + (acc, cur) => { + acc.inUsage += + cur.wshwtr + + cur.coolUswtr + + cur.cleanUswtr + + cur.drwtUswtr + + cur.dltnUswtr + + cur.ithlRswtr + + cur.ithlEtcUswtr + + cur.cleanToilUswtr + + cur.resn; + acc.outUsage += + cur.lndwtr + + cur.hydUswtr + + cur.ugwtChr + + cur.farmUswtr + + cur.rvrKeepUswtr + + cur.indstUswtr; + acc.total = acc.inUsage + acc.outUsage; + + return acc; + }, + { total: 0, inUsage: 0, outUsage: 0 } + ); + + return { + rvbsn: findData?.rvbsn, + lgbsn: findData?.lgbsn, + mdmbsn: findData?.mdmbsn, + rgnNm: findData?.rgnNm, + sido: findData?.sido, + gugun: findData?.gugun, + bscFctNm: findData?.bscFctNm, + efflWqltStndrdNm: findData?.efflWqltStndrdNm, + fctCpc: findData.fctCpc, + tel: findData?.tel ?? "-", + ruseUtztnFcltNm: item.ruseUtztnFcltNm, + fcltCpct: item.fcltCpct, + splyExtraCpct: item.splyExtraCpct, + ruseCpct: item.ruseCpct, + pbnstRuseFcltPk: item.pbnstRuseFcltPk, + usageTotal: usage.total, + inUsage: usage.inUsage, + outUsage: usage.outUsage, + operMthd: item.operList[0]?.operMthd, + rprtvBzenty: item.operList[0]?.rprtvBzenty, + bgngDt: moment(item.operList[0]?.bgngDt).format("YYYY-MM-DD") ?? "", + endDt: moment(item.operList[0]?.endDt).format("YYYY-MM-DD") ?? "", + searchNm: findData.bscFctNm, + searchPk: findData.bscFctCd, + prfList:item?.prfList??[] + }; + } + ); + return data; +}; + +export const pbnstSttnActionsExport: (set: any) => pbnstSttnActions = ( + set: any +) => { + return { + pbnstChSttnHead: async ( + headList: z.infer<typeof sttnHeadListType> + ): Promise<void> => { + const changeNodeList = controlHeadView(headList, pbnstSttNodeList); + + return set((state: pbnstSttnState) => ({ + pbnstSttn: { + ...state.pbnstSttn, + pbnstSttHeadList: headList, + pbnstSttNodeList: changeNodeList, + }, + })); + }, + pbnstSttnSearch: async ( + page: string, + total: string, + reurnValue: ReturnType, + searchNm: string | undefined, + year: string + ) => { + try { + const pbnstSttnRes = await hasuListFetch( + page, + total, + reurnValue, + searchNm, + year + ); + console.log("pbnstSttnRes", pbnstSttnRes); + 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); + } + }, + pbnSttnInit: async ( + page: string, + total: string, + reurnValue: ReturnType, + year: string + ) => { + try { + + const pbnstSttnRes = await hasuListFetch( + page, + total, + reurnValue, + undefined, + year + ); + + console.log('pbnstSttnRes ', pbnstSttnRes) + set((state: pbnstSttnState) => { + return { + ...state, + pbnstSttn: { + ...state.pbnstSttn, + pbnstSttnRes: pbnstSttnRes, + isInit: false, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + getPbnStnDetail: async (pbnstRuseFcltPk: string) => { + try { + let pbnstDetail = await getFetch("wr", `/reuseFc/pubFc/${pbnstRuseFcltPk}`); + if(pbnstDetail.length>=0){ + pbnstDetail= pbnstDetail.find((item: customPbnst) =>item.pbnstRuseFcltPk === pbnstRuseFcltPk) + } + console.log( 'pbnstDetail ', pbnstDetail) + set((state: pbnstSttnState) => { + return { + ...state, + pbnstSttn: { + ...state.pbnstSttn, + pbnstDetail: pbnstDetail, + }, + }; + }); + } catch (errorHasu) { + console.log(errorHasu); + } + }, + pbnNmSearch: async ( + page: string, + total: string, + reurnValue: ReturnType, + searchNm: string | undefined, + year: string + ) => { + try { + + const pbnstSttnRes = await hasuListFetch( + page, + total, + reurnValue, + searchNm, + year + ); + + set((state: pbnstSttnState) => { + return { + ...state, + pbnstSttn: { + ...state.pbnstSttn, + pbnstSearchData: pbnstSttnRes, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + }; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type.tsx new file mode 100644 index 000000000..e4e86da5d --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type.tsx @@ -0,0 +1,27 @@ +import type { PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; + +export type WaterReuseFacility = { + bscFctNm: string; // 기본시설명 (예: "난지") + efflWqltStndrdNm: string; // 방류수 수질 기준명 (예: "500톤 이상(Ⅲ지역)") + fcltCpct: number; // 시설 용량 (숫자형, 예: 1) + fctCpc: string; // 시설 처리 능력 (예: "2360000") + gugun: string; // 구군 정보 (예: " ") + inUsage: number; // 유입량 (예: 1387) + lgbsn: string; // 대수계 (예: "한강본류") + mdmbsn: string; // 중권역 (예: "한강서울") + outUsage: number; // 유출량 (예: 900) + pbnstRuseFcltPk: string; // 공공하수 재이용 시설 PK (예: "11000SW001R1") + rgnNm: string; // 관할 기관명 (예: "한강유역환경청") + ruseCpct: number; // 재이용 가능 용량 (예: 123) + ruseUtztnFcltNm: string; // 재사용 활용 시설명 (예: "난지 재사용") + rvbsn: string; // 소수계 (예: "한강") + sido: string; // 시도 정보 (예: "서울특별시") + splyExtraCpct: number; // 추가 공급 용량 (예: 123) + tel: string; // 전화번호 (예: "-") + usageTotal: number; // 총 사용량 (예: 2287) + operMthd: string; + rprtvBzenty: string; + bgngDt: string; + endDt: string; + prfList:PrfType[]; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx new file mode 100644 index 000000000..13530a81f --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/excel.tsx @@ -0,0 +1,40 @@ +import type { SelectDataType } from "@common/business_components/ui"; +import type { customRainBdst } from "../../fcltReg/rainReg"; + +export const excelRainSttnParsing = ( + data: customRainBdst[], + sidoCode: SelectDataType[], + sggCode: SelectDataType[] +): string[][] => { + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + sidoCode.find((sido) => sido.val === item.ctpvCd.toString())?.text ?? "", + sggCode.find((sgg) => sgg.val === item.sggCd.toString())?.text ?? "", + item.bdstNm ?? "", + item.daddr ?? "", + String(item.bdstInfoPk ?? ""), + String(item.frstArchPrmsnDt ?? ""), + String(item.archArea ?? ""), + String(item.chgBuilding.length > 0 ? "Y" : "N"), + String(item.reuseBuilding?.[0]?.lawTrgtYn ?? ""), + String(item.reuseBuilding?.[0]?.instlCmptnDt ?? ""), + String(item.reuseBuilding?.[0]?.instlCst ?? ""), + item.reuseBuilding?.[0]?.rainyObj?.pntfColKnd ?? "", + String(item.reuseBuilding?.[0]?.rainyObj?.pntfColArea ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctYn ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctCpct ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.filtrTrtmfctPrs ?? ""), + String(item.reuseBuilding?.[0]?.rainyObj?.dtfcltCpct ?? ""), + String(item.reuseBuilding?.[0]?.instlCstSprtYn ?? ""), + String(item.reuseBuilding?.[0]?.instlCstSprtAmt ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertYn ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertYr ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertNo ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertGrd ?? "") + ]); + }); + return excelExportData; +}; + diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx new file mode 100644 index 000000000..7db00678d --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx @@ -0,0 +1,583 @@ +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { z } from "zod"; + +export const sttnHeadListType = z.array( + z.object({ + id: z.string(), + title: z.string(), + visible: z.boolean(), + }) +); + +export const rainSttHeadListExport: z.infer<typeof sttnHeadListType> = [ + { + id: "cityText", + title: "시도", + visible: true, + }, + { + id: "sggText", + title: "시군구", + visible: true, + }, + { + id: "bdstNm", + title: "건축물명", + visible: true, + }, + { + id: "daddr", + title: "주소", + visible: true, + }, + { + id: "bdstUsgCd", + title: "건축물용도코드", + visible: true, + }, + { + id: "frstArchPrmsnDt", + title: "최초건축허가일시", + visible: true, + }, + { + id: "archArea", + title: "건축면적", + visible: true, + }, + { + id: "chBuildingYn", + title: "중축・개축・재축여부", + visible: true, + }, + { + id: "lawTrgtYn", + title: "법적의무대상여부", + visible: true, + }, + { + id: "instlCmptnDt", + title: "설치완료일시", + visible: true, + }, + + { + id: "instlCst", + title: "설치비용", + visible: true, + }, + + { + id: "pntfColKnd", + title: "집수지점종류", + visible: true, + }, + { + id: "pntfColArea", + title: "집수지점면적", + visible: true, + }, + + { + id: "dtfcltCpct", + title: "저류조용량", + visible: true, + }, + { + id: "filtrTrtmfctYn", + title: "여과처리시설유무", + visible: true, + }, + { + id: "filtrTrtmfctCpct", + title: "여과처리시설용량", + visible: true, + }, + { + id: "filtrTrtmfctPrs", + title: "여과처리시설처리공정", + visible: true, + }, + + { + id: "instlCstSprtYn", + title: "설치지원비용여부", + visible: true, + }, + { + id: "instlCstSprtAmt", + title: "설치지원비용", + visible: true, + }, + + { + id: "grntchBdstCertYn", + title: "녹색건축물인증여부", + visible: true, + }, + { + id: "grntchBdstCertYr", + title: "녹색건축물인증년도", + visible: true, + }, + { + id: "grntchBdstCertNo", + title: "녹색건축물인증번호", + visible: true, + }, + { + id: "grntchBdstCertGrd", + title: "녹색건축물인증등급", + visible: true, + }, +]; + +// id는 다확인해야함 +export const rainSttNodeListExport: CommonTableProps[] = [ + { + id: "bdnstInfo", + title: "1.건축물정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 10, + }, + { + id: "rainFcltInfo", + title: "2.빗물이용시설정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 7, + }, + { + id: "insenInfo", + title: "3.인센티브 정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "adminDiv", + title: "행정구역", + visible: true, + row: 2, + upName: "bdnstInfo", + rowSpan: 1, + colSpan: 2, + }, + + // 3번째줄 시작 + { + id: "cityText", + title: "시도", + visible: true, + upName: "adminDiv", + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggText", + title: "시군구", + visible: true, + upName: "adminDiv", + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + // 2번째 줄 + // 1번째 줄 건축물정보 아래의 행으로 만들 헤더 + { + id: "bdstNm", + title: "건축물명", + visible: true, + upName: "bdnstInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "daddr", + title: "주소", + visible: true, + upName: "bdnstInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "bdstUsgCd", + title: "건축물용도코드", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "frstArchPrmsnDt", + title: "최초건축허가일시", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "archArea", + title: "건축면적", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "chBuildingYn", + title: "중축・개축・재축여부", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "lawTrgtYn", + title: "법적의무대상여부", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "instlCmptnDt", + title: "설치완료일시", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "instlCst", + title: "설치비용", + upName: "rainFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "pntfCol", + title: "집수면", + upName: "rainFcltInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 2, + }, + + { + id: "pntfColKnd", + title: "집수지점종류", + upName: "pntfCol", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "pntfColArea", + title: "집수지점면적", + upName: "pntfCol", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + { + id: "dtfcltCpct", + title: "저류조용량", + upName: "rainFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "filtrTrtmfctYn", + title: "여과처리시설유무", + upName: "filtrTrtmfct", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "filtrTrtmfctCpct", + title: "여과처리시설용량", + upName: "filtrTrtmfct", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "filtrTrtmfctPrs", + title: "여과처리시설처리공정", + upName: "filtrTrtmfct", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + { + id: "filtrTrtmfct", + title: "여과등 처리시설", + visible: true, + upName: "insenInfo", + row: 2, + rowSpan: 1, + colSpan: 3, + }, + { + id: "instlCstSprtYn", + title: "설치지원비용여부", + upName: "instlCstSprt", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "instlCstSprtAmt", + title: "설치지원비용", + upName: "instlCstSprt", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + { + id: "instlCstSprt", + title: "설치비지원", + upName: "insenInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 2, + }, + + { + id: "grntchBdstCertYn", + title: "녹색건축물인증여부", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCertYr", + title: "녹색건축물인증년도", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCertNo", + title: "녹색건축물인증번호", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCertGrd", + title: "녹색건축물인증등급", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCert", + title: "녹색건축물인증", + upName: "insenInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 4, + }, +]; + +export const rainSttnDetailNodeListExport:CommonTableProps[] = [ + { id: "totalSplyUswtrUtztnQnt", + title: "연간보급수이용량(m³)", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1, + }, + { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)", + visible: true, + row: 1, + rowSpan: 2, + colSpan: 1 + }, + { id: "useQulity", + title: "이용수질(필요시)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 4 + }, + { id: "ph", title: "PH", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + + }, + { + id: "trbt", + title: "탁도", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + }, + { + id: "grssClfg", + title: "총대장균군수", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + }, + { + id: "etc", + title: "기타", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + }, + { id: "feeHistory", + title: "과태료부과내역", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 3 + }, + { + id: "vltnDt", + title: "부과일(위반일)", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + }, + { + id: "vltnMttr", + title: "위반사항", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + }, + { + id: "levyAmt", + title: "부과금액(천원)", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 1 + }, +]; + +export const rainSttnDetailHeadListExport:CommonTableProps[] = [ + { id: "totalSplyUswtrUtztnQnt", + title: "연간보급수이용량(m³)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1, + }, + { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, + + { id: "ph", title: "PH", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + + }, + { + id: "trbt", + title: "탁도", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, + { + id: "grssClfg", + title: "총대장균군수", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, + { + id: "etc", + title: "기타", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, + + { + id: "vltnDt", + title: "부과일(위반일)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, + { + id: "vltnMttr", + title: "위반사항", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, + { + id: "levyAmt", + title: "부과금액(천원)", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 1 + }, +]; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx new file mode 100644 index 000000000..5aa54ee48 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx @@ -0,0 +1,217 @@ +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { fetchType, getFetch } from "@/components/lib/api"; +import { + type customRainBdst, +} from "../../fcltReg/rainReg"; +import { excelRainSttnParsing } from "./excel"; +import { + rainSttHeadListExport, + sttnHeadListType, + rainSttNodeListExport, + rainSttnDetailHeadListExport, + rainSttnDetailNodeListExport, +} from "./headList"; +import type { z } from "zod"; +import type { CommonTableProps } from "@/app/store/common/type/header"; +import type { SelectDataType } from "@common/business_components/ui"; + +// 테이블 헤드 리스트 +const rainSttHeadList = rainSttHeadListExport; +const rainSttNodeList = rainSttNodeListExport; +const rainSttnDetailNodeList = rainSttnDetailNodeListExport; +const rainSttnDetailHeadList = rainSttnDetailHeadListExport; + +const rainSttnRes: customRainBdst[] = []; +const rainChkRes : rainChkZodType ={} as rainChkZodType + + +const visibleHeadList = (falseHeads:CommonTableProps[], orgNodeList:CommonTableProps[])=>{ + const visibleHeads = orgNodeList.map((node) => { + const copyNode = {...node} + falseHeads.forEach((head)=>{ + if(copyNode.id ===head.id) + copyNode.visible = head.visible + }) + return copyNode; + }) + return visibleHeads +} + +const getColspanReductionMap = (falseHeads:CommonTableProps[], orgList:CommonTableProps[]):Record<string, number> => { + const reduceMap:Record<string, number> = {}; + falseHeads.forEach(head => { + const node = orgList.find(n => n.id === head.id); + if (node?.upName) { + reduceMap[node.upName] = (reduceMap[node.upName] || 0) + 1; + if (node.row&& node.row >= 3) { + const upperUpName = orgList.find(n => n.id === node.upName)?.upName; + if (upperUpName) { + reduceMap[upperUpName] = (reduceMap[upperUpName] || 0) + 1; + } + } + } + }); + return reduceMap; +}; + +// colSpan 반영 및 visible 처리 +const applyColspanReduction = ( + orgList: CommonTableProps[], + reduceMap: Record<string, number> +): CommonTableProps[] => { + return orgList.map(node => { + const newNode: CommonTableProps = { ...node }; + const reduceCount = reduceMap[node.id]; + + if ( + newNode.colSpan !== undefined && + reduceCount !== undefined && + typeof newNode.colSpan === "number" + ) { + newNode.colSpan -= reduceCount; + if (newNode.colSpan <= 0) { + newNode.visible = false; + } + } + + return newNode; + }); +}; +// 일단 헤더가 3개 이상인경우 visible 처리를 위해서 공통함수로 작업 +export const controlHeadView = (headList:CommonTableProps[], orgNodeList:CommonTableProps[] ) =>{ + const falseHeads = headList.filter((item) => item.visible == false); + + const viewHeadList = visibleHeadList(falseHeads, orgNodeList) + const reduceMap= getColspanReductionMap( falseHeads,viewHeadList) + return applyColspanReduction(viewHeadList,reduceMap); +} + + + +export type rainSttnState = { + rainSttn: { + rainSttnRes: customRainBdst[]; + rainSttHeadList: CommonTableProps[]; + rainSttNodeList: CommonTableProps[]; + rainSttnDetailHeadList:CommonTableProps[] + rainSttnDetailNodeList:CommonTableProps[] + rainChkRes:rainChkZodType + excelList: []; + isInit: boolean; + }; +}; +type rainSttnActions = { + rainSttnInit:(con:string) => void + rainSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[], sggCode: SelectDataType[]) => void; + rainChSttnHead: ( + headList: z.infer<typeof sttnHeadListType>, + ) => void; + rainSttnChkSearch:(rusePk:string) => void; +}; + +export type rainSttnType = rainSttnState & rainSttnActions; + +export const rainSttnStateExport: rainSttnState = { + rainSttn: { + rainSttHeadList: rainSttHeadList, + rainSttNodeList: rainSttNodeList, + rainSttnDetailHeadList:rainSttnDetailHeadList, + rainSttnDetailNodeList:rainSttnDetailNodeList, + rainSttnRes: rainSttnRes, + rainChkRes:rainChkRes, + excelList: [], + isInit: false, + }, +}; + +export const rainSttnActionsExport: (set: any) => rainSttnActions = ( + set: any +) => { + return { + rainSttnInit: async( con:string)=>{ + try{ + + const rainSttnRes = await getFetch( + fetchType.WR, + `/reuseFc/rainyInfoList/1/20/${con}` + ); + set((state: rainSttnState) => { + return { + ...state, + rainSttn: { + ...state.rainSttn, + rainSttnRes: rainSttnRes, + isInit: false, + }, + }; + }); + }catch(e){ + console.log(e) + } + }, + rainSttnSearch: async (page: number, total: string, con: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { + try { + const rainSttnRes = await getFetch( + fetchType.WR, + `/reuseFc/rainyInfoList/${page}/${total}/${con}` + ); + if (Number(total) > 9000) { + const rainExcel = excelRainSttnParsing(rainSttnRes, sidoCode, sggCode); + set((state: rainSttnState) => { + return { + ...state, + rainSttn: { + ...state.rainSttn, + excelList: rainExcel, + }, + }; + }); + } else + set((state: rainSttnState) => { + return { + ...state, + rainSttn: { + ...state.rainSttn, + rainSttnRes: rainSttnRes, + isInit: false, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + rainChSttnHead: async ( + headList: z.infer<typeof sttnHeadListType> + ) => { + const changeNodeList= controlHeadView(headList,rainSttNodeList) + + return set((state: rainSttnState) => ({ + rainSttn: { + ...state.rainSttn, + rainSttHeadList: headList, + rainSttNodeList: changeNodeList, + }, + })); + }, + rainSttnChkSearch: async( + rusePk:string + )=>{ + const rainChkRes = await getFetch( + fetchType.WR, + `/reuseChk/rainy/${rusePk}` + ); + + return set((state: rainSttnState) =>({ + rainSttn: { + ...state.rainSttn, + rainChkRes:rainChkRes + }, + + }) ) + } + + }; +}; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx new file mode 100644 index 000000000..ac57a8016 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/excel.tsx @@ -0,0 +1,40 @@ +import type { SelectDataType } from "@common/business_components/ui"; +import type { customRswtBdst } from "../../fcltReg/rswtReg"; +import { useWrStore } from "@/app/store"; + +export const excelRSwtSttnParsing = ( + data: customRswtBdst[], + sidoCode: SelectDataType[], + sggCode: SelectDataType[] +): string[][] => { + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + sidoCode.find((sido) => sido.val === item.ctpvCd.toString())?.text ?? "", + sggCode.find((sgg) => sgg.val === item.sggCd.toString())?.text ?? "", + item.bdstNm ?? "", + item.daddr ?? "", + String(item.bdstUsgText ?? ""), + String(item.frstArchPrmsnDt ?? ""), + String(item.archArea ?? ""), + String(item.reuseBuilding?.[0]?.rswtrObj.wtrUsqty ?? "0"), //물사용량 + String(item.chgBuilding.length > 0 ? "Y" : "N"), //중축・개축・재축여부 + String(item.reuseBuilding?.[0]?.lawTrgtYn ?? ""), //법적의무대상여부 + String(item.reuseBuilding?.[0]?.rswtrObj.oblgTrgtWtrUsqty ?? ""), //의무대상물사용량 + String(item.reuseBuilding?.[0]?.instlCmptnDt ?? ""), //설치완료일시 + String(item.reuseBuilding?.[0]?.instlCst ?? ""), //설치비용 + item.reuseBuilding?.[0]?.rswtrObj?.orgwtKndCd ?? "", //원수종류 + String(item.reuseBuilding?.[0]?.rswtrObj.filtrTrtmfctCpct ?? ""), //처리시설용량 + String(item.reuseBuilding?.[0]?.rswtrObj?.filtrTrtmfctProcs ?? ""), //주요처리공정 + String(item.reuseBuilding?.[0]?.rswtrObj?.filtrTrtmfctUsgCd ?? ""), //이용용도 + String(item.reuseBuilding?.[0]?.instlCstSprtYn ?? ""), //설치지원비용여부 + String(item.reuseBuilding?.[0]?.instlCstSprtAmt ?? ""), //설치지원비용 + String(item.reuseBuilding?.[0]?.grntchBdstCertYn ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertYr ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertNo ?? ""), + String(item.reuseBuilding?.[0]?.grntchBdstCertGrd ?? "") + ]); + }); + return excelExportData; +}; \ No newline at end of file diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/headList.tsx new file mode 100644 index 000000000..804617ad8 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/headList.tsx @@ -0,0 +1,400 @@ +import type { z } from "zod"; +import type { sttnHeadListType } from "../rainSttn/headList"; +import type { CommonTableProps } from "@/app/store/common/type/header"; + +export const rswtSttHeadListExport: z.infer<typeof sttnHeadListType> = [ + { + id: "cityText", + title: "시도", + visible: true, + }, + { + id: "sggText", + title: "시군구", + visible: true, + }, + { + id: "bdstNm", + title: "건축물명", + visible: true, + }, + { + id: "daddr", + title: "주소", + visible: true, + }, + { + id: "bdsgUsgText", + title: "건축물용도", + visible: true, + }, + { + id: "frstArchPrmsnDt", + title: "최초건축허가일시", + visible: true, + }, + { + id: "archArea", + title: "건축면적", + visible: true, + }, + { + id: "wtrUsqty", + title: "물사용량", + visible: true, + }, + { + id: "chBuildingYn", + title: "중축・개축・재축여부", + visible: true, + }, + + { + id: "lawTrgtYn", + title: "법적의무대상여부", + visible: true, + }, + + { + id: "oblgTrgtWtrUsqty", + title: "의무대상물사용량 ", + visible: true, + }, + { + id: "instlCmptnDt", + title: "설치완료일시", + visible: true, + }, + { + id: "instlCst", + title: "설치비용", + visible: true, + }, + + { + id: "orgwtKndCd", + title: "원수종류", + visible: true, + }, + + { + id: "filtrTrtmfctCpct", + title: "처리시설용량", + visible: true, + }, + { + id: "filtrTrtmfctProcs", + title: "주요처리공정", + visible: true, + }, + { + id: "filtrTrtmfctUsgCd", + title: "이용용도", + visible: true, + }, + + { + id: "instlCstSprtYn", + title: "설치지원비용여부", + visible: true, + }, + { + id: "instlCstSprtAmt", + title: "설치지원비용", + visible: true, + }, + + { + id: "grntchBdstCertYn", + title: "녹색건축물인증여부", + visible: true, + }, + { + id: "grntchBdstCertYr", + title: "녹색건축물인증년도", + visible: true, + }, + { + id: "grntchBdstCertNo", + title: "녹색건축물인증번호", + visible: true, + }, + { + id: "grntchBdstCertGrd", + title: "녹색건축물인증등급", + visible: true, + }, +]; + +// id는 다확인해야함 +export const rswtSttNodeListExport: CommonTableProps[] = [ + { + id: "bdnstInfo", + title: "1.건축물정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 12, + }, + { + id: "rswtFcltInfo", + title: "2.중수도시설정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 5, + }, + { + id: "insenInfo", + title: "3.인센티브 정보", + visible: true, + row: 1, + rowSpan: 1, + colSpan: 6, + }, + { + id: "adminDiv", + title: "행정구역", + visible: true, + row: 2, + upName: "bdnstInfo", + rowSpan: 1, + colSpan: 2, + }, + + // 3번째줄 시작 + { + id: "cityText", + title: "시도", + visible: true, + upName: "adminDiv", + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "sggText", + title: "시군구", + visible: true, + upName: "adminDiv", + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + // 2번째 줄 + // 1번째 줄 건축물정보 아래의 행으로 만들 헤더 + { + id: "bdstNm", + title: "건축물명", + visible: true, + upName: "bdnstInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "daddr", + title: "주소", + visible: true, + upName: "bdnstInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "bdsgUsgText", + title: "건축물용도", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "frstArchPrmsnDt", + title: "최초건축허가일시", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "archArea", + title: "건축면적", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "wtrUsqty", + title: "물사용량", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "chBuildingYn", + title: "중축・개축・재축여부", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "lawTrgtYn", + title: "법적의무대상여부", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "oblgTrgtWtrUsqty", + title: "의무대상물사용량", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "instlCmptnDt", + title: "설치완료일시", + upName: "bdnstInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "instlCst", + title: "설치비용", + upName: "rswtFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "orgwtKndCd", + title: "원수종류", + upName: "rswtFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "filtrTrtmfctCpct", + title: "처리시설용량", + upName: "rswtFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "filtrTrtmfctProcs", + title: "주요처리공정", + upName: "rswtFcltInfo", + visible: true, + row: 2, + rowSpan: 2, + colSpan: 1, + }, + + { + id: "filtrTrtmfctUsgCd", + title: "이용용도", + visible: true, + upName: "rswtFcltInfo", + row: 2, + rowSpan: 2, + colSpan: 1, + }, + { + id: "instlCstSprtYn", + title: "설치지원비용여부", + upName: "instlCstSprt", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "instlCstSprtAmt", + title: "설치지원비용", + upName: "instlCstSprt", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + + { + id: "instlCstSprt", + title: "설치비지원", + upName: "insenInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 2, + }, + + { + id: "grntchBdstCertYn", + title: "녹색건축물인증여부", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCertYr", + title: "녹색건축물인증년도", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCertNo", + title: "녹색건축물인증번호", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCertGrd", + title: "녹색건축물인증등급", + upName: "grntchBdstCert", + visible: true, + row: 3, + rowSpan: 1, + colSpan: 1, + }, + { + id: "grntchBdstCert", + title: "녹색건축물인증", + upName: "insenInfo", + visible: true, + row: 2, + rowSpan: 1, + colSpan: 4, + }, +]; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx new file mode 100644 index 000000000..301cd965e --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx @@ -0,0 +1,128 @@ +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { fetchType, getFetch } from "@/components/lib/api"; +import type { z } from "zod"; +import type { customRswtBdst } from "../../fcltReg/rswtReg"; +import { controlHeadView } from "../rainSttn"; +import type { sttnHeadListType } from "../rainSttn/headList"; +import { excelRSwtSttnParsing } from "./excel"; +import { rswtSttHeadListExport, rswtSttNodeListExport } from "./headList"; +import type { SelectDataType } from "@common/business_components/ui"; + + + +// 테이블 헤드 리스트 +const rswtSttNodeList = rswtSttNodeListExport; +const rswtSttHeadList = rswtSttHeadListExport; +const rswtSttnRes: customRswtBdst[] = []; + + + +export type rswtSttnState = { + rswtSttn: { + rswtSttnRes: customRswtBdst[]; + rswtSttHeadList: CommonTableProps[]; + rswtSttNodeList: CommonTableProps[]; + excelList: []; + isInit: boolean; + }; +}; + +type rswtSttnActions = { + rswtSttnInit: (con: string) => void; + rswtSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; + rswtChSttnHead: ( + headList: z.infer<typeof sttnHeadListType>, + ) => void; +}; + +export type rswtSttnType = rswtSttnState & rswtSttnActions; + +export const rswtSttnStateExport: rswtSttnState = { + rswtSttn: { + rswtSttHeadList: rswtSttHeadList, + rswtSttNodeList: rswtSttNodeList, + rswtSttnRes: rswtSttnRes, + excelList: [], + isInit: false, + }, +}; + + + +export const rswtSttnActionsExport: (set: any) => rswtSttnActions = ( + set: any +) => { + return { + rswtSttnInit: async (con: string) => { + try { + + const rswtSttnRes = await getFetch( + fetchType.WR, + `/reuseFc/waterreuseFc/1/20/${con}` + ); + set((state: rswtSttnState) => { + return { + ...state, + rswtSttn: { + ...state.rswtSttn, + rswtSttnRes: rswtSttnRes, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e) + } + }, + rswtSttnSearch: async (page: number, total: string, con: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { + try { + const rswtSttnRes = await getFetch( + fetchType.WR, + `/reuseFc/waterreuseFc/${page}/${total}/${con}` + ); + if (Number(total) > 9000) { + const rswtExcel = excelRSwtSttnParsing(rswtSttnRes, sidoCode, sggCode); + set((state: rswtSttnState) => { + return { + ...state, + rswtSttn: { + ...state.rswtSttn, + excelList: rswtExcel, + }, + }; + }); + } else + set((state: rswtSttnState) => { + + return { + ...state, + rswtSttn: { + ...state.rswtSttn, + rswtSttnRes: rswtSttnRes, + isInit: false, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + rswtChSttnHead: async ( + headList: z.infer<typeof sttnHeadListType> + ): Promise<void> => { + + const changeNodeList = controlHeadView(headList, rswtSttNodeList) + + return set((state: rswtSttnState) => ({ + rswtSttn: { + ...state.rswtSttn, + rswtSttHeadList: headList, + rswtSttNodeList: changeNodeList, + }, + })); + }, + }; +}; diff --git a/apps/wr/app/store/stats/matStat/headList.tsx b/apps/wr/app/store/stats/matStat/headList.tsx new file mode 100644 index 000000000..881662774 --- /dev/null +++ b/apps/wr/app/store/stats/matStat/headList.tsx @@ -0,0 +1,68 @@ +import type { TableUpperProps } from "../../common/type/header"; + +export const matStatHeadListExport :TableUpperProps[]=[ + { + id: "시도", + title: "시도", + row:0, + rowSpan:2 + }, + { + id: "개소", + title: "개소", + row:0, + rowSpan:2 + }, + { + id: "요구수량", + title: "요구수량", + row:0, + rowSpan:2 + }, + + + { + id: "용도", + title: "용도", + row:0, + colSpan:6 + }, + { + id: "조경용수", + title: "조경용수", + row:1, + }, + { + id: "청소화장실용수", + title: "청소화장실용수", + row:1, + }, + { + id: "농업용수", + title: "농업용수", + row:1, + }, + { + id: "공업용수", + title: "공업용수", + row:1, + }, + { + id: "친수용수", + title: "친수용수", + row:1, + }, + { + id: "기타", + title: "기타", + row:1, + }, + { + id: "사업비", + title: "사업비(천원)", + row:0, + rowSpan:2 + }, + + +] \ No newline at end of file diff --git a/apps/wr/app/store/stats/matStat/index.tsx b/apps/wr/app/store/stats/matStat/index.tsx new file mode 100644 index 000000000..3f2e5813f --- /dev/null +++ b/apps/wr/app/store/stats/matStat/index.tsx @@ -0,0 +1,28 @@ +import type { TableUpperProps } from "../../common/type/header"; +import { matStatHeadListExport } from "./headList"; + +type matStatActions = { +} + + +export type matStatState = { + matStat: { + matStatHeadList:TableUpperProps[] + } +} + +export type matStatType = matStatState & matStatActions; + +export const matStatExport: matStatState = { + matStat: { + matStatHeadList:matStatHeadListExport + } +} + +export const matStatActionsExport: (set: any) => matStatActions = ( + set: any +) => { + return { + + } +} \ No newline at end of file diff --git a/apps/wr/app/store/stats/rainStat/excel.tsx b/apps/wr/app/store/stats/rainStat/excel.tsx new file mode 100644 index 000000000..db2099d9a --- /dev/null +++ b/apps/wr/app/store/stats/rainStat/excel.tsx @@ -0,0 +1,41 @@ +import type { RainStatProps } from "."; + +export const excelRainStat = (excelList:RainStatProps[]) : (string | number)[][] => { + const excelData: (string | number)[][] = []; + excelList.forEach((item) => { + excelData.push([ + //시도 + item.ctpvCd ?? "", + //시군구 + item.sggCd ?? "", + //행정구역명 + String(item.areaNm) ?? "", + //시설명 + String(item.bdstNm) ?? "", + //건축물용도코드 (값 없음) + //String(item.bdstUsgCd) ?? "", + "", + //주소 + String(item.daddr) ?? "", + //법적의무대상여부 + String(item.lawTrgtYn)==="Y" ? "대상" : "미대상", + //설치완료일자 + String(item.instlCmptnDt).substring(0, 10) ?? "", + //설치비용 + item.instlCst ?? "", + //접수면종류 + String(item.pntfColKnd) ?? "", + //여과등처리시설유무 + String(item.filtrTrtmfctYn) ?? "", + //처리용량(저류조용량) + item.dtfcltCpct ?? "", + //연간사용량 + item.wtrUsqty ?? "", + //이용용도코드 + item.utztnUsgCd ?? "", + //연간운영비 + item.anlOperCst ?? "", + ]); + }); + return excelData; +} \ No newline at end of file diff --git a/apps/wr/app/store/stats/rainStat/headList.tsx b/apps/wr/app/store/stats/rainStat/headList.tsx new file mode 100644 index 000000000..664e060c4 --- /dev/null +++ b/apps/wr/app/store/stats/rainStat/headList.tsx @@ -0,0 +1,151 @@ +import type { TableUpperProps } from "../../common/type/header"; + +export const rainStatHeadListExport: TableUpperProps[] = [ + { + id: "ctpvCd", + title: "시도", + row:0, + rowSpan:2 + }, + { + id: "tmp", + title: "개소", + row:0, + rowSpan:2 + }, + { + id: "instlCst", + title: "설치비(백만원)", + row:0, + rowSpan:2 + }, + { + id: "dtfcltCpct", + title: "저류조용량(m³/년)", + row:0, + rowSpan:2 + }, + { + id: "wtrUsqty", + title: "연간사용량(㎥/년)", + row:0, + rowSpan:2 + }, + { + id: "a", + title: "이용용도", + row:0, + colSpan:7 + }, + { + id: "lndwtr", + title: "조경용수", + row:1, + }, + { + id: "cleanToilUswtr", + title: "청소화장실용수", + row:1, + }, + { + id:"resn", + title:"세척살수용수", + row:1, + }, + { + id: "farmUswtr", + title: "농업용수", + row:1, + }, + { + id: "indstUswtr", + title: "공업용수", + row:1, + }, + { + id: "hydUswtr", + title: "친수용수", + row:1, + }, + { + id: "ithlEtcUswtr", + title: "기타", + row:1, + }, + { + id: "anlOperCst", + title: "연간운영비(백만원/년)", + row:0, + rowSpan:2 + }, +]; + +export const rainStatDetailHeadListExport: TableUpperProps[] = [ + { + id: "ctpvCd", + title: "시도", + row:0, + }, + { + id: "sggCd", + title: "시군구", + row:0, + }, + { + id: "areaNm", + title: "행정구역명", + row:0, + }, + { + id: "", + title: "시설명", + row:0, + }, + { + id: "이용용도", + title: "이용용도", + row:0, + colSpan:7 + }, + { + id: "조경용수", + title: "조경용수", + row:1, + }, + { + id: "청소화장실용수", + title: "청소화장실용수", + row:1, + }, + { + id:"세척살수용수", + title:"세척살수용수", + row:1, + }, + { + id: "농업용수", + title: "농업용수", + row:1, + }, + { + id: "공업용수", + title: "공업용수", + row:1, + }, + { + id: "친수용수", + title: "친수용수", + row:1, + }, + { + id: "기타", + title: "기타", + row:1, + }, + { + id: "연간운영비", + title: "연간운영비(백만원/년)", + row:0, + rowSpan:2 + }, +]; \ No newline at end of file diff --git a/apps/wr/app/store/stats/rainStat/index.tsx b/apps/wr/app/store/stats/rainStat/index.tsx new file mode 100644 index 000000000..d6e48a53e --- /dev/null +++ b/apps/wr/app/store/stats/rainStat/index.tsx @@ -0,0 +1,184 @@ +import { fetchType, getFetch } from "@/components/lib/api"; +import type { TableUpperProps } from "../../common/type/header"; +import { excelRainStat } from "./excel"; +import { rainStatHeadListExport } from "./headList"; + +export type RainStatProps = { + ctpvCd: string //시도 + sggCd: string //시군구 + areaNm: string //행정구역명 + bdstNm: string //시설명 + bdstUsgCd: string //건축물용도코드 + daddr: string //주소 + lawTrgtYn: string //법적의무대상여부 + instlCmptnDt: string //설치완료일자 + instlCst: number //설치비용 + pntfColKnd: string //접수면종류 + filtrTrtmfctYn: string //여과등처리시설유무 + archArea: number //건축연면적 + dtfcltCpct: number //처리용량(저류조용량) + wtrUsqty: number //연간사용량 + utztnUsgCd: string //이용용도코드 + anlOperCst: number //연간운영비 + annRnwtQty: number //물사용량 + jungsudo: number //중수도​이용량 + dachae: number //대체수​이용량 + lndwtr: number //조경용수 + cleanToilUswtr: number //청소화장실용수 + resn: number //세척살수용수 + farmUswtr: number //농업용수 + indstUswtr: number //공업용수 + hydUswtr: number //친수용수 + ithlEtcUswtr: number //기타 + tmp: number //개소 + guideStats: GuideStatProps[] +} + +export type GuideStatProps = { + utztnUsgCd: string + splyUswtrUtztnQnt : number, + ruseFcltUtztnQnt: number +} + +type ExcelType = { + [key: string]: string | number | undefined; +}; + +const excelDownList: ExcelType[] = []; + +const fetchRainStatList = async (year: string) => { + const res = await getFetch(fetchType.WR, `/reuseChk/rainy/stat/${year}`) + return res; +} + +type rainStatActions = { + searchRainStatList: (year: string) => void +} + +export type rainStatState = { + rainStat: { + rainStatHeadList: TableUpperProps[], + rainStatList: RainStatProps[], + excelList: ExcelType[] + rainDetailList : [][] + } +} + +export type rainStatType = rainStatState & rainStatActions; + +export const rainStatExport: rainStatState = { + rainStat: { + rainStatHeadList: rainStatHeadListExport, + rainStatList: [], + excelList: [], + rainDetailList : [] + } +} +export const rainStatActionsExport = ( + set: (fn: (state: rainStatState) => rainStatState) => void +) => { + return { + searchRainStatList: async (year: string) => { + const res = await fetchRainStatList(year); + const cityArr: any[] = []; + res.forEach((item: RainStatProps) => { + + let obj = cityArr.find((e) => { + return e.ctpvCd == item.ctpvCd + }) + + if (obj) { + obj.instlCst += item.instlCst + obj.dtfcltCpct += item.dtfcltCpct + obj.wtrUsqty += item.wtrUsqty + obj.anlOperCst += item.anlOperCst + obj.tmp += 1 + } else { + obj = {}; + obj.ctpvCd = item.ctpvCd + obj.instlCst = item.instlCst + obj.tmp = 1 + obj.dtfcltCpct = item.dtfcltCpct + obj.wtrUsqty = item.wtrUsqty + obj.anlOperCst = item.anlOperCst + + cityArr.push(obj) + } + + if (item.guideStats && item.guideStats.length > 0) { + item.guideStats.forEach((guide: GuideStatProps) => { + rainStatHeadListExport.forEach((headItem: TableUpperProps) => { + + if (headItem.title == guide.utztnUsgCd) { + if (!obj[headItem.id] || typeof obj[headItem.id] !== "number") obj[headItem.id] = 0; + if (typeof obj[headItem.id] === "number") { + + obj[headItem.id] += guide.ruseFcltUtztnQnt; + } + } + }); + }) + } + }) + + cityArr.forEach(item => { + excelDownList.push({ + ctpvCd: item.ctpvCd, + tmp: item.tmp, + instlCst: item.instlCst, //설치비 + dtfcltCpct: item.dtfcltCpct, //저류조용량 + bdstUsgCd: item.bdstUsgCd, //연간사용량 + lndwtr: item.lndwtr, //조경용수 + cleanToilUswtr: item.cleanToilUswtr, //청소화장실용수 + resn: item.resn, //세척살수용수 + farmUswtr: item.farmUswtr, //농업용수 + indstUswtr: item.indstUswtr, //공업용수 + hydUswtr: item.hydUswtr, //친수용수 + ithlEtcUswtr: item.ithlEtcUswtr, //기타 + anlOperCst: item.anlOperCst, //연간운영비 + }) + }) + + //상세 + const rainDetailList : RainStatProps[] = []; + res.forEach((item:RainStatProps) => { + const utztnUsgArray = new Array<string>(); + item.guideStats.forEach((guideStat) => { + utztnUsgArray.push(guideStat.utztnUsgCd); + }) + rainDetailList.push({ + ...item, + utztnUsgCd : utztnUsgArray.join(', '), + }) + }) + const excelDetailData = excelRainStat(rainDetailList); + /* const rainStatHeadList = data.map((item:RainStatProps) => ({ + ...item, + ctpvCd:item.ctpvCd, + sggCd:item.sggCd, + areaNm:item.areaNm, + archArea:item.archArea, + dtfcltCpct:item.dtfcltCpct, + annRnwtQty:item.annRnwtQty, + jungsudo:item.jungsudo, + dachae:item.dachae, + way1:item.way1, + way2:item.way2, + way3:item.way3, + way4:item.way4, + way5:item.way5, + way6:item.way6, + way7:item.way7, + yearMoney:item.yearMoney, + })) */ + set((state: rainStatState) => ({ + rainStat: { + ...state.rainStat, + rainDetailList: excelDetailData as [][], + rainStatList: cityArr, + excelList: excelDownList + } + })); + } + } +} diff --git a/apps/wr/app/store/stats/rswtStat/excel.tsx b/apps/wr/app/store/stats/rswtStat/excel.tsx new file mode 100644 index 000000000..35f567253 --- /dev/null +++ b/apps/wr/app/store/stats/rswtStat/excel.tsx @@ -0,0 +1,46 @@ +import type { RswtStatProps } from "."; + +export const excelRswtStat = (excelList:RswtStatProps[]) : (string | number)[][] => { + const excelData: (string | number)[][] = []; + excelList.forEach((item) => { + excelData.push([ + //시도 + String(item.ctpvCd) ?? "", + //시군구 + String(item.sggCd) ?? "", + //행정구역명 + String(item.areaNm) ?? "", + //건축물명 + String(item.bdstNm) ?? "", + //건축물용도코드(빈칸) + //String(item.bdstUsgCd) ?? "", + "", + //주소 + String(item.daddr) ?? "", + //법적의무대상여부 + String(item.lawTrgtYn)==="Y" ? "대상" : "미대상", + //설치완료일자 + item.instlCmptnDt ? String(item.instlCmptnDt).substring(0, 10) : "", + //가동개시일(빈칸) + //String(item.instlCst) ?? "", + "", + //건축물면적 + item.archArea ?? "", + //처리용량 + item.filtrTrtmfctCpct ?? "", + //물사용량 + item.wtrUsqty ?? "", + //중수도+대체수이용량 + item.rswtAltSum ?? "", + //중수도이용량 + item.rswtrUsqty ?? "", + //대체수이용량 + item.altUswtrUtztnQnt ?? "", + //중수도용도 + String(item.utztnUsgCd) ?? "", + //연간운영비 + item.anlOperCst ?? "", + ]); + }); + return excelData; +} \ No newline at end of file diff --git a/apps/wr/app/store/stats/rswtStat/headList.tsx b/apps/wr/app/store/stats/rswtStat/headList.tsx new file mode 100644 index 000000000..877089092 --- /dev/null +++ b/apps/wr/app/store/stats/rswtStat/headList.tsx @@ -0,0 +1,101 @@ +import type { TableUpperProps } from "../../common/type/header"; + +export const rswtStatHeadListExport :TableUpperProps[]=[ + { + id: "ctpvCd", + title: "시도", + row:0, + rowSpan:2 + }, + { + id: "ctpvCnt", + title: "개소", + row:0, + rowSpan:2 + }, + { + id: "a", + title: "시설현황", + row:0, + colSpan:2 + }, + { + id: "archArea", + title: "건축연면적(m³)", + row:1, + }, + { + id: "filtrTrtmfctCpct", + title: "처리용량(m³)", + row:1, + }, + { + id: "wtrUsqty", + title: "물사용량(m³/년)", + row:0, + rowSpan:2 + }, + { + id: "rswtAltSum", + title: "중수도+대체수이용량(m³/년)", + row:0, + rowSpan:2 + }, + { + id: "rswtrUsqty", + title: "중수도이용량(m³/년)", + row:0, + rowSpan:2 + }, + { + id: "altUswtrUtztnQnt", + title: "대체수이용량(m³/년)", + row:0, + rowSpan:2 + }, + + { + id: "b", + title: "용도", + row:0, + colSpan:6 + }, + { + id: "lndwtr", + title: "조경용수", + row:1, + }, + { + id: "cleanToilUswtr", + title: "청소화장실용수", + row:1, + }, + { + id: "farmUswtr", + title: "농업용수", + row:1, + }, + { + id: "indstUswtr", + title: "공업용수", + row:1, + }, + { + id: "hydUswtr", + title: "친수용수", + row:1, + }, + { + id: "ithlEtcUswtr", + title: "기타", + row:1, + }, + { + id: "anlOperCst", + title: "연간운영비(백만원/년)", + row:0, + rowSpan:2 + }, + + +] \ No newline at end of file diff --git a/apps/wr/app/store/stats/rswtStat/index.tsx b/apps/wr/app/store/stats/rswtStat/index.tsx new file mode 100644 index 000000000..4e2a7ecc8 --- /dev/null +++ b/apps/wr/app/store/stats/rswtStat/index.tsx @@ -0,0 +1,168 @@ +import { fetchType, getFetch } from "@/components/lib/api"; +import type { TableUpperProps } from "../../common/type/header"; +import { rswtStatHeadListExport } from "./headList"; +import { excelRswtStat } from "./excel"; + +export type RswtStatProps = { + ctpvCd: string //시도 + sggCd : string //시군구 + areaNm: string //행정구역명 + bdstNm: string //시설명 + daddr: string //주소 + ctpvCnt: string //개소 + archArea: number //건축연면적 + filtrTrtmfctCpct: number //처리용량 + wtrUsqty: number //물사용량 + rswtAltSum: number //중수도+대체수이용량 + rswtrUsqty: number //중수도이용량 + altUswtrUtztnQnt: number //대체수이용량 + lndwtr: number //조경용수 + cleanToilUswtr: number //청소화장실용수 + resn: number //세척살수용수 + farmUswtr: number //농업용수 + indstUswtr: number //공업용수 + hydUswtr: number //친수용수 + ithlEtcUswtr: number //기타 + anlOperCst: number //연간운영비 + guideStats: GuideStatProps[] + utztnUsgCd : string //이용코드 + lawTrgtYn : string //법적의무대상여부 + instlCmptnDt : string //설치완료일자 +} + +export type GuideStatProps = { + utztnUsgCd: string + ruseFcltUtztnQnt: number +} + +type rswtStatActions = { + searchRswtStatList: (year: string) => void; +} + +export type ExcelType = { + [key: string]: string | number | undefined; +}; + +const excelDownList: ExcelType[] = []; + + +export type rswtStatState = { + rswtStat: { + rswtStatHeadList: TableUpperProps[]; + rswtStatList: RswtStatProps[]; + excelList: ExcelType[]; + rswtDetailList: RswtStatProps[][]; + } +} + +export type rswtStatType = rswtStatState & rswtStatActions; + +export const rswtStatExport: rswtStatState = { + rswtStat: { + rswtStatHeadList: rswtStatHeadListExport, + rswtStatList: [], + excelList: [], + rswtDetailList: [] + } +} + +export const rswtStatActionsExport: (set: any) => rswtStatActions = ( + set: any +) => { + return { + searchRswtStatList: async (year: string) => { + const res = await getFetch(fetchType.WR, `/reuseChk/reuseWater/stat/${year}`) + const rswtStatList: RswtStatProps[] = []; + + const cityArr: any[] = []; + res.forEach((item: RswtStatProps) => { + let obj = cityArr.find((e) => { + return e.ctpvCd == item.ctpvCd + }) + + if (obj) { + obj.ctpvCnt += 1 + obj.archArea += item.archArea + obj.filtrTrtmfctCpct += item.filtrTrtmfctCpct + obj.wtrUsqty += item.wtrUsqty + obj.rswtAltSum += (item.rswtrUsqty + item.altUswtrUtztnQnt) + obj.rswtrUsqty += item.rswtrUsqty + obj.altUswtrUtztnQnt += item.altUswtrUtztnQnt + obj.anlOperCst += item.anlOperCst + } else { + obj = {}; + obj.ctpvCd = item.ctpvCd + obj.ctpvCnt = 1 + obj.archArea = item.archArea + obj.filtrTrtmfctCpct = item.filtrTrtmfctCpct + obj.wtrUsqty = item.wtrUsqty + obj.rswtAltSum = item.rswtrUsqty + item.altUswtrUtztnQnt + obj.rswtrUsqty = item.rswtrUsqty + obj.altUswtrUtztnQnt = item.altUswtrUtztnQnt + obj.anlOperCst = item.anlOperCst + + cityArr.push(obj) + } + + if (item.guideStats && item.guideStats.length > 0) { + item.guideStats.forEach((guide: GuideStatProps) => { + rswtStatHeadListExport.forEach((headItem: TableUpperProps) => { + + if (headItem.title == guide.utztnUsgCd) { + if (!obj[headItem.id] || typeof obj[headItem.id] !== "number") obj[headItem.id] = 0; + if (typeof obj[headItem.id] === "number") { + + obj[headItem.id] += guide.ruseFcltUtztnQnt; + } + } + }); + }) + } + }); + + cityArr.forEach(item => { + excelDownList.push({ + ctpvCd: item.ctpvCd, + ctpvCnt: item.ctpvCnt, + archArea: item.archArea, + filtrTrtmfctCpct: item.filtrTrtmfctCpct, + wtrUsqty: item.wtrUsqty, + rswtAltSum: item.rswtAltSum, + rswtrUsqty: item.rswtrUsqty, + altUswtrUtztnQnt: item.altUswtrUtztnQnt, + lndwtr: item.lndwtr, + cleanToilUswtr: item.cleanToilUswtr, + farmUswtr: item.farmUswtr, + indstUswtr: item.indstUswtr, + hydUswtr: item.hydUswtr, + ithlEtcUswtr: item.ithlEtcUswtr, + anlOperCst: item.anlOperCst, + }) + }) + + //상세 + const rswtDetailList : RswtStatProps[] = []; + res.forEach((item:RswtStatProps) => { + const utztnUsgArray = new Array<string>(); + item.guideStats.forEach((guideStat) => { + utztnUsgArray.push(guideStat.utztnUsgCd); + }) + rswtDetailList.push({ + ...item, + rswtAltSum : item.rswtrUsqty + item.altUswtrUtztnQnt, + utztnUsgCd : utztnUsgArray.join(', '), + }) + }); + + const excelDetailData = excelRswtStat(rswtDetailList); + set((state: rswtStatState) => ({ + rswtStat: { + ...state.rswtStat, + rswtStatList: cityArr, + excelList: excelDownList, + rswtDetailList: excelDetailData + } + })); + } + } +} \ No newline at end of file diff --git a/apps/wr/app/store/store.tsx b/apps/wr/app/store/store.tsx index 68ca9db7e..4557662f8 100644 --- a/apps/wr/app/store/store.tsx +++ b/apps/wr/app/store/store.tsx @@ -1,25 +1,225 @@ -import { createStore } from 'zustand/vanilla'; -import { pubFacActionsExport, pubFacStateExport, type pubFacState } from './publicReuseFaciltyInsert/index'; -import { rainFacActionsExport, rainFacStateExport, type rainFacState, type rainFacType } from './rainReuseFacilityInsert'; -import { rswtFacActionsExport, rswtFacStateExport, type rswtFacState, type rswtFacType } from './rswtrReuseFaciltyInsert'; -import { matDmdActionsExport, matDmdStateExport, type matDmdState, type matDmdType } from './matDmd'; +import { createStore } from "zustand/vanilla"; +import { + codeActionExport, + comCodeStateExport, + getCityCd, + getEnvPartCd, + getMiddleRegionCd, + getRegionCd, + getSggCd, + getUnitWaterShedCd, + type comCodeState, + type comCodeType, +} from "./common/code"; +import { + matDmdActionsExport, + matDmdStateExport, + type matDmdState, + type matDmdType, +} from "./matDmd"; +import { + rainChkgdnActionsExport, + rainChkgdnStateExport, + type rainChkgdnState, + type rainChkgdnType, +} from "./prfmncReg/chkgdnReg/rainChkgdn"; +import { + rswtChkgdnActionsExport, + rswtChkgdnStateExport, + type rswtChkgdnState, + type rswtChkgdnType, +} from "./prfmncReg/chkgdnReg/rswrtChkgdn"; +import { + bdstActionsExport, + bdstStateExport, + type bdstFulType, + type bdstState, +} from "./ruseFcltMng/fcltReg/bdstReg"; +import { + htdflFacActionsExport, + htdflFacStateExport, + type htdflFacState, + type htdflFacType, +} from "./ruseFcltMng/fcltReg/htdflReg"; +import { + pubFacActionsExport, + pubFacStateExport, + type pubFacState, + type pubFacType, +} from "./ruseFcltMng/fcltReg/pbnstReg"; +import { + rainFacActionsExport, + rainFacStateExport, + type rainFacState, + type rainFacType, +} from "./ruseFcltMng/fcltReg/rainReg"; +import { + rswtFacActionsExport, + rswtFacStateExport, + type rswtFacState, + type rswtFacType, +} from "./ruseFcltMng/fcltReg/rswtReg"; +import { + rainSttnActionsExport, + rainSttnStateExport, + type rainSttnState, + type rainSttnType, +} from "./ruseFcltMng/fcltSttn/rainSttn"; +import { + rswtSttnActionsExport, + rswtSttnStateExport, + type rswtSttnState, + type rswtSttnType, +} from "./ruseFcltMng/fcltSttn/rswtSttn"; +import { + htdflSttnActionsExport, + htdflSttnStateExport, + type htdflSttnState, + type htdflSttnType, +} from "./ruseFcltMng/fcltSttn/htdflSttn"; +import { + rainChkSttnActionsExport, + rainChkSttnStateExport, + type rainChkSttnState, + type rainChkSttnType, +} from "./prfmncSttn/chkgdnSttn/rainChkgdnSttn"; +import { rswtChkStateExport, rswtChkSttnActionsExport, type rswtChkSttnState, type rswtChkSttnType } from "./prfmncSttn/chkgdnSttn/rswtChkgdnSttn"; +import { pbnstSttnActionsExport, pbnstSttnStateExport, type pbnstSttnState, type pbnstSttnType } from "./ruseFcltMng/fcltSttn/pbnstSttn"; +import { coSttnActionsExport, coSttnStateExport, type coSttnState, type coSttnType } from "./matDmd/coSttn"; // 옮겨야함 +import { searchActions, type comFcltActions, type comFcltState } from "./common/searchCode"; +import { 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"; +import { pbnstPrfmncSttnActionsExport, pbnstPrfmncSttnExport, type pbnstPrfmncSttnState, type pbnstPrfmncSttnType } from "./prfmncSttn/pbnstPrfmncSttn"; +import { drgtActionsExport, drgtStateExport, type drgtState, type drgtType } from "./matDmd/drgtSttn"; +import { ruseCaseActionsExport, ruseCaseStateExport, type ruseCaseType, type ruseCaseState } from "./matDmd/ruseCaseMng"; +import { rainStatActionsExport, rainStatExport, type rainStatState, type rainStatType } from "./stats/rainStat"; +import { type rswtStatType, type rswtStatState, rswtStatExport, rswtStatActionsExport } from "./stats/rswtStat"; +import { matStatActionsExport, matStatExport, type matStatState, type matStatType } from "./stats/matStat"; +import { ldrmntActionsExport, ldrmntStateExport, type ldrmntState, type ldrmntType } from "./matDmd/ldrmntCoSttn"; +export type WrStore = comCodeType & + pubFacType & + rainFacType & + rswtFacType & + matDmdType & + htdflFacType & + bdstFulType & + rainChkgdnType & + rswtChkgdnType & + rainSttnType & + rswtSttnType & + htdflSttnType & + rainChkSttnType& + rswtChkSttnType& + pbnstSttnType& + comFcltActions&comFcltState& + coSttnType& // 옮겨야함 + ldrmntType& // 옮겨야함 + pbnstPrfmncType& + coRvwType& + ruseHstryType& + pbnstPrfmncSttnType& + drgtType& + ruseCaseType& + rainStatType& + rswtStatType& + matStatType + ; -export type WrStore = pubFacState & rainFacType &rswtFacType &matDmdType; -export const StoreZus = (initState: rainFacState& pubFacState & rswtFacState &matDmdState= { - ...pubFacStateExport, - ...rainFacStateExport, - ...rswtFacStateExport , - ...matDmdStateExport -}) => { +export const StoreZus = ( + initState: comCodeState & + rainFacState & + pubFacState & + rswtFacState & + matDmdState & + htdflFacState & + bdstState & + rainChkgdnState & + rswtChkgdnState & + rainSttnState & + rswtSttnState & + htdflSttnState & + rainChkSttnState& + rswtChkSttnState & + pbnstSttnState& + coSttnState& // 옮겨야함 + ldrmntState& // 옮겨야함 + pbnstPrfmncState& + coRvwState& + ruseHstryState& + pbnstPrfmncSttnState& + drgtState& + ruseCaseState& + rainStatState& + rswtStatState& + matStatState + = { + ...comCodeStateExport, + ...pubFacStateExport, + ...rainFacStateExport, + ...rswtFacStateExport, + ...matDmdStateExport, + ...htdflFacStateExport, + ...bdstStateExport, + ...rainChkgdnStateExport, + ...rswtChkgdnStateExport, + ...rainSttnStateExport, + ...rswtSttnStateExport, + ...htdflSttnStateExport, + ...rainChkSttnStateExport, + ...rswtChkStateExport, + ...pbnstSttnStateExport, + ...coSttnStateExport, // 옮겨야함 + ...ldrmntStateExport, // 옮겨야함 + ...pbnstPrfmncStateExport, + ...coRvwStateExport, + ...ruseHstryStateExport, + ...pbnstPrfmncSttnExport, + ...drgtStateExport, + ...ruseCaseStateExport, + ...rainStatExport, + ...rswtStatExport, + ...matStatExport + } +) => { return createStore<WrStore>()((set) => { + getSggCd(set); + getCityCd(set); + getRegionCd(set); + getMiddleRegionCd(set); + getUnitWaterShedCd(set); + getEnvPartCd(set); return { ...initState, + ...searchActions(set), ...pubFacActionsExport(set), ...rainFacActionsExport(set), ...rswtFacActionsExport(set), - ...matDmdActionsExport(set) - } - }) -} -//store 등록하는 부분 \ No newline at end of file + ...matDmdActionsExport(set), + ...htdflFacActionsExport(set), + ...codeActionExport(set), + ...bdstActionsExport(set), + ...rainChkgdnActionsExport(set), + ...rswtChkgdnActionsExport(set), + ...rainSttnActionsExport(set), + ...rswtSttnActionsExport(set), + ...htdflSttnActionsExport(set), + ...rainChkSttnActionsExport(set), + ...rswtChkSttnActionsExport(set), + ...pbnstSttnActionsExport(set), + ...coSttnActionsExport(set), // 옮겨야함 + ...ldrmntActionsExport(set), // 옮겨야함 + ...pbnstPrfmncActionsExport(set), + ...coRvwActionsExport(set), + ...ruseHstryActionsExport(set), + ...pbnstPrfmncSttnActionsExport(set), + ...drgtActionsExport(set), + ...ruseCaseActionsExport(set), + ...rainStatActionsExport(set), + ...rswtStatActionsExport(set), + ...matStatActionsExport(set) + }; + }); +}; +//store 등록하는 부분 diff --git a/apps/wr/app/style/PublicReuseFaciltyInsert.module.css b/apps/wr/app/style/PublicReuseFaciltyInsert.module.css deleted file mode 100644 index 6fc6f75ee..000000000 --- a/apps/wr/app/style/PublicReuseFaciltyInsert.module.css +++ /dev/null @@ -1,9 +0,0 @@ -.formInsideTitle{ - background-color: gray; - text-align: center; -} - - - -.smallTitle{ -} \ No newline at end of file diff --git a/apps/wr/components/lib/api/index.ts b/apps/wr/components/lib/api/index.ts new file mode 100644 index 000000000..452788eb0 --- /dev/null +++ b/apps/wr/components/lib/api/index.ts @@ -0,0 +1,144 @@ +//8880 + +import { CS_URL, DEV_URL_FO, DEV_URL_WR, LOCAL_URL } from "../config/"; + +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 + : type === fetchType.LOCAL + ? LOCAL_URL + : type === fetchType.CS + ? '' + : DEV_URL_WR; + + const response = await fetch(div + url, { + method: "GET", + headers: { + accept: "application/json", + }, + }); + + 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 getFetchWithCount = 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", + }, + }); + + 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; + } 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}`); + } + return await response.json(); +}; + +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, type?: string) => { + const div = + type === fetchType.COMMON + ? DEV_URL_FO + : type === fetchType.LOCAL + ? LOCAL_URL + : type === fetchType.CS + ? '' + : DEV_URL_WR; + + 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/class/comClass.tsx b/apps/wr/components/lib/class/comClass.tsx new file mode 100644 index 000000000..654b006d7 --- /dev/null +++ b/apps/wr/components/lib/class/comClass.tsx @@ -0,0 +1,4 @@ +export const formTableName = 'text-xl my-4 font-bold' +export const commonButonSize = "sm" +export const formH1Class = "bg-[#eaf3fb] text-xl text-center border-2 p-2 mt-4 font-medium text-gray-500" +export const formH2Class = "text-xl my-4 underline font-bold" diff --git a/apps/wr/components/lib/config/index.ts b/apps/wr/components/lib/config/index.ts new file mode 100644 index 000000000..f85f7bad9 --- /dev/null +++ b/apps/wr/components/lib/config/index.ts @@ -0,0 +1,17 @@ + +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://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/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx new file mode 100644 index 000000000..dbb3aa7a6 --- /dev/null +++ b/apps/wr/components/lib/func/TableData.tsx @@ -0,0 +1,249 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { Checkbox, Input, TableCell, TableRow } from "@common/components/ui"; + +export const rainChkTableData = (chkData: rainChkZodType[]) => { + // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 + const maketotal = chkData.map((item: rainChkZodType) => { + const selectTotal = item.guideDetails.reduce((acc: any, cur: any) => { + const code = cur.utztnUsgCd; + const fk = cur.chkgdnFk; + + const key = `${fk}_${code}`; + acc[key] = acc[key] || { + totalSplyUswtrUtztnQnt: 0, + totalRuseFcltUtztnQnt: 0, + }; + + acc[key].totalSplyUswtrUtztnQnt += cur.splyUswtrUtztnQnt; + acc[key].totalRuseFcltUtztnQnt += cur.ruseFcltUtztnQnt; + + return acc; + }, {}); + return selectTotal; // 모든 키: `${chkgdnFk}_${utztnUsgCd}` 형태 + }); + + // 2. 누계 정보들을 하나의 객체로 합치기 (평탄화) + const flatTotalMap = Object.assign({}, ...maketotal); + + // 3. rainyDetails에 누계 정보 주입 + + const updatedRainyDetails: any[] = []; + chkData.forEach((rd: rainChkZodType) => { + rd.rainyDetails.forEach((rainyItem: any) => { + const key = `${rainyItem.chkgdnFk}_${rainyItem.utztnUsgCd}`; + const totals = flatTotalMap[key] || { + totalSplyUswtrUtztnQnt: 0, + totalRuseFcltUtztnQnt: 0, + }; + + updatedRainyDetails.push({ + ...rd, + ...rd.building, + ...rainyItem, + ...totals, + rowspan: rd.rainyDetails?.length, + }); + }); + }); + + return updatedRainyDetails; +}; + +export const rswtChkTableData = (chkData: rswtChkZodType[]) => { + // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 + const makeRswtData: any[] = []; + chkData.forEach((root) => { + root.rswtrDetails.forEach((detail) => { + let flatRswtChk = {}; + const findGuide = root.guideDetails.find( + (guide) => guide.utztnUsgCd === detail.utztnUsgCd + ); + const findPoluttion = root.rswtrPollutions.find( + (polution) => polution.utztnUsgCd === detail.utztnUsgCd + ); + const operMngCd = root?.reuseBuilding.operMngCd; + flatRswtChk = { + ...root, + ...detail, + ...findGuide, + ...findPoluttion, + ...flatRswtChk, + operMngCd: operMngCd, + }; + makeRswtData.push(flatRswtChk); + }); + }); + return makeRswtData; +}; + +export const chRowspanTable = ( + makeTableData: any[], + headList: TableUpperProps[], + handleChange: ( + rowIndex: number, + field: keyof (typeof makeTableData)[0], + value: string | number + ) => void, + toggleSelection: (pk: number, checked: boolean) => void, + selectedIds: number[], + handleCellClick: (index: number, headId: string) => void +) => { + let order = 0 + // rowspan된 셀 렌더링 여부 추적용 + const renderedMap = new Map<string, Set<number>>(); + return ( + <> + {makeTableData.map((row, rowIndex) => ( + <TableRow key={rowIndex}> + {headList + .map((head) => { + + const key = head.id; + const isMerged = head.mergeCell; + const value = row[key]; + // 병합 대상이면 중복 렌더링 여부 판단 + if (isMerged) { + const renderedSet = renderedMap.get(key) ?? new Set<number>(); + if (renderedSet.has(rowIndex)) { + + return null; // 이미 렌더링된 셀은 skip + } + + const rowspan = row.rowspan ?? 1; + for (let i = 0; i < rowspan; i++) { + renderedSet.add(rowIndex + i); + } + renderedMap.set(key, renderedSet); + return ( + <TableCell + key={key} + rowSpan={rowspan} + className="align-middle text-center whitespace-nowrap" + > + {key === "checkBox" ? ( + <Checkbox + checked={selectedIds.includes(row.chkgdnPk)} + onCheckedChange={(checked) => + toggleSelection(row.chkgdnPk, checked === true) + } + ></Checkbox> + ) : key === "order" ? ( + ++order + ) : ( + value + )} + </TableCell> + ); + } + + // 기본 셀 + return ( + <TableCell + key={key} + className="text-center whitespace-nowrap" + onClick={() => handleCellClick(rowIndex, head.id)} + > + {head.edit === true ? ( + <Input + className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`} + type={head.type} + onChange={(e) => { + handleChange(rowIndex, head.id, e.target.value); + }} + value={value ?? ""} + disabled={!selectedIds.includes(row.chkgdnPk)} + /> + ) : ( + (head.id === "totalSplyUswtrUtztnQnt" || head.id === "totalRuseFcltUtztnQnt" || head.id === "splyUswtrUtztnQnt" || head.id === "ruseFcltUtztnQnt") ? ( + <span className="underline text-blue-600 cursor-pointer"> + {value} + </span> + ) : ( + <>{value}</> + ) + )} + </TableCell> + ); + + })} + </TableRow> + ))} + </> + ); +}; + + + +export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean) => { + return headList.forEach((head) => { + if (head.edit !== undefined) + head.edit = isEdit; + if (head.id === 'checkBox') { + head.visible = false + } + + }); +} + + +const handleChange = ( + rowIndex: number, + field: any, + value: string | number, + setInputData: (data:any) => void +) => { + if(Number(value) < 0 ||! Number(value)){ + value = 0; + }else{ + value = Number(value); + } + setInputData((prevData:any) => + prevData?.map((row:any, index: number) => { + if (index === rowIndex) { + return { + ...row, + [field ]: value, + }; + } + return row; + }) + ); +}; +; + + + +export const InputTableBody = (chgRes: any[] | [] ,headList:TableUpperProps[], setInputData: (data: any[]) => void) => { + return ( + <> + {chgRes.map((data, dataIndex) => ( + <TableRow key={dataIndex + "row"}> + {headList.map((head, headIndex) => ( + <TableCell key={head.id + headIndex} className="w-36" > + {head.edit ===true && data.edit !==false? + <Input + className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`} + onChange={(e) => + handleChange(dataIndex, head.id, e.target.value,setInputData) + } + value={data[head.id as keyof any] ?? ""} + /> : ( + <div + className={` + ${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} + !text-xl w-full h-10 + `} + > + {data[head.id as keyof any] ?? ""} + </div> + ) + } + </TableCell> + ))} + </TableRow> + ))} + </> + ); +} \ No newline at end of file diff --git a/apps/wr/components/lib/func/calculateConstCost .tsx b/apps/wr/components/lib/func/calculateConstCost .tsx new file mode 100644 index 000000000..5e9387385 --- /dev/null +++ b/apps/wr/components/lib/func/calculateConstCost .tsx @@ -0,0 +1,101 @@ +export const findBounds = ( + data: any[], + fctCd: string, + dataCode: string, + qtyCode:string, + qty: number +): { lower: any; upper: any } => { + + + const filtered = data.filter((item) => item[dataCode] === fctCd); + + const lowers = filtered.filter((item) => Number(item[qtyCode]) <= qty); + const uppers = filtered.filter((item) => Number(item[qtyCode]) > qty); + + const lower = + lowers.length > 0 + ? lowers.reduce((acc, curr) => + Number(curr[qtyCode]) > Number(acc[qtyCode]) ? curr : acc + ) + : undefined; + + const upper = + uppers.length > 0 + ? uppers.reduce((acc, curr) => + Number(curr[qtyCode]) < Number(acc[qtyCode]) ? curr : acc + ) + : undefined; + + // ✅ 바운더리 하나만 있을 경우 대체 처리 + if (!lower && upper) return { lower: upper, upper }; + if (!upper && lower) return { lower, upper: lower }; + + // ✅ 정상 보간 구간 + if (lower && upper) return { lower, upper }; + + // ❌ 둘 다 없을 경우 (fctCd 필터 결과가 없음) + throw new Error(`No matching bounds found for fctCd: ${fctCd}, qty: ${qty}`); +}; + + +// (기울기) a = (y2 - y1) / (x2 - x1) +// (절편) b = y1 - a * x1 +// ↓ +// 결과값 y = a * x + b + + +export const getInterpolation = ( + Upper: { stdCost: number; fctCpc: number }, + Lower: { stdCost: number; fctCpc: number }, + qtyYear: number +): number => { + const x1 = Number(Lower.fctCpc); // 최소 처리 용량 + const y1 = Number(Lower.stdCost); // 최소 공사비/요율 + const x2 = Number(Upper.fctCpc); // 최대 처리 용량 + const y2 = Number(Upper.stdCost); // 최대 공사비/요율 + + if (qtyYear <= x1) return y1; // 최소 이하면 y1 반환 + if (qtyYear >= x2) return y2; // 최대 이상이면 y2 반환 + + const slope = (y2 - y1) / (x2 - x1); // 기울기 a + const intercept = y1 - slope * x1; // 절편 b + + return slope * qtyYear + intercept; // 보간 계산 +}; + +export const transformAndInterpolate = ( + rawData: any, + dmndQtyYear: number +): any => { + const result: any = {}; + + for (const key in rawData) { + const item = rawData[key]; + + const range = { + Lower: { + fctCpc: item.lower?.stdCost, + stdCost: item.lower?.rt, + }, + Upper: { + fctCpc: item.upper?.stdCost, + stdCost: item.upper?.rt, + }, + }; + result[key] = getInterpolation(range.Upper, range.Lower, dmndQtyYear); + } + + return result; +}; + +export const getConstFctCode = (fctCd: string) => { + const fctMap: { [key: string]: string } = { + 총인처리시설: "001", + 염소소독: "002", + UV소독: "003", + 전처리시설: "004", + RO: "005", + }; + + return fctMap[fctCd]; +}; diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx new file mode 100644 index 000000000..e5dfc8c0c --- /dev/null +++ b/apps/wr/components/lib/func/commom.tsx @@ -0,0 +1,161 @@ +import type { comCodeState } from "@/app/store/common/code"; +import { + filePickedBlob, + type SelectDataType, + type ReturnType, + filePickedBlobArray, + type SheetParameter, +} from "@common/business_components/ui"; +import type { RefObject } from "react"; + +export const getCity = (val: string, sggCd: SelectDataType[]) => { + const data = sggCd + .filter((item) => String(item.val).substring(0, 2) === val) + .map((item) => ({ + val: item.val, + text: item.text, // 한국어 텍스트 + })); + data.unshift({ val: "0", text: "전체" }); + return data; +}; + +// cd text 넣기 +export const tableData = (data: any, code: comCodeState) => { + const chCode = code.code; + + return data?.map( + (item: { + sggCd: string; + ctpvCd: string; + reuseBuilding: { lawTrgtYn: any; bdstUsgCd: any }[]; + }) => { + let returnData = {}; + const sggText = chCode.sggCd.find( + (cd) => cd.val === String(item.sggCd).trim() + )?.text; + const cityText = chCode.cityCd.find( + (cd) => cd.val === String(item.ctpvCd).trim() + )?.text; + if (item?.reuseBuilding?.length > 0) { + const bdsgUsgText = chCode.bdstUsgCd.find( + (cd) => Number(cd.val) === item?.reuseBuilding[0]?.bdstUsgCd + )?.text; + const lawTrgtYn = item?.reuseBuilding[0]?.lawTrgtYn; + returnData = { + ...item, + sggText: sggText ?? "없음", + cityText: cityText ?? "없음", + bdsgUsgText: bdsgUsgText, + lawTrgtYn: lawTrgtYn, + }; + } else { + returnData = { + ...item, + sggText: sggText ?? "없음", + cityText: cityText ?? "없음", + }; + } + return returnData; + } + ); +}; + +export const excelDownload = ( + excelList: [][], + fileNm: string, + sampleFile: string, + headerRange = "A1:V3", + writeLine = 4 +) => { + if (excelList.length === 0) return; + fetch(`/${sampleFile}`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlob(blobRet, excelList, fileNm, writeLine, headerRange); + }); +}; + +export const excelArrayDownload = ( + fileNm: string, + sampleFile: string, + sheetArray:SheetParameter [] +) => { + if (!sheetArray) return; + fetch(`/${sampleFile}`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlobArray(blobRet, fileNm, sheetArray); + }); +}; + +// onchange 텍스트를 숫자로 and ,제거 +export const handleCommaNumberChange = ( + e: React.ChangeEvent<HTMLInputElement>, + onChange: (value: number | "") => void +) => { + const raw = e.target.value.replace(/,/g, ""); + if (!/^\d*$/.test(raw)) return; + onChange(raw === "" ? "" : Number(raw)); +}; + +// 숫자입력시 , 삽입 +export const formatCommaNumber = ( + value: number | string | undefined | null +): string => { + if (value === undefined || value === null || value === "") return ""; + const num = Number(value); + // 숫자로 변환할 수 없는 값이 들어올 경우 빈 문자열을 반환하여 예상치 못한 "NaN" 출력을 방지합니다. + if (isNaN(num)) { + return ""; + } + return num.toLocaleString(); +}; + +export const getCityOrSgg = (ref: RefObject<ReturnType>) => { + let con; + if (ref.current != null) + if (ref.current.sigunValue != "0") { + con = ref.current.sigunValue; + } else { + con = ref.current.sidoValue; + } + return con; +}; + +export const safeDate = (value: any): Date | undefined => { + const date = new Date(value); + return isNaN(date.getTime()) ? undefined : date; +}; + +const currentYear = new Date().getFullYear(); +export const yearOptions: { val: string; text: string }[] = Array.from( + { length: currentYear - 1969 }, + (_, i) => ({ + val: String(1970 + i), + text: `${1970 + i}년`, + }) +); + +export const getdmdMatFlag = (flag: string|undefined) => { + let flagMat = ""; + + if(flag === undefined || flag === null) { + flagMat = "신청"; + }else if(flag.trim() === "Sub") { + flagMat = "검토"; + + }else if(flag.trim() === "matched") { + flagMat = "매칭완료"; + }else { + flagMat = "신청"; + } + return flagMat; +}; diff --git a/apps/wr/components/lib/useTus_wr/useTusWr.tsx b/apps/wr/components/lib/useTus_wr/useTusWr.tsx new file mode 100644 index 000000000..b9441e6a4 --- /dev/null +++ b/apps/wr/components/lib/useTus_wr/useTusWr.tsx @@ -0,0 +1,78 @@ +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: 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); + }, + }); + + options?.handleUploadStart?.(uploader); + uploader.start(); + } catch (e) { + console.log(e); + reject(e as DetailedError); + } + // setUpload(uploader); + }); +// +// diff --git a/apps/wr/components/toastFromUi.tsx b/apps/wr/components/toastFromUi.tsx deleted file mode 100644 index 5270ca5ec..000000000 --- a/apps/wr/components/toastFromUi.tsx +++ /dev/null @@ -1,56 +0,0 @@ - -import { Button } from "@common/components/ui"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import toast from "react-hot-toast"; - - - -export function ToastFormUi(body:string,title:string,url:string,buttonName:string){ - - //const router = useRouter(); - - return( - - toast.custom((t) => ( - - <div - className={`${ - t.visible ? 'animate-enter' : 'animate-leave' - } w-4/12 h-40 bg-white shadow-lg rounded-lg pointer-events-auto flex ring-1 ring-black ring-opacity-5`} - > - <div className="w-full flex-1 py-10"> - <div className="flex items-start"> - <div className="flex-shrink-0 pt-0.5"> - - </div> - <div className="ml-3 flex-1"> - <p className="text-xl font-medium underline"> - {title} - </p> - <p className="mt-1 text-2xl"> - {body} - </p> - </div> - </div> - </div> - <div className="flex py-20"> - <Button - size='sm' - onClick={() => toast.dismiss(t.id)} - > - <Link href={url}> {buttonName}</Link> - </Button> - </div> - <div className="flex pl-4 pr-4 py-20"> - <Button - onClick={() => toast.dismiss(t.id)} - size='sm' - > - 닫기 - </Button> - </div> - </div> - )) - ) -} \ No newline at end of file diff --git a/apps/wr/components/ui/IteratreForm.tsx b/apps/wr/components/ui/IteratreForm.tsx deleted file mode 100644 index 9d35d865e..000000000 --- a/apps/wr/components/ui/IteratreForm.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { FileUpload, FormControl, FormField, FormItem, FormLabel, FormMessage, SelectBox } from "@common/business_components"; -import { DatePicker } from "@common/components"; -import { Input } from "@common/components"; - - -export type typeOfForm ={ - labelName: string; - type: string; - placeholder:any; - formName: string; - selectValue:selectValue|undefined; -} -type onSelectValue = (val:string) => void; -export type selectValue ={ - label:string, - value:{ text: string; val: string }[], - fun:onSelectValue -} - - -export function IteraterFrom(formData:typeOfForm[],form:any , formClass:string ){ - - const formItem =(data:typeOfForm,field:any)=>{ - let tag - if(data.type==='select'){ - tag= (data.selectValue ? <SelectBox className='' label={data.selectValue.label} selectArray={data.selectValue.value} onSelectValue={data.selectValue.fun} >{...form}</SelectBox> : null) - }else if(data.type==='date'){ - tag =(<DatePicker mode="single" {...field} />) - }else{ - tag=(<Input className ='w-64'placeholder= {data.placeholder} type="" {...field} />) - } - return tag; - } - - return ( - <> - {formData.map((data: typeOfForm, index) => ( - <FormField key={`${index}+key`} - control={form.control} - name={data.formName} - render={({ field }) => ( - <FormItem className={formClass} > - <FormLabel>{data.labelName}</FormLabel> - <FormControl> - {formItem(data,field)} - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - ))} - </> - ); - }; \ No newline at end of file diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx new file mode 100644 index 000000000..b22f9474e --- /dev/null +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -0,0 +1,467 @@ +import { useWrStore } from "@/app/store"; +import { usageProcData } from "@/app/store/matDmd/coMatRvw/headList"; +import type { + DedMatUpdateType, + DmdCoReqType, +} from "@/app/store/matDmd/coMatRvw/type"; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { formH1Class } from "@/components/lib/class/comClass"; +import { + findBounds, + getConstFctCode, + getInterpolation, + transformAndInterpolate, +} from "@/components/lib/func/calculateConstCost "; +import { ConstCostFeeTable } from "@/components/ui/modal/coMat/constCostFeeTable"; +import { RateFeeTableModal } from "@/components/ui/modal/coMat/rateFeeModal"; +import { + Form, + FormField, + FormUi, + UiModal, + UiTable, +} from "@common/business_components/ui"; +import PopoverUi from "@common/business_components/ui/popover_ui"; +import { Button, Input, Label } from "@common/components/ui"; +import type { read } from "fs"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { type UseFormReturn } from "react-hook-form"; +import toast from "react-hot-toast"; + +export default function ConCostForm({ + form, + type, +}: { + form: UseFormReturn<DmdCoReqType>; + type: string; +}) { + const { + constCostRes, + rateFeeRes, + coMatRvwUpdate, + ruseRvwCalUpdate, + getRateFee, + getConstCostFee, + } = useWrStore((state) => state); + + const ref = useRef<boolean>(false); + + useEffect(() => { + if (!ref.current) { + const currentYear = new Date().getFullYear(); + getConstCostFee(String(currentYear)); + getRateFee(String(currentYear)); + ref.current = true; + } + }, [ref, getConstCostFee, getRateFee]); + + const zipDowonLoad = () => { + fetch(`/2024_constarctFeeProgram.zip`, {}).then(async (response) => { + const blobRet = await response.blob(); + // const blob = new Blob([blobRet], { type: "application/octet-stream" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blobRet); + link.download = + "24년_수도시설 운영비 및 개략공사비 산정프로그램.zip"; + link.click(); + }); + }; + const usgPrcsProcsWatch = form.watch("demandCst.usgPrcsProcs"); + const dmndQtyWatch = form.watch("dmndQty"); + const totalConstCost = form.watch("totalConstCost"); + const cstrnCst = form.watch("demandCst.cstrnCst"); + + const calculateConstCost = useCallback(() => { + const usgPrcsProcs = usgPrcsProcsWatch; + const dmndQtyYear = dmndQtyWatch * 365; + if (usgPrcsProcs && usgPrcsProcs?.length > 0) { + const firstProc = getConstFctCode(usgPrcsProcs?.split("+")[0] || "") || ""; + const secondProc = getConstFctCode(usgPrcsProcs?.split("+")[1] || "") || ""; + + const { lower: firstLower, upper: firstUpper } = findBounds( + constCostRes, + firstProc || "", + 'fctCd', + 'fctCpc', + Number(dmndQtyYear) + ); + const { lower: secondLower, upper: secondUpper } = findBounds( + constCostRes, + secondProc || "", + 'fctCd', + 'fctCpc', + Number(dmndQtyYear) + ); + + const firstConstFee = getInterpolation( + firstUpper, + firstLower, + dmndQtyYear + ); + const secondConstFee = getInterpolation( + secondUpper, + secondLower, + dmndQtyYear + ); + const result = Math.round(firstConstFee + secondConstFee) / 100; + + const groupCodes = ["BD", "CE", "SE", "WD"]; + const groupedBounds: { [key: string]: { lower?: any; upper?: any } } = {}; + for (const code of groupCodes) { + + groupedBounds[code] = findBounds(rateFeeRes, code, 'incExpCd', 'stdCost',result); + } + const resultRate = transformAndInterpolate(groupedBounds, result); + const fcltSbsdCstrnCst = (result* (resultRate.BD + resultRate.CE + resultRate.SE + resultRate.WD) / 100); + const realResult = result + fcltSbsdCstrnCst; + + form.setValue("demandCst.prcsFcltCpctCstrnCst", Math.round(result) ); + form.setValue("demandCst.fcltSbsdCstrnCst", Math.round(fcltSbsdCstrnCst)) ; + form.setValue("totalConstCost", Math.round(realResult)); + } + }, [constCostRes, rateFeeRes, usgPrcsProcsWatch, dmndQtyWatch]); + const onSubmit = (value: DmdCoReqType) => { + + if( !form.getValues('demandCst.prcsFcltCpctCstrnCst') ){ + toast.error('공사비 계산 진행 후 저장을 눌러주새요') + return + } + if (type === "mat") { + const data: DedMatUpdateType = { + dmdInfoSn: form.getValues("dmdInfoSn"), + usgPrcsProcs: form.getValues("demandCst.usgPrcsProcs"), + cstrnCst: form.getValues("demandCst.cstrnCst"), + fcltSbsdCstrnCst: form.getValues("demandCst.fcltSbsdCstrnCst"), + prcsFcltCpctCstrnCst: form.getValues("demandCst.prcsFcltCpctCstrnCst"), + rgtr: TEST_UUID, + }; + coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + } else if (type === "hist") { + + delete value.totalConstCost + delete value.ruseUtztnFcltNm + + + ruseRvwCalUpdate(value); + } + }; + + useEffect(() => { + console.log(" form ", form.formState.errors); + }, [form.formState]); + + return ( + <> + <hr className="mt-8" /> + <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 className="mx-3" size={"sm"} onClick={calculateConstCost}> + 계산 + </Button> + <Button className="mr-3" size={"sm"} onClick={form.handleSubmit(onSubmit)}> + 저장 + </Button> + <Label>관로 표준공사비 산정 프로그램</Label> + <Button + color="green" + size={"sm"} + onClick={zipDowonLoad} + className="bg-none ml-6" + > + 다운로드 + </Button> + </div> + <div className="col-span-12 w-full"> + <h1 className={formH1Class}>기본사항</h1> + </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="useUsgCd" + render={({ field }) => ( + <FormUi label="용도"> + <Input + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="dmndQty" + 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"> + <PopoverUi + childrenTriger={ + <> + <Label className="underline"> + 용도에 따른 처리공정 + <i className="diveicon di-search" /> + </Label> + </> + } + childrenBody={ + <> + <UiTable + className="" + headName={"용도에 따른 처리공정"} + tableData={usageProcData} + headlist={[ + { id: "usage", title: "용도" }, + { id: "process", title: "단위공정" }, + ]} + pageSize={0} + total={0} + cellClick={() => {}} + onPageChange={() => {}} + /> + </> + } + bodyClassName={"w-[25vw] max-w-none h-[54vh]"} + ></PopoverUi> + <FormField + control={form.control} + name="demandCst.usgPrcsProcs" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="text" + onChange={field.onChange} + value={form.getValues("demandCst.usgPrcsProcs") ?? ""} + className="mt-2" + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <FormField + control={form.control} + name="dmndQty" + render={({ field }) => ( + <FormUi label="처리시설 용량(요구수량 * 365일)"> + <Input + placeholder="" + type="number" + step="0.001" + onChange={(e) => { + //field.onChange(Number(e.target.value) * 365 ); + }} + value={Number(field.value) * 365 || 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-12 w-full"> + <h1 className={formH1Class}>공사비 입력사항</h1> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <PopoverUi + childrenTriger={ + <> + <Label className="underline"> + 처리시설용량에 따른 공사비(백만원) <i className="diveicon di-search" /> + </Label> + </> + } + childrenBody={ + <> + <ConstCostFeeTable + setReturnData={(e) => {}} + ></ConstCostFeeTable> + </> + } + bodyClassName={"w-[40vw] h-full"} + ></PopoverUi> + </div> + <FormField + control={form.control} + name="demandCst.prcsFcltCpctCstrnCst" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="number" + step="0.001" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <div className="flex items-center justify-between"> + <PopoverUi + childrenTriger={ + <> + <Label className="underline"> + 시설부대경비 <i className="diveicon di-search" /> + </Label> + </> + } + childrenBody={ + <> + <RateFeeTableModal + setReturnData={(e) => {}} + ></RateFeeTableModal> + </> + } + bodyClassName={"w-[22vw]"} + ></PopoverUi> + </div> + <FormField + control={form.control} + name="demandCst.fcltSbsdCstrnCst" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + type="number" + step="0.01" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 mt-4"> + <PopoverUi + childrenTriger={ + <> + <Label className="underline"> + 총사업비 (백만원) <i className="diveicon di-search" /> + </Label> + </> + } + childrenBody={ + <> + <div className="!w-full "> + <div className=" items-center font-bold mb-1 !text-3xl"> + 총사업비 계산방법 + </div> + <div className=" items-center !text-2xl"> + 하수처리수 재이용시설 총 사업비 = + 공사비+시설부대경비{" "} + </div> + </div> + </> + } + bodyClassName={"w-[22vw]"} + ></PopoverUi> + <FormField + control={form.control} + name="totalConstCost" + render={({ field }) => ( + <FormUi label=""> + <Input + placeholder="" + className="mt-2" + type="number" + step="0.01" + onChange={(e) => {}} + value={field.value ?? 0} + readOnly + /> + </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(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4 mt-4"> + <FormField + control={form.control} + name="demandCst.cstrnCst" + 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-4 mt-4 mb-3"> + <Label className="text-xl"> + 처리시설 + 공급관로 총사업비(백만원) + </Label> + <Input + placeholder="" + type="number" + step="0.001" + readOnly={true} + value={(totalConstCost ?? 0) + (cstrnCst ?? 0)} + /> + </div> + </div> + </Form> + </> + ); +} diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx new file mode 100644 index 000000000..5ab5edf98 --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -0,0 +1,340 @@ +import { useWrStore } from "@/app/store"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { formH1Class } from "@/components/lib/class/comClass"; +import { + formatCommaNumber, + getCity, + handleCommaNumberChange, + safeDate, +} from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + FormField, + FormUi, + DatePicker, + SelectBox, + type SelectDataType, +} from "@common/business_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"; + +export default function BdstReg({ form }: { form: any }) { + const { code } = useWrStore((state) => { + return state; + }); + + const { fields, append, remove } = useFieldArray({ + name: "chgBuilding", + control: form?.control, // Provide a fallback to avoid errors + }); + + const plusHist = () => { + append({ + bdstNm: "", + daddr: "", + bdstUsgCd: "", + }); + }; +// 잠깐 주석처리 + // const removeLastItem = () => { + // if (fields.length > 0) { + // remove(fields.length - 1); + // } + // }; + const removeSelected = () => { + const idxsToRemove = fields + .map((item, idx) => selectedIds.includes(item.id) ? idx : -1) + .filter(index => index > -1) + .sort((prevIndex, nextIndex) => nextIndex - prevIndex); + + remove(idxsToRemove); + setSelectedIds([]); + }; + + + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | undefined>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const getSelCity = (val: string) => { + const data = getCity(val, code.sggCd); + setSelectSgg(data); + const sgg = form.getValues("sggCd"); + + setObjSearch({ city: val, sgg: "" }); + }; + + const getSgg = (val: string | number) => { + const sgg = form.getValues("sggCd"); + setObjSearch({ ...objSearch, sgg: val != "" ? String(val) : sgg }); + }; + + useEffect(() => { + const city = form.getValues("ctpvCd"); + const sgg = form.getValues("sggCd"); + setObjSearch({ city: city, sgg: sgg }); + }, [selectSgg]); + + useEffect(() => { + const subscription = form.form?.watch((value: any) => { + console.log("vale", value); + }); + + return () => subscription?.unsubscribe(); + }, [form.form]); + + const [selectedIds, setSelectedIds] = useState<string[]>([]); + + const toggleSelect = (id: string) => { + setSelectedIds(prev => + prev.includes(id) + ? prev.filter(x => x !== id) + : [...prev, id] + ); + }; + + return ( + <> + <h1 className={formH1Class}> + 건축물 시설 정보 + </h1> + <div className="grid grid-cols-6 gap-2 mt-4"> + <div className="col-span-3"> + <FormField + control={form.control} + name="ctpvCd" + render={({ field }) => ( + <FormUi label="시도"> + <SelectBox + className="col-span-3" + label={""} + selectClass={selectClass} + selectArray={code.cityCd} + onSelectValue={(val) => { + getSelCity(String(val)); + field.onChange(Number(val)); + }} + value={field.value} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="sggCd" + render={({ field }) => ( + <FormUi label="시군구"> + <SelectBox + className="col-span-3" + label={""} + selectClass={`w-full min-h-[2.9rem] ${selectClass}`} + selectArray={selectSgg ? selectSgg : code.sggCd || []} + onSelectValue={(val) => { + getSgg(val); + field.onChange(Number(val)); + }} + value={field.value} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-6 gap-2 mt-4 "> + <div className="col-span-2"> + <FormField + control={form.control} + name="bdstNm" + render={({ field }) => ( + <FormUi label="건축물명"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + {...field} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="daddr" + render={({ field }) => ( + <FormUi label="위치"> + <Input + className={selectClass} + placeholder="1,000,000" + type="search" + {...field} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.bdstUsgCd" + render={({ field }) => ( + <FormUi label="건축물용도"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.bdstUsgCd} + onSelectValue={(val) => { + field.onChange(Number(val)); + }} + value={field.value} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="frstArchPrmsnDt" + render={({ field }) => ( + <FormUi label="최초건축허가일"> + <DatePicker + mode={"single"} + field={{ + value: field.value || new Date(), + onChange: (date: Date | undefined) => field.onChange(date), + }} + selectClass={'w-full'} + ></DatePicker> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="archArea" + render={({ field }) => ( + <FormUi label="건축면적(m³)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="number" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.lawTrgtYn" + render={({ field }) => ( + <FormUi label="법정의무대상여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={(val) => { + field.onChange(val); + }} + value={field.value} + /> + </FormUi> + )} + /> + </div> + </div> + <h1 className={formH1Class}> + 중축, 개축, 재축 + </h1> + + <div className="flex justify-end gap-2 mt-4"> + <Button type="button" className="" size={"sm"} onClick={plusHist}> + 이력추가 + </Button> + <Button + type="button" + className="!bg-red-500 text-white" + size={"sm"} + onClick={removeSelected} + > + 삭제 + </Button> + </div> + {fields.map((field, index) => ( + <div key={field.id} className="grid grid-cols-[auto,1fr,1fr,1fr] gap-2 mt-4 items-center"> + <div className="col-span-1 flex justify-center mr-4"> + <Checkbox + checked={selectedIds.includes(field.id)} + onCheckedChange={() => toggleSelect(field.id)} + /> + </div> + <div className=""> + <FormField + control={form.control} + name={`chgBuilding.${index}.bdstChgNm`} + render={({ field: f }) => ( + <FormUi label="구분"> + <SelectBox + selectClass={selectClass} + selectArray={code.chBuCd} + onSelectValue={val => f.onChange(val)} + value={f.value ?? code.chBuCd[0]?.val} className={""} label={""} /> + </FormUi> + )} + /> + </div> + <div className=""> + <FormField + control={form.control} + name={`chgBuilding.${index}.archChgPrmsnDt`} + render={({ field: f }) => ( + <FormUi label="허가일"> + <DatePicker + mode="single" + field={{ + value: safeDate(f.value) || new Date(), + onChange: (date) => f.onChange(date), + }} + selectClass="w-full" + /> + </FormUi> + )} + /> + </div> + <div className=""> + <FormField + control={form.control} + name={`chgBuilding.${index}.archChgArea`} + render={({ field: f }) => ( + <FormUi label="면적"> + <Input + placeholder="-" + type="text" + onChange={e => handleCommaNumberChange(e, f.onChange)} + value={formatCommaNumber(f.value)} + className={selectClass} + /> + </FormUi> + )} + /> + </div> + </div> + ))} + <div className="border-b border-gray-300 my-4"> </div> + </> + ); +} diff --git a/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx b/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx new file mode 100644 index 000000000..70e62eb7f --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx @@ -0,0 +1,634 @@ +"use client"; + +import { + Form, + FormField, + FormUi, + SelectBox, +} from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { useWrStore } from "@/app/store"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + hdtflZod, + type customHtdflBdst, +} from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import { useEffect } from "react"; +import BdstReg from "../bdstReg/bdstReg"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; +import { formH1Class } from "@/components/lib/class/comClass"; + +export default function HtdflRegForm({ + htdflForm, + isModal, + readonly = false +}: { + htdflForm: customHtdflBdst; + isModal: boolean; + readonly?: boolean; +}) { + const { code, htdflBdstUpdate, htdflBdstReg } = useWrStore((state) => state); + + const defaultValues: z.infer<typeof hdtflZod> = hdtflZod.parse({}); + + const form = useForm<z.infer<typeof hdtflZod>>({ + resolver: zodResolver(hdtflZod), + defaultValues: defaultValues, + }); + + const onSubmit = (values: z.infer<typeof hdtflZod>) => { + if (isModal) { + htdflBdstReg(values, '41'); + } else { + htdflBdstUpdate(values, '41'); + } + }; + + + const cleanHtdflData = (raw: any): z.infer<typeof hdtflZod> => { + return { + ...raw, + frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + archChgPrmsnDt: raw?.archChgPrmsnDt + ? new Date(raw?.archChgPrmsnDt) + : null, + chgBuilding: (raw?.chgBuilding || []).map((item: any) => ({ + ...item, + archChgPrmsnDt: + item?.archChgPrmsnDt === null + ? new Date() + : new Date(item?.archChgPrmsnDt), + bdstChgNm: item?.bdstChgNm ?? "", // null → "" + })), + reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + ...item, + instlCmptnDt: new Date(item?.instlCmptnDt), + htdflObj: { + ...item?.htdflObj, + }, + })), + }; + }; + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + useEffect(() => { + if (hdtflZod) form.reset(cleanHtdflData(htdflForm)); + }, [htdflForm]); + + return ( + <div className="!mx-[1.6rem] !p-0r"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + {/* <div className="flex justify-between items-center mx-3"> */} + <div> + <div className="grid grid-cols-12 flex items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10">온배수 시설 정보</h1> + {!readonly && ( + <Button type="submit" className="col-start-12 col-span-1" size={"sm"}> + 저장 + </Button> + )} + </div> + </div> + + {isModal && htdflForm != undefined ? <BdstReg form={form} /> : <></>} + {isModal ? ( + <h1 className="text-xl col-span-10 mt-4"> + 온배수 시설 정보{" "} + </h1> + ) : ( + <></> + )} + + <div className="grid grid-cols-6 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.etgtType" + render={({ field }) => ( + <FormUi label="발전유형(코드정비후 수정)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e) + }} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.fclCpct" // 이름 이상함. + render={({ field }) => ( + <FormUi label="설비용량"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.plwtYn" + render={({ field }) => ( + <FormUi label="담수화 여부"> + <SelectBox + className={""} + label={""} + selectClass={selectClass} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=" my-4"> + <h1 className={formH1Class}> + 온배수 시설 운영현황 + </h1> + </div> + + <div className="grid grid-cols-10 gap-1 mt-4"> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.orgwtWtritSpt" + render={({ field }) => ( + <FormUi label="원수취수 지점(코드정비후 수정)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + value={field.value ?? 0} + onChange={(e) => { + if (!readonly) field.onChange(Number(e.target.value)); + }} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.orgwtOcrnProcs" + render={({ field }) => ( + <FormUi label="원수발생 공정"> + <Input + className={selectClass} + placeholder="1,000,000" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange + }} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.wtritEntrcTp" + render={({ field }) => ( + <FormUi label="취수구 온도"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.amtgnr" + render={({ field }) => ( + <FormUi label="발생량(m³/년)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.prcsProcsYn" + render={({ field }) => ( + <FormUi label="처리공정"> + <SelectBox + className={""} + label={""} + selectClass={selectClass} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.prcsFcltCpct" + render={({ field }) => ( + <FormUi label="처리시설 용량(m³/년)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.fmYn" + render={({ field }) => ( + <FormUi label="유량계유무"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.utztnCpct" + render={({ field }) => ( + <FormUi label="이용량(m³/년)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.utztnUsg" + render={({ field }) => ( + <FormUi label="이용용도"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + value={field.value ?? ""} + onChange={() => { + if (!readonly) field.onChange + }} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className=" my-2"> + <h1 className={formH1Class}> + 요구수질 + </h1> + </div> + + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.grssClfg" + render={({ field }) => ( + <FormUi label="총대장균군"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.tn" + render={({ field }) => ( + <FormUi label="총질소"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.combResdch" + render={({ field }) => ( + <FormUi label="결합잔류염소"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-6 "> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.tph" + render={({ field }) => ( + <FormUi label="총인"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.trbt" + render={({ field }) => ( + <FormUi label="탁도"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.ph" + render={({ field }) => ( + <FormUi label="수소이온농도"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.bod" + render={({ field }) => ( + <FormUi label="생물학적 산소요구량"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.clIonQnt" + render={({ field }) => ( + <FormUi label="염화물"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.smll" + render={({ field }) => ( + <FormUi label="냄새"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.cdtnv" + render={({ field }) => ( + <FormUi label="전기전도도"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.crmty" + render={({ field }) => ( + <FormUi label="색도"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-6"> + <FormField + control={form.control} + name="reuseBuilding.0.htdflObj.emsnExitTp" + render={({ field }) => ( + <FormUi label="배출구온도"> + <Input + className={selectClass} + placeholder="10" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + </form> + </Form> + </div> + ); +} diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx new file mode 100644 index 000000000..df63b3520 --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -0,0 +1,737 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + businessMoneyInfo, + businessSttnInfo, + concentratedWaterInfo, + ruseFcltinfo, + ruseWaterQulityList, + wasteWaterFcltINfo, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/headList"; +import { + fcBizSchema, + type fcBizType, + type PbnstRegArrrayType, + 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 { DatePicker, FormUi, UiModal } from "@common/business_components/ui"; +import { Form, FormField } from "@common/business_components/ui/form"; +import { Button, DataTable, Input, Label } from "@common/components"; +import { useEffect, useRef, useState } from "react"; +import { useFieldArray, useForm, type UseFormReturn } 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 from "@common/business_components/ui/sdmInsertForm"; +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; +import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm"; +import type { SelectDataType } from "@common/business_components"; + +export function PbnstBuseForm({ + indexMng = 0, + indexBiz = 0, + setIndexBiz, + form, +}: { + formType: string; + firstBizData?: fcBizType[]; + form: UseFormReturn<{ items: PbnstRegArrrayType }>; + indexMng: number; + indexBiz: number; + setIndexBiz: (index: number) => void; +}) { + const ref = useRef<ppInstlType[] | null>(null); + const { + code, + pbnst, + demandCompList, + getpbnstInsertIndex, + appendRuseFclt, + getDemandCompList, + regDemandCompany, + } = useWrStore((state) => state); + const { pbnstBizCycle, pbnstRes } = pbnst; + const { fields: fcBizs, append: appendBiz } = useFieldArray({ + control: form.control, + name: `items.${indexMng}.fcBizs`, + }); + + const [bizCycle, setBizCycle] = useState("1"); + const [ppiSelectList, setPpiSelectList] = + useState<[{ name: string; val: SelectDataType[] }]>(); + const [ppiYn, setPpiYn] = useState("Y"); + + const [selectBizCycle, setSelectBizCycle] = useState< + { + text: string; + val: number; + }[] + >([{ text: "없음", val: 0 }]); + const [isFcOrgwts, setIsFcOrgwts] = useState<boolean>(false); + const [sumCpct, setSumCpct] = useState<number>(0); + const [getDmdList, setGetFmdList] = useState<number>(0); + const [isDmdModal, setIsDmdModal] = useState<{ + isMoal: boolean; + data: DemandCompType; + }>({ isMoal: false, data: {} as DemandCompType }); + + useEffect(() => { + const total = + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`) ?? 0) + + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`) ?? 0) + + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0); + + form.setValue(`items.${indexMng}.fcBizs.${indexBiz}.totalMoney`, total); + }, [ + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`), + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`), + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`), + ]); + + // fcBizs 데이터가 변경될 때마다 차수 선택 목록을 업데이트합니다. + useEffect(() => { + const cycleLength = fcBizs.length; + const newSelectBizCycle = Array.from( + { length: cycleLength }, + (_, index) => ({ + text: `${index + 1}차`, + val: index, + }) + ); + setSelectBizCycle(newSelectBizCycle); + + // 현재 차수 인덱스가 유효하지 않게 되면 마지막 인덱스로 조정합니다. + if (indexBiz >= cycleLength && cycleLength > 0) { + setIndexBiz(cycleLength - 1); + } + }, [fcBizs, indexBiz, setIndexBiz]); + // 2. Define a submit handler. + function addRuseFcltObj() { + const formRuse = form.getValues(`items.${indexMng}`); + const formBizs = form.getValues(`items.${indexMng}.fcBizs`); + const pbnstBizCycle = formBizs.length; + + // 🧩 새로운 fcBizs 기본 객체 생성 (필수값만 채우고 나머지는 default로) + const newItem = fcBizSchema.parse({ + cycl: pbnstBizCycle + 1, + pbnstRuseFcltFk: formRuse.pbnstRuseFcltPk, + fcOrgwts: undefined, + pplInstls: undefined, + fcDemands: undefined, + }); + appendBiz(newItem); + setIndexBiz(pbnstBizCycle); // 새로 추가된 차수로 포커스 이동 + } + useEffect(() => { + //console.log("form ", form.watch()); + }, [form.formState]); + + const { + fields: fcDemands, + append: appendDemand, + remove: removeDemand, + } = useFieldArray({ + control: form.control, + name: `items.${indexMng}.fcBizs.${indexBiz}.fcDemands`, + }); + + const { fields: fcOrgwtFiled } = useFieldArray({ + control: form.control, + name: `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`, + }); + const { + fields: pplInstls, + append: appendPpl, + remove: removePpl, + } = useFieldArray({ + control: form.control, + name: `items.${indexMng}.fcBizs.${indexBiz}.pplInstls`, + }); + + useEffect(() => { + if (ppiYn === "N") { + form.setValue(`items.${indexMng}.fcBizs.${indexBiz}.pplInstls`, []); + } + }, [ppiYn, indexMng, indexBiz]); + + const plusHist = () => { + if ( + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.pbnstRuseFcltBizPk`) + ) { + appendPpl({ + ruseFcltBizFk: form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.pbnstRuseFcltBizPk` + ), + choose: false, + ppMqlt: "", + pdmt: "", + cpct: 0, + }); + } else { + appendPpl({ + choose: false, + ppMqlt: "", + pdmt: "", + cpct: 0, + }); + } + }; + + const plusArray = () => { + appendDemand({ + dmdCoNm: "", + daddr: "", + useUsgCd: "", + splyBgngDay: new Date(), + splyEndDay: new Date(), + splyCpct: 0, + splyFmYn: "Y", + pbnstRuseFcltBizPk: form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.pbnstRuseFcltBizPk` + ), + pbnstRuseFcltFk: + form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.pbnstRuseFcltFk` + ) ?? "", + dmdInfoPk: "", + //telno: "", + }); + }; + + const removeSelected = () => { + const nowPPl = form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.pplInstls` + ); + + if (!nowPPl) return; + + const selectedIdxs = nowPPl + .map((item, idx) => (item.choose ? idx : -1)) + .filter((idx) => idx !== -1); + selectedIdxs.reverse().forEach((idx) => removePpl(idx)); + }; + + 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]); + + useEffect(() => { + const pplSum = form.watch(`items.${indexMng}.fcBizs.${indexBiz}.pplInstls`); + if (!pplSum) return; + let sum = 0; + pplSum.forEach((item) => { + sum += Number(item.cpct) || 0; + }); + setSumCpct(sum); + }, [form.formState, indexMng, indexBiz]); + + // // 차수별 정보(fcBizs)가 하나도 없을 경우 플레이스홀더 UI를 보여줍니다. + // if (fcBizs.length === 0) { + // return ( + // <div className="!mx-[1.6rem] !p-0r flex flex-col items-center justify-center h-64 text-gray-500 border rounded-md"> + // <p className="text-lg">등록된 차수별 정보가 없습니다.</p> + // <p className="text-sm mb-4">아래 버튼을 눌러 신규 차수 정보를 등록해주세요.</p> + // <Button + // onClick={addRuseFcltObj} + // type="button" + // size={commonButonSize} + // className="mt-2" + // > + // 신규 차수 등록 + // </Button> + // </div> + // ); + // } + + const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`); + return ( + <> + {pbnstBiz && pbnstBiz?.length > 0 ? ( + <div className=" !mx-[1.6rem] !p-0r"> + {/* 차수별 등록 현황 폼 */} + <div className="justify-between"> + <Form {...form}> + <form + key={indexBiz} + // 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="" + key={`cycle-${indexMng}-${indexBiz}`} + selectArray={selectBizCycle || []} + value={selectBizCycle[indexBiz]?.val} + className={""} + selectClass={selectClass} + onSelectValue={(val) => { + setIndexBiz(Number(val)); + }} + ></SelectBox> + + <Button + onClick={addRuseFcltObj} + type="button" + size={commonButonSize} + > + 추가 + </Button> + </div> + </div> + <MngRegFormDetail + key={`buziInSttn-${indexMng}-${indexBiz}`} + formList={businessSttnInfo} + h1Title={"사업현황"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + indexMng={indexMng} + indexBiz={indexBiz} + /> + <MngRegFormDetail + key={`buziMoney-${indexMng}-${indexBiz}`} + formList={businessMoneyInfo} + h1Title={"사업비"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + indexMng={indexMng} + indexBiz={indexBiz} + /> + + <MngRegFormDetail + key={`reprocessingFclt-${indexMng}-${indexBiz}`} + formList={ruseFcltinfo} + h1Title={"재처리시설"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + indexMng={indexMng} + indexBiz={indexBiz} + /> + <MngRegFormDetail + key={`concertedWater-${indexMng}-${indexBiz}`} + formList={concentratedWaterInfo} + h1Title={"농축수관리"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + indexMng={indexMng} + indexBiz={indexBiz} + /> + <MngRegFormDetail + key={`wasteFcltinfo-${indexMng}-${indexBiz}`} + formList={wasteWaterFcltINfo} + h1Title={"폐수배출시설 정보"} + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + indexMng={indexMng} + indexBiz={indexBiz} + /> + + <h1 className={formH1Class}>재이용 원수취수 현황</h1> + <div> + <div className="text-right mb-3"> + <Button + type="button" + onClick={() => setIsFcOrgwts(true)} + size={commonButonSize} + className="" + > + 추가등록 + </Button> + </div> + <RuseMngTable + key={`fcOrgwts-${indexMng}-${indexBiz}`} // 이게 핵심! + tableData={ + form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts` + ) ?? [] + } + headList={pbnst.pbnstOrgwtHeadList} + upHeadList={[]} + indexMng={indexMng} + indexBiz={indexBiz} + /> + </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> + <RuseMngTableForm + key={`pplInstls-${indexMng}-${indexBiz}`} // 이게 핵심! + tableData={ + form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.pplInstls` + ) ?? [] + } + headList={pbnst.pplInstlsHeadLisat} + code={ppiSelectList ?? []} + //ref={ref} + indexMng={indexMng} + indexBiz={indexBiz} + form={form} + formNm={"pplInstls"} + upHeadList={[]} + onTableSet={(val) => { + const pplSum = form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.pplInstls` + ); + if (!pplSum) return; + let sum = 0; + pplSum.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" + indexMng={indexMng} + indexBiz={indexBiz} + /> + <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> + {( + form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.fcDemands` + ) || [] + ).map((field, index) => ( + <div + key={`fcDemands-${indexMng}-${indexBiz}`} // 이게 핵심! + className="grid grid-cols-12 gap-4 mt-3" + > + <div key={index + "keySon1"} className="col-span-4"> + <FormField + control={form.control} + name={`items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.dmdCoNm`} + render={({ field: f }) => ( + <FormUi label="수요처명"> + <SearchInputText + label={"수요처"} + initValue={f.value} + setValue={(val) => { + f.onChange(val); + if (val && val.length > 2) { + getDemandCompList(val, 3); + } + }} + setRtnData={(valList: any) => { + if (valList) { + form.setValue( + `items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.daddr`, + valList.daddr + ); + form.setValue( + `items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.useUsgCd`, + valList.useUsgCd + ); + form.setValue( + `items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.splyCpct`, + valList.splyCpct + ); + form.setValue( + `items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.dmdInfoPk`, + valList.dmdInfoPk?.toString() + ); + } + }} + searchList={demandCompList?.map((item) => { + return { + ...item, + searchNm: item.dmdCoNm, + searchPk: item.dmdInfoPk, + }; + })} + ></SearchInputText> + </FormUi> + )} + /> + </div> + <div key={index + "keySon2"} className="col-span-4"> + <FormField + control={form.control} + name={`items.${indexMng}.fcBizs.${indexBiz}.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={`items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.useUsgCd`} + render={({ field: f }) => ( + <FormUi label="사용용도"> + <SelectBox + label={""} + selectClass={""} + className="w-full" + selectArray={code.dmdUszCd} + onSelectValue={f.onChange} + value={f.value ?? code.dmdUszCd[0]?.val} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon4"} className="col-span-3"> + <FormField + control={form.control} + name={`items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.splyBgngDay`} + render={({ field: f }) => ( + <FormUi label="공급시작일"> + <DatePicker + mode={"single"} + field={{ + value: safeDate(f.value) || new Date(), + onChange: (date: Date | undefined) => + f.onChange(date), + }} + selectClass={"rounded-lg !w-full"} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon5"} className="col-span-3"> + <FormField + control={form.control} + name={`items.${indexMng}.fcBizs.${indexBiz}.fcDemands.${index}.splyEndDay`} + render={({ field: f }) => ( + <FormUi label="공급종료일"> + <DatePicker + mode={"single"} + onSelect={(date: Date | undefined) => + f.onChange(date) + } + field={{ + value: safeDate(f.value) || new Date(), + onChange: (date: Date | undefined) => + f.onChange(date), + }} + selectClass={"rounded-lg !w-full"} + /> + </FormUi> + )} + /> + </div> + <div key={index + "keySon6"} className="col-span-3"> + <FormField + control={form.control} + name={`items.${indexMng}.fcBizs.${indexBiz}.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={`items.${indexMng}.fcBizs.${indexBiz}.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 ?? code.yn[0]?.val} + /> + </FormUi> + )} + /> + </div> + </div> + ))} + </div> + </form> + </Form> + </div> + + <UiModal + open={isFcOrgwts} + onOpenChange={(open: boolean) => setIsFcOrgwts(open)} + bodyClass={"w-[50vw] max-w-none h-[90vh]"} + body={ + <FcOrgwtsRegModal + closeModal={() => { + setIsFcOrgwts(false); + }} + onRightSet={(val) => { + form.setValue( + `items.${indexMng}.fcBizs.${indexBiz}.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) => { + 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 className="!mx-[1.6rem] !p-0r flex flex-col items-center justify-center h-64 text-gray-500 border rounded-md"> + <p className="text-lg">등록된 차수별 정보가 없습니다.</p> + <p className="text-sm mb-4"> + 아래 버튼을 눌러 신규 차수 정보를 등록해주세요. + </p> + <Button + onClick={addRuseFcltObj} + type="button" + size={commonButonSize} + className="mt-2" + > + 신규 차수 등록 + </Button> + </div> + )} + </> + ); +} diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx new file mode 100644 index 000000000..d07d24a23 --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx @@ -0,0 +1,522 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + FileUpload, + FormUi, + SelectBox, + type SelectDataType, +} from "@common/business_components/ui"; +import { Form, FormField } from "@common/business_components/ui/form"; +import { Button, Input } from "@common/components"; +import { useEffect, useState } from "react"; +import { useFieldArray, useWatch, type UseFormReturn } from "react-hook-form"; +import { z } from "zod"; +import { + commonButonSize, + formH1Class, + formTableName, +} from "@/components/lib/class/comClass"; +import { TusClientUpload } from "@/components/lib/useTus_wr/useTusWr"; +import { + pbnstRegZod, + type customPbnst, + type fcFileType, + type PbnstRegArrrayType, + 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"; +import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm"; +export type FilesType = { + atflCd: string; + file: File; +}; + +export function PbnstMngForm({ + form, + formType, + indexMng, + setIndexMng, +}: { + form: UseFormReturn<{ items: PbnstRegArrrayType }>; + formType: string; + firstFcltData?: customPbnst; + indexMng: number; + setIndexMng: (index: number) => void; +}) { + const { + code, + pbnst, + pbnstInsert, + fileDelete, + fileDownLoad, + } = useWrStore((state) => state); + + const { pbnstRes } = pbnst; + 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 [mngMtdCode, setMngMtdCode] = + useState<{ name: string; val: SelectDataType[] }[]>(); + + const { + fields: fcOpersField, + append, + remove, + } = useFieldArray({ + control: form.control, + name: `items.${indexMng}.fcOpers`, + }); + + const watchedItem = useWatch({ + control: form.control, + name: `items.${indexMng}`, + }); + + const plusHist = () => { + append({ + choose: false, + pbnstRuseFcltFk: + form.getValues(`items.${indexMng}.pbnstRuseFcltPk`) ?? "", + rgtr: "", + mdfr: "", + operMthd: "", + rprtvBzenty: "", + bgngDt: new Date(), + endDt: new Date(), + }); + }; + + const removeSelected = () => { + const nowOpers = form.getValues(`items.${indexMng}.fcOpers`); + if (!nowOpers) return; + + const selectedIdxs = nowOpers + .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 (indexMng >= 0) { + const defaultFiles = watchedItem?.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), + })) + ); + else { + setRelationFiles(null); + } + + if (planFileList.length > 0) + setPlanFiles( + planFileList.map((item) => ({ + atflCd: item.fileTkn, + file: new File([] as BlobPart[], item.filePath), + })) + ); + else { + setPlanFiles(null); + } + } else { + setRelationFiles(null); + setPlanFiles(null); + } + } + }, [watchedItem, indexMng]); + + useEffect(() => { + // pbnstMngData(pbnst.pbnstMngRegData); + }, [pbnst.pbnstMngRegData]); + + useEffect(() => { + if (pbnst.pbnstRes && pbnst.pbnstRes?.length >= 0) { + + const fcltNmList = pbnst.pbnstRes?.map((item, index) => ({ + text: item.ruseUtztnFcltNm, + val: String(index), + })); + + + console.log(' fcltNmList ', fcltNmList) + if (fcltNmList) setFcltNm(fcltNmList); + }else{ + setFcltNm(null); + } + }, [pbnst.pbnstRes]); + + // 2. Define a submit handler. + function onSubmit(values: z.infer<typeof pbnstRegZod>) { + values.fcOpers = []; + values.fcFiles = regFileList || []; + pbnstInsert(values); + } + + useEffect(() => { + if (relationfiles) { + setRegFileList(regFileFilter(relationfiles, "계통도", regFileList || [])); + } + }, [relationfiles]); + useEffect(() => { + if (planFiles) { + setRegFileList(regFileFilter(planFiles, "평면도", regFileList || [])); + } + }, [planFiles]); + + useEffect(() => { + if (code.ruseMngMtd) { + setMngMtdCode([{ name: "ruseMngMtd", val: code.ruseMngMtd }]); + } + }, [code]); + + + useEffect(() =>{ + // console.log(' fcltNm ', watchedItem?.ruseUtztnFcltNm) + },[watchedItem]) + + + useEffect(() => { + console.log(" form indexMng ", indexMng); + }, [indexMng]); + + return ( + <div className=" !mx-[1.6rem] !p-0r"> + <div> + <Form {...form}> + {/* <form onSubmit={form.handleSubmit()}> */} + <form> + <div className="flex justify-between items-center mx-3 mb-3"> + <div className="col-span-2 flex items-top"> + <Label className={formTableName + "pr-3 col-span-2 "}> + 재이용 시설명 + </Label> + + {formType === "update" ? ( + <FormUi label=""> + <SelectBox + label="" + key={`fclt-selector-${indexMng}`} + selectArray={fcltNm ?? []} + onSelectValue={(val) => { + setIndexMng(Number(val)); + }} + value={String(indexMng)} + className={""} + selectClass={undefined} + /> + </FormUi> + ) : ( + <FormField + control={form.control} + name={`items.${indexMng}.ruseUtztnFcltNm`} + key={`items-ruseUtztnFcltNm-${indexMng}`} + render={({ field }) => ( + <FormUi label=""> + <Input + + + className="!text-2xl" + placeholder="" + type="text" + onChange={(e) => { + field.onChange(String(e.target.value)); + }} + value={field.value ?? ""} + readOnly={formType === "sttn" ? true : false} + /> + </FormUi> + )} + /> + )} + </div> + </div> + + <div> + <div className="h-[200px] overflow-y-auto"> + <div className="flex justify-between items-center mx-3 mb-3"> + <span className={formTableName}>재이용시설 운영정보</span> + {formType === "sttn" ? ( + <></> + ) : ( + <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> + <RuseMngTableForm + tableData={form.getValues(`items.${indexMng}.fcOpers`) || []} + headList={ + formType === "sttn" + ? pbnst.pbnstMngUpHeadList.map((item) => { + return { + ...item, + readonly: true, + }; + }) + : pbnst.pbnstMngUpHeadList + } + upHeadList={pbnst.pbnstMngNm} + code={mngMtdCode || []} + onTableSet={(val) => { + //setFcOpresData(val); + }} + indexMng={indexMng} + form={form} + formNm={"fcOpers"} + // ref={ref} + ></RuseMngTableForm> + </div> + </div> + <div> + <h1 className={formH1Class}>재이용수 공급 가능량</h1> + <div className="grid grid-cols-12 gap-4"> + <div className="col-span-4 mt-4"> + <FormField + key={`FormField-items.${indexMng}.splyExtraCpct`} + control={form.control} + name={`items.${indexMng}.fcltCpct`} + render={({ field }) => ( + <FormUi label="재이용 시설용량(m³/일)"> + <Input + key={`items.${indexMng}.fcltCpct`} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly={formType === "sttn" ? true : false} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-4 mt-4"> + <FormField + key={`FormField-items.${indexMng}.ruseCpct`} + control={form.control} + name={`items.${indexMng}.ruseCpct`} + render={({ field }) => ( + <FormUi label="재이용수공급량(m³/일)"> + <Input + key={`items.${indexMng}.ruseCpct`} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly={formType === "sttn" ? true : false} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4 mt-4"> + <FormField + key={`FormField-items.${indexMng}.splyExtraCpct`} + control={form.control} + name={`items.${indexMng}.splyExtraCpct`} + render={({ field }) => ( + <FormUi label="공급여유량(m³/일)"> + <Input + key={`items.${indexMng}.splyExtraCpct`} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly={formType === "sttn" ? true : false} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=""> + <h1 className={formH1Class}>계통도 평면도</h1> + </div> + <div className="grid grid-cols-12 gap-4 my-4"> + <div className="col-span-6"> + {formType === "sttn" ? ( + relationfiles && relationfiles?.length > 0 ? ( + <> + <Label className="text-3xl">계통도</Label> + {relationfiles?.map((item, index) => ( + <span + key={index + "plan"} + className="text-2xl block cursor-pointer text-blue-600 hover:underline" + onClick={() => fileDownLoad(item.atflCd)} + > + {item.file.name} + </span> + ))} + </> + ) : ( + <></> + ) + ) : ( + <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"> + {formType === "sttn" ? ( + planFiles && planFiles?.length > 0 ? ( + <> + <Label className="text-3xl">평면도</Label> + {planFiles?.map((item, index) => ( + <span + key={index + "plan"} + className="text-2xl block cursor-pointer text-blue-600 hover:underline" + onClick={() => fileDownLoad(item.atflCd)} + > + {item.file.name} + </span> + ))} + </> + ) : ( + <></> + ) + ) : ( + <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> + </form> + </Form> + </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 new file mode 100644 index 000000000..971888e10 --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx @@ -0,0 +1,110 @@ +"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 { + DatePicker, + FormField, + FormUi, + SearchInputSelect, + SelectBox, + type SelectDataType, +} from "@common/business_components/ui"; +import { Input } from "@common/components/ui"; +import { useState } from "react"; + +export default function MngRegFormDetail({ + formList, + h1Title, + form, + moClass, + sonClass, + indexBiz, + indexMng, +}: { + formList: formDetailType[]; + h1Title?: string; + form: any; + moClass?: string; + indexBiz?: number; + indexMng?: number; + sonClass?: string; +}) { + const { code } = useWrStore((state) => state); + + return ( + <> + {h1Title ? <h1 className={formH1Class}>{h1Title}</h1> : <></>} + <div className={moClass}> + {formList?.map((formHead, index) => { + const fieldId = + typeof indexMng === "number" && + indexMng >= 0 && + typeof indexBiz === "number" && + indexBiz >= 0 + ? `items.${indexMng}.fcBizs.${indexBiz}.${formHead.id}` + : typeof indexMng === "number" && indexMng >= 0 + ? `items.${indexMng}.${formHead.id}` + : formHead.id; + + return ( + <div className={sonClass} key={index + `${formHead.id}`}> + <FormField + control={form.control} + name={fieldId} + render={({ field }) => ( + <FormUi label={formHead.lable}> + {formHead.type === "select" ? ( + <Input placeholder="" type="text" {...field} /> + ) : formHead.type === "date" ? ( + <DatePicker + mode={"single"} + className={"rounded-lg !w-full"} + field={{ + value: safeDate(field.value) || new Date(), + onChange: (date: Date | undefined) => + field.onChange(date), + }} + selectClass={"w-full"} + /> + ) : formHead.type === "number" ? ( + <Input + placeholder="" + type="number" + value={field.value ?? 0} + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + readOnly={formHead.option === "readOnly"?true:false} + /> + ) : formHead.type === "selectYn" ? ( + <SelectBox + label={""} + selectClass={""} + className="w-full" + selectArray={code.yn} + onSelectValue={(val: string | number) => + field.onChange(val) + } + value={field.value ?? "Y"} + /> + ) : ( + <Input + placeholder="" + type="text" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={formHead.option === "readOnly"?true:false} + /> + )} + </FormUi> + )} + /> + </div> + ); + })} + </div> + </> + ); +} diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx new file mode 100644 index 000000000..9ec4fc5c0 --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -0,0 +1,431 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + rainZod, + selectClass, + type customRainBdst +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; +import { DatePicker, FormUi } from "@common/business_components"; +import { Form, FormField, SelectBox } from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import BdstReg from "../bdstReg/bdstReg"; + +export default function RainRegForm({ + rainForm, + isModal, + readonly = false +}: { + rainForm: customRainBdst; + isModal: boolean; + readonly?: boolean; +}) { + const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore( + (state) => state + ); + const { rainFcltCd } = code; + const defaultValues: z.infer<typeof rainZod> = rainZod.parse({}); + const form = useForm<z.infer<typeof rainZod>>({ + resolver: zodResolver(rainZod), + defaultValues: defaultValues, + }); + + const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn] = form.watch([ + "reuseBuilding.0.grntchBdstCertYn", + "reuseBuilding.0.instlCstSprtYn", + "reuseBuilding.0.rainyObj.filtrTrtmfctYn", + ]); + + const cleanRainData = (raw: any): z.infer<typeof rainZod> => { + return { + ...raw, + frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + archChgPrmsnDt: raw?.archChgPrmsnDt + ? new Date(raw?.archChgPrmsnDt) + : null, + reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + ...item, + instlCmptnDt: new Date(item?.instlCmptnDt), + operMngCd: item?.operMngCd ?? "", // null → "" + grntchBdstCertYr: + typeof item?.grntchBdstCertYr === "string" + ? item?.grntchBdstCertYr + : "", // null → "" + // Ensure enum fields are null if empty string to trigger Zod default + rainyObj: { + ...item?.rainyObj, + filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn === "" ? null : item?.rainyObj?.filtrTrtmfctYn, + }, + })), + }; + }; + + useEffect(() => { + if (grntchBdstCertYn === "N") { + form.setValue("reuseBuilding.0.grntchBdstCertYr", ""); + form.setValue("reuseBuilding.0.grntchBdstCertNo", ""); + form.setValue("reuseBuilding.0.grntchBdstCertGrd", ""); + } else if (instlCstSprtYn === "N") { + form.setValue("reuseBuilding.0.instlCstSprtAmt", 0); + } else if (filtrTrtmfctYn === "N") { + form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", 0); + form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", ""); + } + }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]); + + // form 값이 변경될 때마다 로그 출력 + useEffect(() => { + const subscription = form.watch((value) => { + }); + return () => subscription.unsubscribe(); + }, [form]); + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + const onSubmit = (values: z.infer<typeof rainZod>) => { + if (isModal) { + rainBdstReg(values, "41"); + } else { + rainBdstUpdate(values, "41"); + } + }; + + useEffect(() => { + if (rainZod) form.reset(cleanRainData(rainForm)); + }, [rainForm]); + + return ( + <div className="!mx-[1.6rem] !p-0r"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <div className="grid grid-cols-12 flex items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10"> + {isModal ? "건축물 운영정보" : "빗물이용시설 정보"} + </h1> + {!readonly && ( + <Button type="submit" className="col-start-12 col-span-1" size={"sm"}> + 저장 + </Button> + )} + </div> + </div> + {isModal && rainForm != undefined ? <BdstReg form={form} /> : <></>} + {isModal ? ( + <h1 className="pl-3 text-xl col-span-10 mt-4"> + 빗물이용시설 정보{" "} + </h1> + ) : ( + <></> + )} + <h1 className={formH1Class}> + 기본정보 + </h1> + + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCmptnDt" + render={({ field }) => ( + <FormUi label="설치완료일"> + <DatePicker + mode="single" + field={{ + value: + field.value && !isNaN(new Date(field.value).getTime()) + ? new Date(field.value) + : new Date(), // Provide a default Date value + onChange: (date) => field.onChange(date), + + }} + selectClass={"w-full"} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCst" + render={({ field }) => ( + <FormUi label="설치비"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=""> + <h1 className={formH1Class}> + 빗물이용시설 정보 + </h1> + </div> + <div> + <h2 className={formH2Class}>집수면</h2> + </div> + <div className="grid grid-cols-10 gap-4 mt-4 "> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.pntfColKnd" + render={({ field }) => ( + <FormUi label="종류"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={rainFcltCd} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? rainFcltCd[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.pntfColArea" + render={({ field }) => ( + <FormUi label="면적(m³)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div> + <h2 className={formH2Class}>여과 등 처리시설</h2> + </div> + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.filtrTrtmfctYn" + render={({ field }) => ( + <FormUi label="유무"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value || code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.filtrTrtmfctCpct" + render={({ field }) => ( + <FormUi label="용량(m³/일)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={filtrTrtmfctYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.filtrTrtmfctPrs" + render={({ field }) => ( + <FormUi label="처리공정"> + <Input + className={selectClass} + placeholder="" + type="text" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={filtrTrtmfctYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=" my-2"> + <h1 className={formH1Class}> + 인센티브 정보 + </h1> + </div> + <div> + <h2 className={formH2Class}>설치비 지원</h2> + </div> + <div className="grid grid-cols-10 gap-2 mt-4"> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtYn" + render={({ field }) => ( + <FormUi label="지원 여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtAmt" + render={({ field }) => ( + <FormUi label="지원금액(원)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={instlCstSprtYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + + <div> + <h2 className={formH2Class}>녹색건축물 인증</h2> + </div> + <div className="grid grid-cols-6 gap-2 mt-4"> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYn" + render={({ field }) => ( + <FormUi label="인증여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYr" + render={({ field }) => ( + <FormUi label="인증년도"> + <Input + className={selectClass} + placeholder="2024" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e); + }} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 "> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertNo" + render={({ field }) => ( + <FormUi label="인증번호"> + <Input + className={selectClass} + placeholder="ab1234678" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e); + }} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertGrd" + render={({ field }) => ( + <FormUi label="인증등급"> + <Input + className={selectClass} + placeholder="A" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e); + }} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + </form> + </Form> + </div> + ); +} diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx new file mode 100644 index 000000000..a4f29477a --- /dev/null +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -0,0 +1,422 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + rswtZod, + type customRswtBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; +import { + DatePicker, + Form, + FormField, + FormUi, + SelectBox, +} from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import BdstReg from "../bdstReg/bdstReg"; + +export default function RswtrRegForm({ + rswtForm, + isModal, + readonly = false +}: { + rswtForm: customRswtBdst; + isModal: boolean; + readonly?: boolean; +}) { + const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore( + (state) => state + ); + + const defaultValues: z.infer<typeof rswtZod> = rswtZod.parse({}); + + const form = useForm<z.infer<typeof rswtZod>>({ + resolver: zodResolver(rswtZod), + defaultValues: defaultValues, + }); + + const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs] = form.watch([ + "reuseBuilding.0.grntchBdstCertYn", + "reuseBuilding.0.instlCstSprtYn", + "reuseBuilding.0.rswtrObj.filtrTrtmfctProcs", + ]); + + const onSubmit = (values: z.infer<typeof rswtZod>) => { + if (isModal) { + rswtBdstReg(values, "41"); + } else { + rswtBdstUpdate(values, "41"); + } + }; + const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => { + return { + ...raw, + frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + archChgPrmsnDt: raw?.archChgPrmsnDt + ? new Date(raw?.archChgPrmsnDt) + : null, + reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + ...item, + instlCmptnDt: new Date(item?.instlCmptnDt), + operMngCd: item?.operMngCd ?? "", // null → "" + grntchBdstCertYr: + typeof item?.grntchBdstCertYr === "string" + ? item?.grntchBdstCertYr + : "", // null → "" + rswtrObj: { + ...item?.rswtrObj, + }, + })), + }; + }; + + useEffect(()=>{ + console.log("rswtForm:", rswtForm) + }, [rswtForm]) + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + useEffect(() => { + if (grntchBdstCertYn === "N") { + form.setValue("reuseBuilding.0.grntchBdstCertYr", ""); + form.setValue("reuseBuilding.0.grntchBdstCertNo", ""); + form.setValue("reuseBuilding.0.grntchBdstCertGrd", ""); + } else if (instlCstSprtYn === "N") { + form.setValue("reuseBuilding.0.instlCstSprtAmt", 0); + } + }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs, form]); + + useEffect(() => { + if (rswtZod) form.reset(cleanRswtData(rswtForm)); + }, [rswtForm, form]); + + return ( + <div className="!mx-[1.6rem] !p-0r"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + {/* <div className="flex justify-between items-center mx-3"> */} + <div> + <div className="grid grid-cols-12 flex items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10"> + 건축물 운영정보 + </h1> + {!readonly && ( + <Button type="submit" className="col-start-12 col-span-1" size={"sm"}> + 저장 + </Button> + )} + </div> + </div> + + {isModal && rswtForm != undefined ? <BdstReg form={form} /> : <></>} + {isModal ? ( + <h1 className="pl-3 text-xl col-span-10 mt-4 "> + 중수도 시설 정보{" "} + </h1> + ) : ( + <></> + )} + <h1 className={formH1Class}>기본정보</h1> + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCmptnDt" + render={({ field }) => ( + <FormUi label="설치완료일"> + <DatePicker + mode="single" + field={{ + value: + field.value && !isNaN(new Date(field.value).getTime()) + ? new Date(field.value) + : new Date(), // Provide a default Date value + onChange: (date) => field.onChange(date), + }} + selectClass={"w-full"} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCst" + render={({ field }) => ( + <FormUi label="설치비"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.wtrUsqty" + render={({ field }) => ( + <FormUi label="물사용량(m³)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.oblgTrgtWtrUsqty" + render={({ field }) => ( + <FormUi label="의무대상 물사용량(m³)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=" my-4"> + <h1 className={formH1Class}>중수도 시설 정보</h1> + </div> + + <div className="grid grid-cols-12 gap-1 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.filtrTrtmfctCpct" + render={({ field }) => ( + <FormUi label="처리시설 용량"> + <Input + className={selectClass} + placeholder="" + type="number" + value={field.value ?? 0} + onChange={(e) => { + if (!readonly) field.onChange(Number(e.target.value)); + }} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.orgwtKndCd" + render={({ field }) => ( + <FormUi label="원수 종류"> + <SelectBox + className={""} + label={""} + selectClass={selectClass} + selectArray={code.orgwtKndCd} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.orgwtKndCd[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.filtrTrtmfctProcs" + render={({ field }) => ( + <FormUi label="주요처리공정"> + <Input + className={selectClass} + placeholder="" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange + }} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className=" my-2"> + <h1 className={formH1Class}>인센티브 정보</h1> + </div> + <div> + <h2 className={formH2Class}>설치비 지원</h2> + </div> + <div className="grid grid-cols-2 gap-2 mt-4"> + <div className="col-span-1"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtYn" + render={({ field }) => ( + <FormUi label="설치비 지원여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-1"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtAmt" + render={({ field }) => ( + <FormUi label="지원금액(원)"> + <Input + className={selectClass} + placeholder="1,000,000" + type="text" + onChange={(e) => { + if (!readonly) handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={instlCstSprtYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + + <div> + <h2 className={formH2Class}>녹색건축물 인증</h2> + </div> + <div className="grid grid-cols-6 gap-2 mt-4"> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYn" + render={({ field }) => ( + <FormUi label="인증여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={readonly ? () => { } : field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={readonly} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYr" + render={({ field }) => ( + <FormUi label="인증년도"> + <Input + className={selectClass} + placeholder="2024" + type="text" + {...field} + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e) + }} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertNo" + render={({ field }) => ( + <FormUi label="인증번호"> + <Input + className={selectClass} + placeholder="ab1234678" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e) + }} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertGrd" + render={({ field }) => ( + <FormUi label="인증등급"> + <Input + className={selectClass} + placeholder="A" + type="" + value={field.value ?? ""} + onChange={(e) => { + if (!readonly) field.onChange(e) + }} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + </form> + </Form> + </div> + ); +} diff --git a/apps/wr/components/ui/matDmd/coRegForm/component.tsx b/apps/wr/components/ui/matDmd/coRegForm/component.tsx deleted file mode 100644 index f27639251..000000000 --- a/apps/wr/components/ui/matDmd/coRegForm/component.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useWrStore } from "@/app/store"; -import { selectClass } from "@/app/store/rainReuseFacilityInsert"; -import { DatePicker, FormField, FormItem, FormLabel, FormUi, SelectBox } from "@common/business_components"; -import { Button, Input } from "@common/components"; -import { useState } from "react"; - -export function CoRegForm(form:any){ - - const { dmd } = useWrStore((state) => state); - - return( - - <> - <FormField - control={form.control} - name="ctpv" - render={({ field }) => ( - <FormUi label="시도"> - <SelectBox selectClass={selectClass} label={""} selectArray={dmd.selectSido} onSelectValue={field.onChange} className={""} /> - </FormUi> - - )} - /> - - <FormField - control={form.control} - name="sgg" - render={({ field }) => ( - <FormUi label="시군구"> - <SelectBox selectClass={selectClass} label={""} selectArray={dmd.selectSgg} onSelectValue={field.onChange} className={""} /> - </FormUi> - - )} - /> - - <FormField - control={form.control} - name="coNm" - render={({ field }) => ( - <FormUi label="회사명"> - <Input placeholder="" type="text" {...field} /> - </FormUi> - )} - /> - - <FormField - control={form.control} - name="bznmNo" - render={({ field }) => ( - <FormUi label="사업자번호"> - <Input - placeholder="a0123456789" - type="text" - {...field} /> - </FormUi> - )} - /> - - <FormField - control={form.control} - name="addr" - render={({ field }) => ( - <FormUi label="주소"> - <Input - placeholder="성북로24길 16" - - type="text" - {...field} /> - </FormUi> - )} - /> - - <FormField - control={form.control} - name="telNo" - render={({ field }) => ( - <FormUi label="전화번호"> - <Input - placeholder="01033332222" - - type="text" - {...field} /> - </FormUi> - - )} - /> - - - </> - ) -} \ No newline at end of file diff --git a/apps/wr/components/ui/matDmd/dmdRegform/component.tsx b/apps/wr/components/ui/matDmd/dmdRegform/component.tsx deleted file mode 100644 index 4e6bf0a38..000000000 --- a/apps/wr/components/ui/matDmd/dmdRegform/component.tsx +++ /dev/null @@ -1,316 +0,0 @@ -import { useWrStore } from "@/app/store"; -import { selectClass } from "@/app/store/rainReuseFacilityInsert"; -import { DatePicker, FormField, FormItem, FormLabel, FormUi, SelectBox } from "@common/business_components"; -import { Button, Input } from "@common/components"; -import { useState } from "react"; - -export function DmdRegForm(form: any) { - const { dmd } = useWrStore((state) => state); - - const [dmdDate, setDmdDate] = useState(new Date()) - - - const onChangeDate = (val: Date|undefined) => { - if(val)setDmdDate(val) - } - - return ( - <> - <FormField - control={form.control} - name="dmdSel" - render={({ field }) => ( - <FormUi label="수요처명"> - <Input - placeholder="수요처명 검색" - - type="text" - {...field} /> - </FormUi> - )} - /> - - <FormField - control={form.control} - name="dmdTmng" - render={({ field }) => ( - <FormItem className="flex flex-col"> - <FormLabel>요구시기</FormLabel> - <DatePicker mode={"single"} field={{ value: dmdDate, onChange: onChangeDate }} /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name="dmdWq" - render={({ field }) => ( - <FormUi label="요구수질"> - <Input - placeholder="55.22" - - type="text" - {...field} /> - </FormUi> - - )} - /> - - <FormField - control={form.control} - name="useUsgCd" - render={({ field }) => ( - <FormUi label="사용용도"> - <SelectBox selectClass={selectClass} label={""} selectArray={dmd.selectUsg} onSelectValue={field.onChange} className={""} /> - </FormUi> - - )} - /> - - <FormField - control={form.control} - name="wntUntprc" - render={({ field }) => ( - <FormUi label="희망단가"> - <Input - placeholder="1000000000000000" - - type="text" - {...field} /> - </FormUi> - - )} - /> - - <div className="grid grid-cols-4"> - <h1 className=" col-span-2 pl-5 text-2xl underline"> - 요구수질 - </h1> - </div> - <div className="grid grid-cols-12 gap-4"> - - <div className="col-span-6"> - <FormField - control={form.control} - name="grssClfg" - render={({ field }) => ( - <FormUi label="총대장균군"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - - <div className="col-span-6"> - <FormField - control={form.control} - name="tn" - render={({ field }) => ( - <FormUi label="총질소"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - - <div className="grid grid-cols-12 gap-4"> - <div className="col-span-6"> - <FormField - control={form.control} - name="combResdch" - render={({ field }) => ( - <FormUi label="결합잔류염소"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - - <div className="col-span-6"> - <FormField - control={form.control} - name="tp" - render={({ field }) => ( - <FormUi label="총인"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - - <div className="grid grid-cols-12 gap-4"> - <div className="col-span-6"> - <FormField - control={form.control} - name="trbt" - render={({ field }) => ( - <FormUi label="탁도"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - - <div className="col-span-6"> - <FormField - control={form.control} - name="ph" - render={({ field }) => ( - <FormUi label="수소이온농도"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - - <div className="grid grid-cols-12 gap-4"> - <div className="col-span-6"> - <FormField - control={form.control} - name="bod" - render={({ field }) => ( - <FormUi label="생물학적 산소요구량"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - - <div className="col-span-6"> - <FormField - control={form.control} - name="clIonQnt" - render={({ field }) => ( - <FormUi label="염화물"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - - <div className="grid grid-cols-12 gap-4"> - <div className="col-span-6"> - <FormField - control={form.control} - name="smll" - render={({ field }) => ( - <FormUi label="냄새"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - - <div className="col-span-6"> - <FormField - control={form.control} - name="cdtnv" - render={({ field }) => ( - <FormUi label="전기전도도"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - - <div className="grid grid-cols-12 gap-4"> - <div className="col-span-6"> - <FormField - control={form.control} - name="crmty" - render={({ field }) => ( - <FormUi label="색도"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - - <div className="col-span-6"> - <FormField - control={form.control} - name="rmrk" - render={({ field }) => ( - <FormUi label="비고"> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - <div className="grid grid-cols-4"> - <h1 className=" col-span-1 pl-2 text-2xl underline"> - 공급후보지 선택 - </h1> - <div className=" col-start-3 col-span-1 "> - <Button > - 후보지 조회 - </Button> - </div> - <div className=" col-start-4 col-span-1 "> - <Button > - 유사사례조회 - </Button> - </div> - - </div> - <div className="grid grid-cols-12 gap-4"> - <div className="col-span-4"> - <FormField - control={form.control} - name="sply1" - render={({ field }) => ( - <FormUi label="후보지_1"> - <Input placeholder="후보지1" type="text" {...field} /> - </FormUi> - )} - /> - </div> - <div className="col-span-4"> - <FormField - control={form.control} - name="sply2" - render={({ field }) => ( - <FormUi label="후보지_2"> - <Input placeholder="후보지2" type="text" {...field} /> - </FormUi> - )} - /> - </div> - <div className="col-span-4"> - <FormField - control={form.control} - name="sply3" - render={({ field }) => ( - <FormUi label="후보지_3"> - <Input placeholder="후보지3" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - <div className="grid grid-cols-4"> - <h1 className=" col-span-2 pl-2 text-2xl underline"> - 수요신청 비밀번호 - </h1> - - <div className="col-start-3 col-span-2"> - <FormField - control={form.control} - name="password" - render={({ field }) => ( - <FormUi label=""> - <Input placeholder="10" type="text" {...field} /> - </FormUi> - )} - /> - </div> - </div> - </> - ); -} diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx new file mode 100644 index 000000000..15badc59d --- /dev/null +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -0,0 +1,242 @@ +import { useWrStore } from "@/app/store"; +import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { excelToRainChkData } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel"; +import { chkgdRegExcel } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { + excelToRswtGuideData, + excelToRswtPolution, +} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import { + filePicked, + filePickedArray, + FileUpload, + Form, + FormField, + FormUi, + SelectBox, + Title, +} from "@common/business_components/ui"; +import { Button, Input } 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 ChkgdExcel({ type }: { type: string }) { + const { + rswtChkgdn, + rainChkExcelDataUPload, + rswtChkExceDataUpload, + } = useWrStore((state) => { + return state; + }); + const [files, setFiles] = useState<File[] | null>(null); + const [excelData, setExcelData] = useState< + rswtChkZodType[] | rainChkZodType[] + >(); + const [excelRswtData, setExcelRswtData] = useState<rswtChkZodType[]>(); + + const currentYear = new Date().getFullYear(); + const startYear = 1970; + const yearOptions: { val: string; text: string }[] = Array.from( + { length: currentYear - startYear + 1 }, + (_, i) => { + const y = startYear + i; + return { val: String(y), text: `${y}년` }; + } + ); + + const defaultValues: z.infer<typeof chkgdRegExcel> = chkgdRegExcel.parse({ + chckYr: String(currentYear) + }); + + const form = useForm<z.infer<typeof chkgdRegExcel>>({ + resolver: zodResolver(chkgdRegExcel), + defaultValues: defaultValues, + }); + + useEffect(() => { + if (files) { + const file = files[0]; + if (form.getValues("chckYr").length < 1) { + ToastFormUi({ + body: "파일업로드 이전에 등록년도를 작성해주세요 ", + title: "지도점검 등록", + }); + setFiles(null); + return; + } + if (file) + if (type === "RNB") { + filePicked(file, 0).then((res) => { + if (res !== undefined) + setExcelData(excelToRainChkData(res, form.getValues("chckYr"))); + }); + } else if (type === "RST") { + filePickedArray(file, 0).then((res) => { + if (res && res?.length > 1) { + const excelArray = []; + const guideData = excelToRswtGuideData( + res[0], + form.getValues("chckYr") + ); + const polutionData = excelToRswtPolution( + res[1], + form.getValues("chckYr") + ); + + + const keyGenerator = (item: rswtChkZodType) => + `${item.bdstRuseFcltFk}|${item.chckYmd}|${item.utztnUsgCd}`; + const totalData: rswtChkZodType[] = guideData.map((guide) => { + const fusion = polutionData.find( + (polution) => + guide.bdstRuseFcltFk === polution.bdstRuseFcltFk && + guide.chckYmd === polution.chckYmd && + guide.utztnUsgCd === polution.utztnUsgCd + ); + + + if (fusion) { + return { + ...guide, + rswtrPollutions: fusion?.rswtrPollutions, + rswtrDetails:fusion?.rswtrDetails + }; + } else { + return { + ...guide, + }; + } + }); + + const guideKey = new Set(guideData.map(keyGenerator)); + + const onlypolution = polutionData.filter( + (item) => !guideKey.has(keyGenerator(item)) + ); + + + const listTotalData:rswtChkZodType[] =[ + ...totalData, + ...onlypolution + ] + + + setExcelRswtData(listTotalData); + } + }); + } + } + }, [files]); + + function findMatchingItems<T>( + arrayA: T[], + arrayB: T[], + keySelector: (item: T) => string, + condition?: (item: T) => boolean + ): T[] { + const bKeySet = new Set(arrayB.map(keySelector)); + return arrayA.filter( + (item) => + (!condition || condition(item)) && bKeySet.has(keySelector(item)) + ); + } + + + const onSubmit = (values: z.infer<typeof chkgdRegExcel>) => { + if (values.chckYr === "") { + ToastFormUi({ body: "등록년도를 작성해주세요", title: "지도점검 등록" }); + return; + } + + if (type === "RNB") { + if (excelData === undefined || excelData?.length === 0) { + ToastFormUi({ + body: "빈 파일 및 파일 오류 입니다.", + title: "지도점검 등록", + }); + return; + } else { + + rainChkExcelDataUPload(excelData as rainChkZodType[]); + } + } else if (type === "RST") { + if (excelRswtData === undefined || excelRswtData?.length === 0) { + ToastFormUi({ + body: "빈 파일 및 파일 오류 입니다.", + title: "중수지도점검 등록", + }); + return; + } else { + rswtChkExceDataUpload(excelRswtData as rswtChkZodType[]); + } + } + }; + + return ( + <> + <div> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <Title + title={ + type === "RNB" + ? "빗물 이용시설" + : "중수도 이용시설" + "점검 결과등록" + } + > +
+
+

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

+ + +
+ +
+ ( + + field.onChange(String(val))} + /> + + )} + /> + + ( + + { + if (files) setFiles(files); + }} + fileTypes="xlsx" + > + + )} + /> +
+ + +
+ + ); +} diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx new file mode 100644 index 000000000..9572d1228 --- /dev/null +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -0,0 +1,304 @@ +import { useWrStore } from "@/app/store"; +import { ScrollArea, Title } from "@common/business_components/ui"; +import { Button } from "@common/components/ui"; +import { useEffect, useState } from "react"; +import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { GuideDetailsTable } from "@/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable"; +import type { splRuseWaterType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { rswtrPolutionsZode } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList"; +import type { z } from "zod"; +import React from "react"; +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { + const { + rainChkgdn, + rswtChkgdn, + rainChkSttn, + rswtChkSttn, + rainChkGuidUpdate, + rwstPolAndGuideUpdate, + } = useWrStore((state) => state); + const [guidData, setGuidData] = useState(); + const [headList, setHeadList] = useState<{ + splHeadList: TableUpperProps[]; + ruseHeadList: TableUpperProps[]; + rswtDetailHeadList: TableUpperProps[]; + }>({ splHeadList: [], ruseHeadList: [], rswtDetailHeadList: [] }); + + const [polutionData, setPolutionData] = useState>({ + "1": [] as z.infer[], + "2": [] as z.infer[], + "3": [] as z.infer[], + "4": [] as z.infer[], + }); + const [upPolutionData, setUpPolutionData] = useState>({ + "1": [] as z.infer[], + "2": [] as z.infer[], + "3": [] as z.infer[], + "4": [] as z.infer[], + }); + + const { rainChk, ruseHeadList, splHeadList } = rainChkgdn; + const [updateData, setUpdateData] = useState(); + + const initStats = (): z.infer => ({ + grssClfgUswtr: 0, + combResdch: 0, + trbt: 0, + bod: 0, + smll: 0, + crmty: 0, + tph: 0, + tn: 0, + ph: 0, + clIonQnt: 0, + }); + + ///현황일 경우 input 비활성화 한다. + useEffect(() => { + if (rainChkSttn.isSttn) { + setHeadList({ + splHeadList: rainChkSttn.splHeadList, + ruseHeadList: rainChkSttn.ruseHeadList, + rswtDetailHeadList: type === "RNB" ? [] : rswtChkSttn.rswtDetailSttnHeadList, + }); + } else { + setHeadList({ + splHeadList: rainChkgdn.splHeadList, + ruseHeadList: rainChkgdn.ruseHeadList, + rswtDetailHeadList: + type === "RNB" ? [] : rswtChkgdn.rswtDetailHeadList, + }); + } + }, [ + rainChkSttn.isSttn, + rainChkgdn.splHeadList, + rainChkSttn.splHeadList, + rswtChkgdn.rswtDetailHeadList, + rswtChkSttn.rswtDetailSttnHeadList + ]); + + useEffect(() => { + const result = obj.guideDetails?.reduce((acc: any, cur: any) => { + const code = cur.utztnUsgCd; + const month = cur.ym?.slice(4); // "MM" + const ruseMonthKey = `ruse${month}`; + const splMonthKey = `spl${month}`; + + const ruseValue = cur.ruseFcltUtztnQnt; + const splValue = cur.splyUswtrUtztnQnt; + + // 해당 utztnUsgCd 객체가 없으면 새로 생성 + let target = acc.find( + (it: { utztnUsgCd: string }) => it?.utztnUsgCd === code + ); + if (!target) { + target = { utztnUsgCd: code, ruseTotal: 0, splTotal: 0 }; + acc.push(target); + } + + // 월별 값 누적 + target[ruseMonthKey] = (target[ruseMonthKey] ?? 0) + ruseValue; + target[splMonthKey] = (target[splMonthKey] ?? 0) + splValue; + // total 누적 + target.ruseTotal += ruseValue; + target.splTotal += splValue; + return acc; + }, []); + setGuidData(result); + + if (obj.rswtrPollutions) { + const resultPolution: Record = { + "1": [], + "2": [], + "3": [], + "4": [], + }; + obj.rswtrPollutions.forEach( + (item: z.infer) => { + const nodeKey = String(item.node); + resultPolution[nodeKey]?.push(item); + } + ); + + // 각 노드에 대해 합계/평균 추가 + Object.entries(resultPolution).forEach(([nodeKey, items]) => { + const onlyData = items as z.infer[]; + if (onlyData.length === 0) return; + + const total = initStats(); + onlyData.forEach((item) => { + total.grssClfgUswtr += item.grssClfgUswtr; + total.combResdch += item.combResdch; + total.trbt += item.trbt; + total.bod += item.bod; + total.smll += item.smll; + total.crmty += item.crmty; + total.tph += item.tph; + total.tn += item.tn; + total.ph += item.ph; + total.clIonQnt += item.clIonQnt; + }); + + const average = { + utztnUsgCd: "평균", + edit: false, + grssClfgUswtr: total.grssClfgUswtr / onlyData.length, + combResdch: total.combResdch / onlyData.length, + trbt: total.trbt / onlyData.length, + bod: total.bod / onlyData.length, + smll: total.smll / onlyData.length, + crmty: total.crmty / onlyData.length, + tph: total.tph / onlyData.length, + tn: total.tn / onlyData.length, + ph: total.ph / onlyData.length, + clIonQnt: total.clIonQnt / onlyData.length, + }; + + if (!resultPolution[nodeKey]) { + resultPolution[nodeKey] = []; + } + resultPolution[nodeKey].push(average); + }); + + setPolutionData(resultPolution); + } + }, [obj]); + + const setChGuidData = (data: splRuseWaterType[]) => { + if (data.length > 0) { + setUpdateData(data); + } + }; + + const allDataUpdate = () => { + const realGuideDetail = obj.guideDetails; + + if (updateData === undefined || updateData.length === 0) return; + realGuideDetail.map((item: any) => { + const findObj = updateData.find( + (ch: any) => ch.utztnUsgCd === item.utztnUsgCd + ); + + const month = item.ym?.slice(4); + if (findObj) { + const ruseMonthKey = `ruse${month}` as keyof splRuseWaterType; + const splMonthKey = `spl${month}` as keyof splRuseWaterType; + + if ( + findObj[ruseMonthKey] !== undefined && + findObj[splMonthKey] !== undefined + ) { + item.splyUswtrUtztnQnt = findObj[splMonthKey]; + item.ruseFcltUtztnQnt = findObj[ruseMonthKey]; + + return item; + } + } + }); + obj.guideDetails = realGuideDetail; + if (type === "RNB") { + rainChkGuidUpdate(obj); + } else { + const polution = Object.values(upPolutionData) + .flat() + .filter((item) => item.utztnUsgCd !== "평균"); + obj.rswtrPollutions = polution; + rwstPolAndGuideUpdate([obj]); + } + }; + + const getPolutionData = (data: z.infer[]) => { + const getNode = Number(data[0]?.node); + setUpPolutionData((prevPolutionData) => ({ + ...prevPolutionData, + [getNode]: data, + })); + }; + + return ( + <> +
+
+ +
+
+

+ {`(${obj.bdstNm})`} +

+ {!rainChkSttn.isSttn ? ( + + ) : ( + <> + )} +
+ + +
+

+ {`연간보급수이용량`} +

+
+ { + setChGuidData(data); + }} + /> +
+

+ {type === "RNB" ? `연간빗물 이용량` : "연간중수도 이용량"} +

+
+ { + setChGuidData(data); + }} + /> + + {type !== "RNB" && polutionData !== undefined ? ( +
+ {Object.entries(polutionData).map(([key, value]) => { + const keyNum = Number(key); + + return ( + +
+

+ {`연간이용수질 ${keyNum}분기`} +

+
+ { + getPolutionData(data); + }} + /> +
+ ); + })} +
+ ) : ( + <> + )} +
+
+ + ); +} 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..28dc882c3 --- /dev/null +++ b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx @@ -0,0 +1,160 @@ +import { useWrStore } from "@/app/store"; +import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel"; +import { pbnPrfRegExcelZod } from "@/app/store/prfmncReg/pbnstPrfmncReg/headList"; +import type { FcPrfType, PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +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, Input } 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({ data }: { data: FcPrfType }) { + const { prfmnRegData } = useWrStore((state) => state); + + const [files, setFiles] = useState(null); + const [excelData, setExcelData] = useState(); + const form = useForm>({ + resolver: zodResolver(pbnPrfRegExcelZod), + defaultValues: { + ym: String(new Date().getFullYear()), + }, + }); + const onSubmit = (values: z.infer) => { + + if (excelData) { + prfmnRegData(excelData as unknown as PrfType); + } + + + }; + useEffect(() => { + if (data && data.bscFctNm) + form.setValue("pbnstRuseFcltFk", data.pbnstRuseFcltPk); + form.setValue("bscFctNm", data.bscFctNm || ""); + form.setValue("pbnstRuseFcltNm", data.ruseUtztnFcltNm); + }, [data]); + + useEffect(() => { + console.log("excelData", excelData); + }, [excelData]); + + 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) + setExcelData(excelToPbnPrfData(res as unknown as JSON[], form.getValues("ym"),form.getValues('pbnstRuseFcltFk') ) as PrfType[]) + }); + } + }, [files]); + + return ( + <> +
+
+ +
+ +
+
+

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

+ + +
+ +
+ ( + + field.onChange(val)} + /> + + )} + /> + + ( + + + + )} + /> + ( + + + + )} + /> + + ( + + { + if (files) setFiles(files); + }} + fileTypes="xlsx" + maxFiles={1} + > + + )} + /> +
+
+ +
+ + ); +} diff --git a/apps/wr/components/ui/modal/coMat/constCostFeeTable.tsx b/apps/wr/components/ui/modal/coMat/constCostFeeTable.tsx new file mode 100644 index 000000000..efd47c69b --- /dev/null +++ b/apps/wr/components/ui/modal/coMat/constCostFeeTable.tsx @@ -0,0 +1,103 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import type { + conCostFeeType, + ContCostMap, +} from "@/app/store/common/searchCode/type"; +import { yearOptions } from "@/components/lib/func/commom"; + +import { SelectBox, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Label, ScrollArea } from "@common/components/ui"; +import { useEffect, useRef, useState } from "react"; + +export function ConstCostFeeTable({ + setReturnData, +}: { + setReturnData: (data: any) => void; +}) { + const { coRvw, constCostRes, getConstCostFee } = useWrStore((state) => state); + const [year, setYear] = useState(String(new Date().getFullYear())); + + const [constCostData, setConstCostData] = useState([]); + + + const getFctDescription = (fctCd: string) => { + const fctMap: { [key: string]: string } = { + "001": "총인처리시설", + "002": "염소소독", + "003": "UV소독", + "004": "전처리시설", + "005": "RO", + }; + + return fctMap[fctCd]; + } + useEffect(() => { + const tableData = constCostRes?.reduce( + (acc: ContCostMap[], cur: conCostFeeType) => { + const key = cur.fctCd; + let target = acc?.find((item: any) => item.gubun === key); + if (!target) { + target = { + gubun: cur.fctCd, + "500": 0, + "1000": 0, + "2000": 0, + "3000": 0, + "5000": 0, + "10000": 0, + "20000": 0, + "30000": 0, + "50000": 0, + "100000": 0, + }; + acc?.push(target); + } + target[cur.fctCpc] = cur.stdCost; + + return acc; + }, + [] + ); + const formattedTableData = tableData?.map((item: ContCostMap) => { + return { + ...item, + gubun: getFctDescription(item.gubun) || item.gubun, + }; + }); + setConstCostData(formattedTableData); + }, [constCostRes]); + + return ( + <> + +

+ 공사비 산정표 보기 +

+
+
+ + setYear(String(val))} + /> +
+ {}} + onPageChange={() => {}} + /> +
+ + ); +} diff --git a/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx b/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx new file mode 100644 index 000000000..a6b05ca77 --- /dev/null +++ b/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx @@ -0,0 +1,48 @@ +"use client" + +import { btnClass } from "@/app/store/common/code" +import { drgtDetailHeadListExport } from "@/app/store/matDmd/drgtSttn/headList" +import { ContentsDiv, Title, UiTable } from "@common/business_components/ui" +import ListDiv from "@common/business_components/ui/list_div" +import { Button } from "@common/components/ui" + +export default function DrgtDetailModal() { + return ( + <> + +
+
+ + <div className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={() => { }} + > + 다운로드 + </Button> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={[]} + headlist={drgtDetailHeadListExport} + pageSize={10} + total={10000} + cellClick={(index: number | undefined, headId: string) => { }} + onPageChange={() => { }} + ></UiTable> + </ListDiv> + </div> + </div> + </div> + </div> + </div> + + </ContentsDiv> + </> + ) +} \ No newline at end of file diff --git a/apps/wr/components/ui/modal/coMat/rateFeeModal.tsx b/apps/wr/components/ui/modal/coMat/rateFeeModal.tsx new file mode 100644 index 000000000..ee82420a8 --- /dev/null +++ b/apps/wr/components/ui/modal/coMat/rateFeeModal.tsx @@ -0,0 +1,116 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import type { rateFeeType } from "@/app/store/common/searchCode/type"; +import { yearOptions } from "@/components/lib/func/commom"; +import { SelectBox, UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Input, Label, ScrollArea } from "@common/components/ui"; +import { Scroll } from "lucide-react"; +import { useEffect, useState } from "react"; + +export function RateFeeTableModal({ + setReturnData, +}: { + setReturnData: (data: any) => void; +}) { + const { code, coRvw, rateFeeRes } = useWrStore((state) => state); + const [year, setYear] = useState<string>(String(new Date().getFullYear())); + const [reateFeeTable, setRateFeeTable] = useState<rateFeeType[]>([]); + + const gubunLabelMap: Record<number, string> = { + 50: "5천만원까지", + 100: "1억원까지", + 200: "2억원까지", + 300: "3억원까지", + 500: "5억원까지", + 1000: "10억원까지", + 2000: "20억원까지", + 3000: "30억원까지", + 5000: "50억원까지", + 10000: "100억원까지", + 20000: "200억원까지", + 30000: "300억원까지", + 50000: "500억원까지", + 100000: "1000억원까지", + 200000: "2000억원까지", + 300000: "3000억원까지", + 500000: "5000억원까지", + }; + + const fetchData = async (rateFeeRes: rateFeeType[]) => { + const data = await rateFeeRes?.filter((item: rateFeeType) => { + return item.incExpCd !== "SS"; + }) + .reduce((acc: any, cur: rateFeeType) => { + const key = cur.stdCost; + let target = acc.find((item: any) => item.stdCost === key); + if (!target) { + target = { + stdCost: cur.stdCost, + BD: 0, + SE: 0, + CE: 0, + }; + acc.push(target); + } + target[cur.incExpCd] = cur.rt; + return acc; + }, []); + + + const dataGubun = data?.map((item: any) => { + return { + ...item, + gubun: gubunLabelMap[item.stdCost] || item.stdCost, + }; + }) + + return dataGubun?.sort((a: any, b: any) => a.stdCost - b.stdCost); + }; + + useEffect(() => { + if (rateFeeRes?.length >= 0) { + fetchData(rateFeeRes).then((data) => { + setRateFeeTable(data); + }); + } + }, [rateFeeRes]); + + return ( + <> + <ListDiv className="min-w-[600px] h-[calc(100vh-60vh)] overflow-auto"> + <div className="sticky top-0 z-10 grid grid-cols-6 gap-4 p-4 bg-white"> + <h3 className="col-span-6 text-2xl font-medium text-label"> + 요율표 보기 + </h3> + <hr className="col-span-6 border-gray-300" /> + + <div className="col-span-5 flex items-center"> + <Label className=" text-xl text-gray-600">기준년도</Label> + <Input + className="col-span-1 text-2xl text-right ml-2 w-32" + placeholder="" + type="text" + onChange={(e) => {}} + value={new Date().getFullYear() || ""} + /> + </div> + <div className="col-span-6 pr-6"> + <UiTable + className="min-w-full pr-12" + key={"rateTable" + reateFeeTable?.length} + headName={""} + tableData={reateFeeTable || []} + headlist={coRvw.rateFeeHeadList} + onPageChange={() => {}} + pageSize={0} + total={0} + cellClick={undefined} + /> + </div> + </div> + </ListDiv> + </> + ); +} diff --git a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx new file mode 100644 index 000000000..1fa8fa8f3 --- /dev/null +++ b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx @@ -0,0 +1,107 @@ +import { useWrStore } from "@/app/store"; +import { coRegExcel } from "@/app/store/matDmd/coSttn/headList"; +import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn"; +import { excelToLdrmntData, names } from "@/app/store/matDmd/ldrmntCoSttn/excel"; +import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui"; +import { Form, FormField } from "@common/business_components/ui/form"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import type { z } from "zod"; + +export default function CoRegExcel() { + + + const form = useForm<z.infer<typeof coRegExcel>>({ + resolver: zodResolver(coRegExcel), + }); + const { ldrmntRegData, ldrmntDelete } = useWrStore((state) => state); + const [excelData, setExcelData] = useState<customLdrmnt[]>(); + + + const [file, setFiles] = useState<File | undefined>(); + + const onSubmit = (values: z.infer<typeof coRegExcel>) => { + ldrmntDelete(); + if (file) { + filePicked(file, 0).then((res) => { + try { + console.log("res", res) + let array: { [key: string]: string | number }[] = [] + if (Array.isArray(res)) + res.forEach((item, index) => { + let object: { [key: string]: string | number } = {} + Object.keys(names).forEach((key) => { + if (item && names[key] !== undefined) { + if (key === "공장관리번호") + item[key] = String(item[key]) + object[names[key] as string] = item[key]; + } + + }) + array.push(object) + + if (index != 0 && index % 5000 == 0) { + ldrmntRegData(array); + console.log("array", array) + array = [] + array.length = 0; + // throw new Error("error") + } + }) + ldrmntRegData(array); + console.log("array끝", array) + } catch (e) { + console.log(e) + } + + }); + }; + } + + + return ( + <> + <div> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <Title title={"잠재수요처 등록"}> +
+
+

+ {"잠재수요처 파일"} +

+ + +
+ +
+ + ( + + { + console.log("files", files) + if (files && files?.length === 1) setFiles(files[0]); + }} + fileTypes="" + maxFiles={1} + > + + )} + /> +
+ + +
+ + ); + } diff --git a/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx new file mode 100644 index 000000000..03c257c59 --- /dev/null +++ b/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx @@ -0,0 +1,36 @@ +import { useWrStore } from "@/app/store"; +import { UiTable } from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useEffect } from "react"; + + + +import type { + chgBuilding, + } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +export default function BdstChgModal({ chgBuilding }: { chgBuilding:chgBuilding[] }) { + +const {bdst,rain} = useWrStore((state) => { + return state; +}); +const { bdstChgHeadList } = bdst; + console.log("chgBuilding", chgBuilding); + + + return ( + <> + + {}} + headName={"○건축물 변경 이력"} + tableData={chgBuilding} + children={undefined} + cellClick={undefined} + /> + + + ); +} diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx new file mode 100644 index 000000000..315f9397f --- /dev/null +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -0,0 +1,155 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { + selectClass, + type customRainBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { ScrollArea, SelectBox, UiTable, type SelectDataType } from "@common/business_components"; +import { Button, Input, Label } from "@common/components"; +import { useEffect, useState } from "react"; +import { getCity, tableData } from "@/components/lib/func/commom"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import RainRegForm from "@/components/ui/form/fcltReg/rainReg/rainRegForm"; +import RswtrRegForm from "@/components/ui/form/fcltReg/rswtrReg/rswtrRegForm"; +import HtdflRegForm from "@/components/ui/form/fcltReg/htdflReg/htdflRegForm"; + +export type objSearchType = { + sgg: string|number; + city: string|number; + }; +//RNB(빗물), RST(중수도), HTB(온배수) + type MoType = 'RNB' | 'RST' | 'HTB'; // 여기에 'htdf' 추가 + + type BdstTypeMap = { + RNB: customRainBdst; + RST: customRswtBdst; + HTB: customRswtBdst; // 해야해// 아직 아노딤 +}; + + interface BdstRegProps { + moType: T; +} + +export function BdstReg({ moType }: BdstRegProps) { + const { rain, code, bdst,findBdst } = useWrStore((state) => { + return state; + }); + const [bdstTable, setbdstTable] = useState(); + const [rainBdstForm, setRainBdstForm] = useState({} as BdstTypeMap[T]); + const [searchTxt, setSearchTxt] = useState() + const [selectSgg, setSelectSgg] = useState() + const [objSearch, setObjSearch] = useState({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); + getSgg(data[0]?.val || ""); + setObjSearch({ city: cityValue, sgg: '' }); + }; + + const getSgg = (val: string | number) => { + console.log(val); + setObjSearch({...objSearch,sgg: String(val)}) + }; + + const searchBdst=(searchTxt: string | undefined)=>{ + let data =objSearch.city; + if(objSearch.city==="" ){ + + data = String(code.cityCd[0]?.val) + } + if(objSearch.sgg !=="" ){ + data = objSearch.sgg + } + + //RNB(빗물), RST(중수도), HTB(온배수) + findBdst(String(data), searchTxt || "",moType ) + + } + + useEffect(() => { + if (code.sggCd.length > 0) { + + setbdstTable( tableData(bdst.bdstBase, { code })); + } + }, [ bdst]); + + + const tableClick: (val: number | undefined , headId:string) => void = (val ,headId) => { + + if(val!==undefined && bdst?.bdstBase.length>0){ + const data:BdstTypeMap[T] =bdst?.bdstBase[val] + if (data) { + setRainBdstForm(data); + } + + + } + + } + + useEffect(()=>{ + setbdstTable([]) // 초기화 작업 + },[]) + return ( + <> +
+ + {""} + + + {" "} + + + setSearchTxt(e.target.value)}/> + +
+
+
+

건축물 목록

+ +
+ (tableClick(idx, headId || ""))} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }}> + +
+
+
+ +
+ { moType === 'RNB'? + + :moType==='RST' + ? + : + } + +
+
+
+ + ); +} 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..92b40e146 --- /dev/null +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -0,0 +1,148 @@ +"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/common/type/header"; +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"; + +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(()=>{ + 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..b930a36bb --- /dev/null +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -0,0 +1,119 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { + type customPbnst, + type PbnstRegArrrayType, + type 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"; +import { useFieldArray, type UseFormReturn } from "react-hook-form"; +import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; + +export function PbnstRegModal({ + data, + form, +}: { + data: pubHasuType; + form: UseFormReturn<{ items: PbnstRegArrrayType }>; +}) { + const { pbnst, pbnstInsert } = useWrStore((state) => { + return state; + }); + const [pbnstBdstForm, setPbnstBdstForm] = useState<customPbnst>( + {} as customPbnst + ); + const [currentTab, setCurrentTab] = useState<string>("mng"); + const [indexMng, setIndexMng] = useState<number>(0); + const [indexBiz, setIndexBiz] = useState<number>(0); + const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { + + const item = valuse.items[indexMng]; + const data = item ? writeRgtrAndMdfr([item]) : []; + + if(data && data[0]){ + pbnstInsert(data[0]) + } + }; + + const handleTabChange = (value: string) => { + setCurrentTab(value); + }; + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + + + useEffect(() => { + if (data) { + + const firstForm = form.getValues("items"); + setIndexMng(firstForm.length - 1) } + }, [data, form]); + + + + useEffect(() => { + console.log(' form.getValues() ' ,form.getValues()) + + }, [form.getValues()]); + + 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={ form.handleSubmit(onSubmit)} + size={"sm"} + className="!justify-end" + > + 저장 + </Button> + </TabsList> + <TabsContent className="max-h-full" value="mng"> + <PbnstMngForm + form={form} + formType={"reg"} + firstFcltData={pbnstBdstForm} + indexMng={indexMng} + setIndexMng={setIndexMng} + /> + </TabsContent> + <TabsContent className="max-h-full" value="buse"> + <PbnstBuseForm + form={form} + indexMng={indexMng} + indexBiz={indexBiz} + setIndexBiz={setIndexBiz} + formType={"reg"} + firstBizData={pbnstBdstForm.fcBizs} + /> + </TabsContent> + </Tabs> + </ScrollArea> + </> + ); +} diff --git a/apps/wr/components/ui/modal/ldrmntSttn/ldrmntExcelModal.tsx b/apps/wr/components/ui/modal/ldrmntSttn/ldrmntExcelModal.tsx new file mode 100644 index 000000000..5427c5dd2 --- /dev/null +++ b/apps/wr/components/ui/modal/ldrmntSttn/ldrmntExcelModal.tsx @@ -0,0 +1,67 @@ +import { useWrStore } from "@/app/store"; +import { pbnPrfRegExcelZod } from "@/app/store/prfmncReg/pbnstPrfmncReg/headList"; +import type { FcPrfType, PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +import { yearOptions } from "@/components/lib/func/commom"; +import { FileUpload, Form, FormField, FormUi, SelectBox, Title } from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import type { z } from "zod"; + +export default function LdrmntExcelModal({ data }: { data: FcPrfType }) { + const { prfmnRegData } = useWrStore((state) => state); + const [excelData, setExcelData] = useState<PrfType[]>(); + const form = useForm<z.infer<typeof pbnPrfRegExcelZod>>({ + resolver: zodResolver(pbnPrfRegExcelZod), + defaultValues: { + ym: String(new Date().getFullYear()), + }, + }); + const onSubmit = (values: z.infer<typeof pbnPrfRegExcelZod>) => { + if (excelData) { + prfmnRegData(excelData as unknown as PrfType); + } + }; + const [files, setFiles] = useState<File[] | null>(null); + return ( + <> + <div> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <Title title={"잠재수요처 등록"}> +
+
+

+ {"잠재수요처 파일"} +

+ + +
+
+ ( + + { + if (files) setFiles(files); + }} + fileTypes="xlsx" + > + + )} + /> +
+ + +
+ + ) + +} \ No newline at end of file diff --git a/apps/wr/components/ui/modal/sttn/headChModal/headChModal.tsx b/apps/wr/components/ui/modal/sttn/headChModal/headChModal.tsx new file mode 100644 index 000000000..83a5a3523 --- /dev/null +++ b/apps/wr/components/ui/modal/sttn/headChModal/headChModal.tsx @@ -0,0 +1,87 @@ +import { useWrStore } from "@/app/store"; +import { sttnHeadListType } from "@/app/store/ruseFcltMng/fcltSttn/rainSttn/headList"; +import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@common/business_components/ui"; +import { Button, Checkbox } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +export default function HeadChModal({headList, type }:{headList : z.infer ,type:string }) { + const { rainChSttnHead , rswtChSttnHead, htdflChSttnHead, pbnstChSttnHead} = useWrStore((state) => state); + + + let items = headList.map(item => ({id:item.id, title:item.title, val:item.visible })); + + + const form = useForm>({ + resolver: zodResolver(sttnHeadListType), + defaultValues: [], + }) + + + useEffect(() =>{ + form.reset(headList) + + + },[headList]) + + const onSubmit = (valueList : z.infer ) =>{ + if(type==='RNB'){ + rainChSttnHead(valueList) + }else if(type=== 'RST'){ + rswtChSttnHead(valueList) + }else if(type === 'HTB'){ + htdflChSttnHead(valueList) + }else if(type === 'PBN'){ + pbnstChSttnHead(valueList) + } + } + + return ( + <> +
+
+ + +
+

조회항목 설정

+
+ {items.map((item,index) => ( + + { + return ( + + + + { + field.onChange(checked); + return; + } + } + /> + + + {item.title} + + + ); + }} + /> + ))} + + + +
+ + ); +} diff --git a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx new file mode 100644 index 000000000..d20d10591 --- /dev/null +++ b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx @@ -0,0 +1,333 @@ +import { useWrStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { + initValInUsage, + initValOutUsage, + subTotalObj, + totalObj, + type PrfType, +} from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +import { + pbnstRegArrayZod, + pbnstRegZod, + type customPbnst, + type fcBizType, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; +import { ScrollArea, Title, UiTable } from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { + Tabs, + TabsList, + TabsTrigger, + Button, + TabsContent, +} from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { set, useForm } from "react-hook-form"; +import { z } from "zod"; + +export function PbnstSttnModal({ data }: { data: customPbnst }) { + const { code, pbnstSttn, prfmnc } = useWrStore((state) => state); + const [fcltTab, setFcltTab] = useState("fclt"); + const [ruseTab, setRuseTab] = useState("inoutTotal"); + const [ruseBizData, setRuseBizData] = useState<{ + data: any[]; + head: TableUpperProps[]; + }>(); + const [ruseHeadList, setRuseHeadList] = useState([]); + const [ruseTableData, setRuseTableData] = useState([]); + const [prfData, setPrfData] = useState<{ + inPrf: any[]; + outPrf: any[]; + totalPrf: any[]; + }>(); + const [indexMng, setIndexMng] = useState(0); + const [indexBiz, setIndexBiz] = useState(0); + const form = useForm<{ + items: z.infer[]; + }>({ + resolver: zodResolver(z.object({ items: pbnstRegArrayZod })), + defaultValues: { items: pbnstRegArrayZod.parse([]) }, + mode: "onChange", + }); + + + const { monthHeadList } = prfmnc; + useEffect(() => { + let tableData; + if (fcltTab === "fclt") { + tableData = data.fcBizs?.map((item: fcBizType) => { + return { + cycl: item.cycl + "차", + seCd: item.seCd, + gvsbs: item.gvsbs ?? "-", + lcep: item.lcep ?? "-", + cusrAlotm: item.cusrAlotm ?? "-", + etc: item.etc ?? "-", + totalMoney: + Number(item.addr) + + Number(item.gvsbs) + + Number(item.lcep) + + Number(item.cusrAlotm), + instlAprvSncnDt: item.instlAprvSncnDt?.toISOString ?? "-", + bizCmcnDay: item.bizCmcnDay?.toISOString ?? "-", + rprcsFcltCpct: item.rprcsFcltCpct ?? "-", + rprcsMthdCd: item.rprcsMthdCd ?? "-", + majorPrcsMsrcCd: item.majorPrcsMsrcCd ?? "-", + inoutYn: item.inoutYn ?? "-", + addr: item.addr ?? "-", + mainSplyPumpCpct: item.mainSplyPumpCpct ?? "-", + }; + }); + } else if (fcltTab === "sply") { + tableData = data.fcBizs.flatMap((item: fcBizType) => { + return item?.fcOrgwts?.map((fcOrgwt) => { + return { + cycl: item.cycl + "차", + orgwtWtritQnt: fcOrgwt.orgwtWtritQnt, + tmsYn: fcOrgwt.tmsYn, + dschgFmYn: fcOrgwt.dschgFmYn, + dtlPstnExpln: fcOrgwt.dtlPstnExpln, + }; + }); + }); + } else if (fcltTab === "ppi") { + tableData = data.fcBizs?.flatMap((item: fcBizType) => { + return item?.pplInstls?.map((ppl) => { + return { + cycl: item.cycl + "차", + ppMqlt: ppl.ppMqlt, + pdmt: ppl.pdmt, + cpct: ppl.cpct, + }; + }); + }); + } else if (fcltTab === "guarantee") { + tableData = data.fcBizs?.map((item: fcBizType) => { + return { + cycl: item.cycl + "차", + bod: item.bod, + grssClfg: item.grssClfg, + combResdch: item.combResdch, + trbt: item.trbt, + smll: item.smll, + crmty: item.crmty, + tph: item.tph, + + tn: item.tn, + ph: item.ph, + cdtnv: item.cdtnv, + }; + }); + } else if (fcltTab === "dmdCo") { + tableData = data.fcBizs?.flatMap((item: fcBizType) => { + + return item?.fcDemands?.map((dmd) => { + return { + cycl: item.cycl + "차", + dmdCoNm: dmd.dmdCoNm, + daddr: dmd.daddr, + useUsgCd: dmd.useUsgCd, + splyBgngDay: dmd.splyBgngDay?.toISOString, + splyEndDay: dmd.splyEndDay?.toISOString, + splyCpct: dmd.splyCpct, + splyFmYn: dmd.splyFmYn, + }; + }); + }); + } + console.log(tableData); + if (tableData && tableData.length > 0) { + setRuseTableData(tableData as any[]); + } + }, [fcltTab, data]); + useEffect(() => { + if (fcltTab === "fclt") { + setRuseHeadList(pbnstSttn.ruseFcltHeadList); + } else if (fcltTab === "sply") { + setRuseHeadList(pbnstSttn.ruseSplyHeadList); + } else if (fcltTab === "ppi") { + setRuseHeadList(pbnstSttn.rusePplHeadList); + } else if (fcltTab === "guarantee") { + setRuseHeadList(pbnstSttn.ruseGuranteeHeadList); + } else if (fcltTab === "dmdCo") { + setRuseHeadList(pbnstSttn.ruseDmdHeadList); + } + }, [ + fcltTab, + pbnstSttn.ruseFcltHeadList, + pbnstSttn.ruseSplyHeadList, + pbnstSttn.rusePplHeadList, + pbnstSttn.ruseGuranteeHeadList, + pbnstSttn.ruseDmdHeadList, + ]); + + const getTablePrfData = (prfList: PrfType[]) => { + const inUsageData = structuredClone(initValInUsage); + const outUsageData = structuredClone(initValOutUsage); + const inSubtotal = structuredClone(subTotalObj); + const outSubtotal = structuredClone(subTotalObj); + + if (prfList) { + prfList?.forEach((prfItem) => { + const month = Number(prfItem?.ym?.slice(4)); // "MM" + const monthKey = `${month}월`; + + inUsageData.forEach((inData: any) => { + const usageKey = inData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + inData[monthKey] = value; + inData.total += value; + + if (inSubtotal && usageKey !== "subtotal") { + (inSubtotal as any)[monthKey] = + ((inSubtotal as any)[monthKey] ?? 0) + value; + inSubtotal.total += value; + } + }); + + outUsageData.forEach((outData: any) => { + const usageKey = outData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + outData[monthKey] = value; + outData.total += value; + + if (usageKey !== "subtotal") { + (outSubtotal as any)[monthKey] = + ((outSubtotal as any)[monthKey] ?? 0) + value; + outSubtotal.total += value; + } + }); + }); + inUsageData.push(inSubtotal); + outUsageData.push(outSubtotal); + const usageTotalObj: { [key: string]: any } = structuredClone(totalObj); + for (const key in usageTotalObj) { + if (["usage", "gubun", "edit"].includes(key)) continue; + const val = + (Number((inSubtotal as any)[key]) || 0) + + (Number((outSubtotal as any)[key]) || 0); + + (usageTotalObj as any)[key] = val; + } + const tableData = [...inUsageData, ...outUsageData, usageTotalObj]; + + setPrfData({ + inPrf: inUsageData, + outPrf: outUsageData, + totalPrf: tableData, + }); + } + }; + useEffect(() => { + if (pbnstSttn.pbnstSttnRes && data) { + const prfList = pbnstSttn.pbnstSttnRes.find( + (item) => item.pbnstRuseFcltPk === data.pbnstRuseFcltPk + )?.prfList; + + if (prfList) {console.log("prfList", prfList); + + getTablePrfData(prfList); + } + } + }, [pbnstSttn.pbnstSttnRes, data]); + + + useEffect(()=>{ + + if(data){ + form.reset({items:[data]}) + } + + },[data, form]) + + + useEffect(()=>{ + console.log("prfData", prfData); + },[prfData]) + return ( + <> +
+ + <ScrollArea className={"h-[70vh]"}> + <PbnstMngForm + form={form} + formType={"sttn"} + indexMng={indexMng} + setIndexMng={setIndexMng} + /> + <Tabs defaultValue="fclt" onValueChange={setFcltTab}> + <TabsList className=""> + <TabsTrigger className="!text-xl" value="fclt"> + 하수처리수 재이용시설 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="sply"> + 재이용 원수 취수 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="ppi"> + 재이용 공급관로 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="guarantee"> + 재이용 보증수질 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="dmdCo"> + 재이용 수요처 현황 + </TabsTrigger> + </TabsList> + <TabsContent className="max-h-full" value={fcltTab}> + <UiTable + headName={undefined} + tableData={ruseTableData || []} + headlist={ruseHeadList || []} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }} + children={ + fcltTab === "fclt" ? ( + <HeadMakeColSpan upHeadList={pbnstSttn.ruseFcltNodeList} /> + ) : undefined + } + ></UiTable> + </TabsContent> + </Tabs> + + <Tabs onValueChange={setRuseTab}> + <TabsList className=""> + <TabsTrigger className="!text-xl" value="totalPrf"> + 장내외합계 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="inPrf"> + 장내용수 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="outPrf"> + 장외용수 + </TabsTrigger> + </TabsList> + <TabsContent className="max-h-full" value={ruseTab}> + {prfData && ( + <UiTable + headName={undefined} + tableData={prfData[ruseTab as keyof typeof prfData] || []} + headlist={monthHeadList || []} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }} + ></UiTable> + )} + </TabsContent> + </Tabs> + </ScrollArea> + </div> + </> + ); +} diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx new file mode 100644 index 000000000..edffcc71d --- /dev/null +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx @@ -0,0 +1,800 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn"; +import { ldrmntZod } from "@/app/store/matDmd/ldrmntCoSttn/headList"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { handleCommaNumberChange } from "@/components/lib/func/commom"; +import { DatePicker, Form, FormField, FormUi, ScrollArea } from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import type { z } from "zod"; + +export default function SttnDetailModal({ + formObj, +}: { + formObj: any; +}) { + const { ldrmntUpdate } = useWrStore( + (state) => state + ); + const defaultValues: z.infer<typeof ldrmntZod> = ldrmntZod.parse({}); + const form = useForm<z.infer<typeof ldrmntZod>>({ + resolver: zodResolver(ldrmntZod), + defaultValues: defaultValues, + }); + + const onSubmit = (values: customLdrmnt) => { + console.log("values", values) + if (values) + ldrmntUpdate(values) + }; + + useEffect(() => { + if (ldrmntZod) form.reset(formObj); + + }, [formObj]); + return ( + <div> + <ScrollArea className="h-[85vh]"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div className="grid grid-cols-12 flex items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10"> + 잠재수요처 상세정보 + </h1> + <Button type="submit" className="col-start-12 col-span-1" size={"sm"}> + 저장 + </Button> + </div> + + <FormField + control={form.control} + name="fctryMngSn" + render={({ field }) => ( + <FormUi label=""> + <Input + className={selectClass} + placeholder="" + type="hidden" + onChange={(e) => { + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="dmdCoNm" + render={({ field }) => ( + <FormUi label="회사명"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="mngInstNm" + render={({ field }) => ( + <FormUi label="관리기관"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="fctrySe" + render={({ field }) => ( + <FormUi label="공장구분"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="cmpxNm" + render={({ field }) => ( + <FormUi label="단지명"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="fndnSe" + render={({ field }) => ( + <FormUi label="설립구분"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="mvnShp" + render={({ field }) => ( + <FormUi label="입주형태"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="hldSe" + render={({ field }) => ( + <FormUi label="보유구분"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? "" ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="frstAprvYmd" + render={({ field }) => ( + <FormUi label="최초승인일"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="frstRegYmd" + render={({ field }) => ( + <FormUi label="최초등록일"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="regSe" + render={({ field }) => ( + <FormUi label="등록구분"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="regYmd" + render={({ field }) => ( + <FormUi label="등록일"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="telno" + render={({ field }) => ( + <FormUi label="전화번호"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + + <div className="grid grid-cols-5 gap-2 mt-4"> + <div className="col-span-1"> + <FormField + control={form.control} + name="maleEmpCnt" + render={({ field }) => ( + <FormUi label="남자종업원"> + <Input + className={selectClass} + placeholder="" + type="number" + onChange={(e) => { + // field.onChange(e.target.value); + handleCommaNumberChange(e, field.onChange); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-1"> + <FormField + control={form.control} + name="fmaleEmpCnt" + render={({ field }) => ( + <FormUi label="여자종업원"> + <Input + className={selectClass} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-1"> + <FormField + control={form.control} + name="frgnrMaleEmpCnt" + render={({ field }) => ( + <FormUi label="외국인남자종업원"> + <Input + className={selectClass} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-1"> + <FormField + control={form.control} + name="frgnrFmaleEmpCnt" + render={({ field }) => ( + <FormUi label="외국인여자종업원"> + <Input + className={selectClass} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-1"> + <FormField + control={form.control} + name="totalEmpCnt" + render={({ field }) => ( + <FormUi label="종업원합계"> + <Input + className={selectClass} + placeholder="" + type="number" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? 0} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="prdctnItem" + render={({ field }) => ( + <FormUi label="생산품"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="splyMtrl" + render={({ field }) => ( + <FormUi label="원자재"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="fctryScl" + render={({ field }) => ( + <FormUi label="공장규모"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="usgRgn" + render={({ field }) => ( + <FormUi label="용도지역"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="ldcg" + render={({ field }) => ( + <FormUi label="지목"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="usgRgnArea" + render={({ field }) => ( + <FormUi label="용지면적"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="mnftrFcltArea" + render={({ field }) => ( + <FormUi label="제조시설면적"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="sbsdFcltArea" + render={({ field }) => ( + <FormUi label="부대시설면적"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="archArea" + render={({ field }) => ( + <FormUi label="건축면적"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="knwlIndstCntrNm" + render={({ field }) => ( + <FormUi label="지식산업센터명"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="rprsTpbiz" + render={({ field }) => ( + <FormUi label="대표업종"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="tpbizNm" + render={({ field }) => ( + <FormUi label="업종명"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="tpbizCd" + render={({ field }) => ( + <FormUi label="업종코드"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="cycl" + render={({ field }) => ( + <FormUi label="차수"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="corpAddr" + render={({ field }) => ( + <FormUi label="법인주소"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="esntlIdx" + render={({ field }) => ( + <FormUi label="필지수"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="fctryMngNo" + render={({ field, fieldState }) => ( + <FormUi label="공장관리번호"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="fctryAddr" + render={({ field }) => ( + <FormUi label="공장주소"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-2"> + <FormField + control={form.control} + name="fctryLotnoAddr" + render={({ field }) => ( + <FormUi label="공장주소_지번"> + <Input + className={selectClass} + placeholder="" + type="text" + onChange={(e) => { + field.onChange(e.target.value); + }} + value={field.value ?? ""} + /> + </FormUi> + )} + /> + </div> + </div> + + + + </form> + </Form> + </ScrollArea> + </div> + ); +} diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx new file mode 100644 index 000000000..0f7dbf706 --- /dev/null +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx @@ -0,0 +1,120 @@ +"use client"; +import { ScrollArea, Title, UiTable } from "@common/business_components/ui"; +import RainRegForm from "../../../form/fcltReg/rainReg/rainRegForm"; +import RswtrRegForm from "../../../form/fcltReg/rswtrReg/rswtrRegForm"; +import HtdflRegForm from "../../../form/fcltReg/htdflReg/htdflRegForm"; +import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import { useWrStore } from "@/app/store"; +import { useEffect, useState } from "react"; +import type { CommonTableProps } from "@/app/store/common/type/header"; +import { rainChkTableData, rswtChkTableData } from "@/components/lib/func/TableData"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; + +export default function SttnDetailModal({ + type, + formObj, +}: { + type: string; + formObj: any; +}) { + const { code, rainSttn, rswtSttn, rainSttnChkSearch,rswtChkPolAndGuide } = useWrStore( + (state) => state + ); + const [detailData, setDetailData] = useState({ + head: [] as CommonTableProps[], + node:[] as CommonTableProps[], + data: {} as any, + }); + + useEffect(() => { + if (type === "RNB" && formObj.reuseBuilding[0]) { + rainSttnChkSearch(formObj.reuseBuilding[0].bdstRuseFcltPk); + + } else if (type === "RST") { + // rswtChkPolAndGuide(formObj.reuseBuilding[0].bdstRuseFcltPk) + } + }, [type, formObj]); + + useEffect(() => { + if ( + type === "RNB" && + formObj.reuseBuilding[0] && + rainSttn.rainChkRes?.bdstRuseFcltFk + ) { + const tableTotalData = rainChkTableData([rainSttn.rainChkRes]); + const data = tableTotalData.map((item) => { + const obj = { + totalSplyUswtrUtztnQnt: item.totalSplyUswtrUtztnQnt, + totalRuseFcltUtztnQnt: item.totalRuseFcltUtztnQnt, + ph: item.ph, + trbt: item.trbt, + grssClfg: item.grssClfg, + etc: item.etc, + vltnDt: item.vltnDt, + vltnMttr: item.vltnMttr, + levyAmt: item.levyAmt, + }; + return obj; + }); + setDetailData({ + head: rainSttn.rainSttnDetailHeadList, + node:rainSttn.rainSttnDetailNodeList, + data: data, + }); + } else if (type === "RST" && rswtSttn.rswtSttnRes) { + // const tableTotalData = rswtChkTableData(rswtSttn.rswtSttnRes as unknown as rswtChkZodType[]); + + // setDetailData({ + // head: rswtSttn.rswtSttHeadList, + // node:rswtSttn.rswtSttNodeList, + // // data: tableTotalData, + // }); + + + } + }, [rainSttn.rainChkRes]); + + useEffect(() =>{ + setDetailData({data:[] , head:[], node:[]}) + + },[]) + + return ( + + <div> + <Title title={ type === "RNB" ? "빗물이용시설 상세정보" + : type === "RST" ? "중수도시설 상세정보" + : "온배수시설 상세정보"}> + 0?"h-[80vh]":"h-[70vh]"} > + {type === "RNB" ? ( + + ) : type === "RST" ? ( + + ) : ()} + + {detailData.data.length>0? + } + + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }} + >:<>} + + +
+ ); +} diff --git a/apps/wr/components/ui/publicReuseInsertForm.tsx b/apps/wr/components/ui/publicReuseInsertForm.tsx deleted file mode 100644 index a521e8a59..000000000 --- a/apps/wr/components/ui/publicReuseInsertForm.tsx +++ /dev/null @@ -1,497 +0,0 @@ -"use client"; - -import { SelectBox } from "@common/business_components"; -import { DatePicker, 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 { useState } from "react"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { useWrStore } from "../../app/store/index"; -import { selectClass, type TableUpperProps } from "@/app/store/rainReuseFacilityInsert"; - -const formSchema = z.object({ - username: z.string().min(2).max(50), - facilityName: z.string().min(2).max(50), -}); - - - const headMakeColSpan = (upHeadList: TableUpperProps[]) => { - return (<> - { - upHeadList.map((head: TableUpperProps) => - (!head.upSequnce ? {head.title} : - head.upName === 'upChangeRe' && head.upSequnce === 1 ? {'운영대행기간'} : null))} - - - {upHeadList.map((head: TableUpperProps) => - (head.upName === 'upChangeRe' ? {head.title} : null))} - - ) -} -export function PublicReuseFaciltyFormInsert() { - const { pubFac } = useWrStore((state) => state); - const { pubMngUpHeadList, pubMngFacData,erctUswtrCd,cyclCd,seCd,buseCd,rprcsCd,buseMet,rprcLoCd} = pubFac; -const nodeList = headMakeColSpan(pubMngUpHeadList); - - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - username: "", - }, - }); - - // 2. Define a submit handler. - function onSubmit(values: z.infer) { - // Do something with the form values. - // ✅ This will be type-safe and validated. - console.log(values); - } - const onSelectValue=(val:string)=>{ - console.log(val) - -} - - const [instlAprvDate, setInstlAprvDate] = useState(new Date()) - const [bizCmcnDate, setBizCmcnDate] = useState(new Date()) - const [files, setFiles] = useState(null); - - return ( -
-
-
- -
-
-

○ 재이용시설 운영정보

- ( - - - - )} - /> -
- - -
-
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- - -
-

○ 재이용시설 운영정보

- -
-
- {nodeList} -
-
-

○ 재이용수 공급 가능량

-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
-
-
- -
- {/* 차수별 등록 현황 폼 */} -
-
- -
-

- ○ 차수별 하수처리수 재이용 시설 현황 -

-
-

- 등록차수 -

- ( - - - - )} - /> - -
-
-
-

♦︎하수처리수 재이용 시설 현황

-

- 사업개요{" "} -

-

1. 현황

-
- -
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
- ( - - { - if (date) setInstlAprvDate(date); - } - }} mode="single" {...field}/> - - )} - /> -
-
- ( - - - - )} - /> -
-
-

2. 사업비

-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
-

- 재처리시설{" "} -

-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
- -
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
- -

- 농축수관리{" "} -

-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
- -
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
- -
-
- -
-
- ); -} diff --git a/apps/wr/components/ui/rainReuseInsertForm.tsx b/apps/wr/components/ui/rainReuseInsertForm.tsx deleted file mode 100644 index 57e30c551..000000000 --- a/apps/wr/components/ui/rainReuseInsertForm.tsx +++ /dev/null @@ -1,240 +0,0 @@ -"use client"; - -import { - Form, - FormField, - SelectBox, -} from "@common/business_components/ui"; -import { Input } from "@common/components/ui"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { FormUi } from "@common/business_components"; -import { useWrStore } from "@/app/store"; -import { selectClass } from "@/app/store/rainReuseFacilityInsert"; - -const formSchema = z.object({ - username: z.string().min(2).max(50), - facilityName: z.string().min(2).max(50), -}); - - -export function RainReuseFaciltyInsertForm() { - const { rain } = useWrStore((state) => state); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - username: "", - }, - }); - - // 2. Define a submit handler. - function onSubmit(values: z.infer) { - // Do something with the form values. - // ✅ This will be type-safe and validated. - console.log(values); - } - - const onSelectValue=(val:string)=>{ - console.log(val) - - } - return ( -
-
- - {/*
*/} -
-
-

○ 빗물이용시설 운영정보

-
-
-

- 기본정보 -

-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
-
-

- 빗물이용시설 정보 -

-
-
-

✓ 1. 집수면

-
-
-
- ( - - - - )} - /> -
-
- ( - - - - - )} - /> -
-
- -
-

✓ 2. 여과 등 처리시설

-
-
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
- -
-
-

- 인센티브 정보 -

-
-
-

✓ 1. 설치비 지원

-
-
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- -
-

✓ 2. 녹색건축물 인증

-
-
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- - -
- ); -} diff --git a/apps/wr/components/ui/rswtrReuseInsetForm.tsx b/apps/wr/components/ui/rswtrReuseInsetForm.tsx deleted file mode 100644 index e4a540f20..000000000 --- a/apps/wr/components/ui/rswtrReuseInsetForm.tsx +++ /dev/null @@ -1,240 +0,0 @@ -"use client"; - -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, - FormUi, - SelectBox, -} from "@common/business_components/ui"; -import { Input } from "@common/components/ui"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { useWrStore } from "@/app/store"; -import { selectClass } from "@/app/store/rainReuseFacilityInsert"; - -const formSchema = z.object({ - username: z.string().min(2).max(50), - facilityName: z.string().min(2).max(50), -}); - - -export function RswtrReuseFaciltyInsertForm() { - const { rswt } = useWrStore((state) => state); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - username: "", - }, - }); - - // 2. Define a submit handler. - function onSubmit(values: z.infer) { - // Do something with the form values. - // ✅ This will be type-safe and validated. - console.log(values); - } - - const onSelectValue=(val:string)=>{ - console.log(val) - - } - return ( -
-
- - {/*
*/} -
-
-

○ 중수도 시설 운영정보

-
-
-

- 기본정보 -

-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
-
-
- ( - - - - )} - /> -
- -
- ( - - - - )} - /> -
-
-
-

- 중수도 시설 정보 -

-
- -
-
- ( - - - - )} - /> -
-
- ( - - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- -
-

- 인센티브 정보 -

-
-
-

✓ 1. 설치비 지원

-
-
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- -
-

✓ 2. 녹색건축물 인증

-
-
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
-
- - -
- ); -} diff --git a/apps/wr/components/ui/searchInput/searchInput.tsx b/apps/wr/components/ui/searchInput/searchInput.tsx new file mode 100644 index 000000000..796c8c298 --- /dev/null +++ b/apps/wr/components/ui/searchInput/searchInput.tsx @@ -0,0 +1,110 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; + +type SearchType = { + searchNm: string; + searchPk: string; +}; +type thisProps = { + label: string | undefined; + initValue: string | undefined; + setValue: (searchNm?: string) => void; + setRtnData: (rtnData: any) => void; + searchList?: any[]; +}; +export default function SearchInputText({ + label = undefined, + initValue = undefined, + setValue = () => {}, + setRtnData = () => {}, + searchList = undefined, +}: thisProps) { + + const [isOpen, setIsOpen] = useState(false); + const [textValue, setTextValue] = useState(initValue || ""); + + const wrapperRef = useRef(null); + const handleInputChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + setTextValue(value); + setValue(value); + if (value) { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, + [setValue] + ); + + const handleSuggestionClick = useCallback( + (dmdCoNm: string) => { + setTextValue(dmdCoNm); + setValue(dmdCoNm); + setIsOpen(false); + setRtnData(searchList?.find((item) => item.dmdCoNm === dmdCoNm) || {}); + }, + [searchList, 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); + }; + }, []); +useEffect(() =>{ + console.log('searchList ', searchList) +},[searchList]) + + return ( +
+ setIsOpen(true)} */ + className="w-full h-[3.2rem] !text-xl pl-3 text-gray-600 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder={`${label}을(를) 입력하세요`} + /> + {isOpen && ( +
+
+ {searchList && searchList.length > 0 ? ( + searchList.map((item: SearchType, index: number) => ( +
handleSuggestionClick(item.searchNm)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + handleSuggestionClick(item.searchNm); + } + }} + > + {item.searchPk?.substring(0, 10)}  + {item.searchNm} +
+ )) + ) : ( +
+ 검색 결과가 없습니다. +
+ )} +
+
+ )} +
+ ); +} diff --git a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx new file mode 100644 index 000000000..94be4ebb6 --- /dev/null +++ b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx @@ -0,0 +1,55 @@ +"use client"; +import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { useWrStore } from "@/app/store"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { +} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { InputTableBody } from "@/components/lib/func/TableData"; +import { + Input, + Table, + TableBody, + TableCell, + TableHeader, + TableRow, +} from "@common/components/ui"; +import { useEffect, useState, type Dispatch, type SetStateAction } from "react"; + +export function GuideDetailsTable({ + chgRes, + headList, + type, + setChGuidData, +}: { + chgRes: any[]; + headList: TableUpperProps[]; + type: string; + setChGuidData: (chgRes: any[]) => void; +}) { + const { code, rainChkgdn } = useWrStore((state) => state); + const [inputData, setInputData] = useState(); + + useEffect(() => { + setInputData(chgRes); + }, [chgRes]); + + + useEffect(() => { + setChGuidData(inputData??[] ); + + }, [inputData]); + + + return ( + <> +
+ + {headMakeprfmnc(headList, rainChkgdn?.rainHeadNm)} + + {InputTableBody(inputData ?? [],headList, setInputData) } + +
+
+ + ); +} diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx new file mode 100644 index 000000000..d0ad437b3 --- /dev/null +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -0,0 +1,189 @@ +"use client"; +import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { useWrStore } from "@/app/store"; +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { tableData } from "@/components/lib/func/commom"; +import { + chRowspanTable, + headVisibleChange, + rainChkTableData +} from "@/components/lib/func/TableData"; +import { + Table, + TableBody, + TableHeader, +} from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +export default function RainChkgdTable({ + detailUp, + handleClick=() => {}, + deleteData, + rainChkData, +}: { + detailUp: (data: rainChkZodType[]) => void; + handleClick?: (data:any) => void; + deleteData:(selectedIds:number[])=>void + rainChkData?: rainChkZodType[]; +}) { + const { code, rainChkgdn ,rainChkSttn,getRainSttnExcel } = useWrStore((state) => state); + const [makeTableData, setMakeTableData] = useState([]); + const [selectedIds, setSelectedIds] = useState([]); + const [upRaintDetail, setUpRaintDetail] = useState([]); + const [headList, setHeadList] = useState< {headList:TableUpperProps[],upHeadList:chHeadType[]}>({headList:[], upHeadList:[]}); + + + const toggleSelection = (pk: number, checked: boolean) => { + setSelectedIds((prev) => + checked ? [...prev, pk] : prev.filter((item) => item !== pk) + ); + }; + const handleChange = ( + rowIndex: number, + field: keyof (typeof makeTableData)[0], + value: string | number + ) => { + + setMakeTableData((prev) => + prev.map((row, idx) => { + return idx === rowIndex ? { ...row, [field]: value } : row + } + ) + ); + }; + + const handleCellClick= useCallback((index: number, headId: string) => { + if ( + headId === "totalSplyUswtrUtztnQnt" || + headId === "totalRuseFcltUtztnQnt" + ) { + + handleClick(makeTableData[index]) + } + + },[makeTableData, handleClick]); + + + + useEffect(() => { + setSelectedIds([])// 셀렉트 초기화를 위한 작업 + if(rainChkData){ + const rainChg = rainChkData.map((item: any) => { + const bud = tableData([item.building], { code }); + return { + ...item, + ...bud[0], + rowspan: item.rainyDetails?.length, + }; + }); + setMakeTableData(rainChkTableData(rainChg)); + } + }, [rainChkData, code]); + + useEffect(() => { + //데이터를 평탄화 한것중에 체크박스 선택한 테이블만 선택 + + //현황 페이지에서 필요한 엑셀 데이터를 바뀔때마다 넣기 위해서 + getRainSttnExcel(makeTableData); + const selectedData = makeTableData.filter((item) =>selectedIds.includes(item.chkgdnPk)); + + if (selectedData.length > 0) { + const updateTableData = rainChkgdn.rainChk + .map((rc) => { + const newRainyDetail = rc.rainyDetails + .map((rd) => { + + // 변경 데이터는 평탄화한 데이터 즉 루트에 변경되어 있음 이용용도까지 잇음 그걸로 찾기 + const findRain = selectedData.find( + (sd) => + rd.chkgdnFk === sd.chkgdnPk && rd.utztnUsgCd === sd.utztnUsgCd + ); + + if (findRain != undefined) { + console.log(findRain) + return { + ...rd, + ph: Number(findRain.ph), + trbt: Number(findRain.trbt), + grssClfg: Number(findRain.grssClfg), + etc: findRain.etc, + vltnDt: findRain.vltnDt, + vltnMttr: findRain.vltnMttr, + levyAmt: Number(findRain.levyAmt), + }; + } else { + return undefined; + } + }) + .filter(Boolean); + if (newRainyDetail[0] != undefined) { + return { + ...rc, + rainyDetails: newRainyDetail, + }; + } else { + return undefined; + } + }) + .filter(Boolean); + + + setUpRaintDetail(updateTableData as rainChkZodType[]); + } + }, [makeTableData,selectedIds]); + + useEffect(() => { + detailUp(upRaintDetail); + + }, [upRaintDetail]); + + useEffect(() =>{ + if(selectedIds.length >0){ + // 삭제를 위한 데이터 삽입 + deleteData(selectedIds ) + } + },[selectedIds]) + + + ///현황일 경우 input 비활성화 한다. + useEffect(() => { + if(rainChkSttn.isSttn){ + + setHeadList({ + headList: rainChkSttn.rainChkgdnHeadList, + upHeadList: rainChkSttn.rainHeadNm + }) + }else{ + setHeadList({ + headList: rainChkgdn.rainChkgdnHeadList, + upHeadList: rainChkgdn.rainHeadNm + }) + + } + + }, [rainChkSttn.isSttn, rainChkgdn.rainChkgdnHeadList , rainChkSttn.rainChkgdnHeadList]); + + + return ( + + + {headMakeprfmnc( + headList?.headList, + headList?.upHeadList + )} + + + + {chRowspanTable( + makeTableData, + headList.headList, + handleChange, + toggleSelection, + selectedIds, + handleCellClick + )} + +
+ ); +} diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx new file mode 100644 index 000000000..da6cacc9a --- /dev/null +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -0,0 +1,151 @@ +"use client"; +import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { useWrStore } from "@/app/store"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { + rswtChkZodType, + rswtrChkDetailsZodType, +} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import { tableData } from "@/components/lib/func/commom"; +import { + chRowspanTable, + rswtChkTableData, +} from "@/components/lib/func/TableData"; +import { Table, TableBody, TableHeader } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; + +export default function RswtChkgdTable({ + detaiRswtlUpdate, + handleRswtClick = () => {}, + deleteRswtData, + rswtChkData, +}: { + detaiRswtlUpdate: (data: rswtrChkDetailsZodType[]) => void; + handleRswtClick?: (data: any) => void; + deleteRswtData: (selectedIds: number[]) => void; + rswtChkData: rswtChkZodType[]; +}) { + const { code, rswtChkgdn,rswtChkSttn , rswtChkgdnInit, rswtChkPolAndGuide,getRswtSttnExcel } = useWrStore( + (state) => state + ); + const [headList, setHeadList] = useState< {headList:TableUpperProps[],upHeadList:chHeadType[]}>({headList:[], upHeadList:[]}); + + const [makeTableData, setMakeTableData] = useState([]); + const [selectedIds, setSelectedIds] = useState([]); + const [upRswttDetail, setUpRswttDetail] = useState( + [] + ); + const toggleSelection = (pk: number, checked: boolean) => { + setSelectedIds((prev) => + checked ? [...prev, pk] : prev.filter((item) => item !== pk) + ); + }; + const handleChange = ( + rowIndex: number, + field: keyof (typeof makeTableData)[0], + value: string | number + ) => { + setMakeTableData((prev) => + prev.map((row, idx) => { + return idx === rowIndex ? { ...row, [field]: value } : row; + }) + ); + }; + + const handleCellClick = useCallback( + (index: number, headId: string) => { + if (headId === "splyUswtrUtztnQnt" || headId === "ruseFcltUtztnQnt") { + rswtChkPolAndGuide(makeTableData[index].chkgdnPk); + } + }, + [makeTableData, handleRswtClick] + ); + useEffect(() => { + if (rswtChkgdn.rswtChkPolGuide?.length > 0) + handleRswtClick(rswtChkgdn.rswtChkPolGuide[0]); + }, [rswtChkgdn.rswtChkPolGuide]); + + useEffect(() => { + console.log("makeTableData ", makeTableData) + getRswtSttnExcel(makeTableData) + const selectedData = makeTableData + .map((item) => { + return { + chkgdnFk: item.chkgdnFk, + utztnUsgCd: item.utztnUsgCd, + vltnDt: item.vltnDt, + vltnMttr: item.vltnMttr, + levyAmt: Number(item.levyAmt), + }; + }) + .filter((item) => selectedIds.includes(item.chkgdnFk)); + + if (selectedData) setUpRswttDetail(selectedData); + }, [makeTableData, selectedIds]); + + useEffect(() => { + setSelectedIds([]); // 셀렉트 초기화를 위한 작업 + + const tableTotalData = rswtChkTableData(rswtChkData); + const makeRswtChkData = tableTotalData.map((item: any) => { + const bud = tableData([item.building], { code }); + return { + ...item, + ...bud[0], + rowspan: item.rswtrDetails?.length, + }; + }); + setMakeTableData(makeRswtChkData); + }, [rswtChkData, code]); + + useEffect(() => { + detaiRswtlUpdate(upRswttDetail); + }, [upRswttDetail]); + + useEffect(() => { + if (selectedIds.length > 0) { + // 삭제를 위한 데이터 삽입 + deleteRswtData(selectedIds); + } + }, [selectedIds]); + + ///현황일 경우 input 비활성화 한다. + useEffect(() => { + if(rswtChkSttn.isSttn){ + setHeadList({ + headList: rswtChkSttn.rswtChkSttnHeadList, + upHeadList: rswtChkSttn.rswtSttnHeadNm + }) + }else{ + setHeadList({ + headList: rswtChkgdn.rswtChkHeadList, + upHeadList: rswtChkgdn.rswtHeadNm + }) + + } + + }, [rswtChkSttn.isSttn, rswtChkgdn.rswtChkHeadList , rswtChkSttn.rswtChkSttnHeadList]); + + + return ( + <> + + + {headMakeprfmnc(headList?.headList, headList?.upHeadList)} + + + + {chRowspanTable( + makeTableData, + headList.headList, + handleChange, + toggleSelection, + selectedIds, + handleCellClick + )} + +
+ + ); +} diff --git a/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx b/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx new file mode 100644 index 000000000..e4ffc1f8d --- /dev/null +++ b/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx @@ -0,0 +1,155 @@ +import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { selectClass } 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[]; + code?: { name: string; val: SelectDataType[] }[]; + onTableSet?: (item: any) => void; + indexMng?: number; + indexBiz?: number; +}; + +const RuseMngTable = React.forwardRef( + ( + { tableData, headList, upHeadList, code, onTableSet, indexMng, indexBiz }, + 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 ( + <> + + + {upHeadList ? ( + headMakeprfmnc(headList, upHeadList) + ) : ( + + {headList.map((head) => ( + + {head.title} + + ))} + + )} + + + + {tableState.map((item, i) => ( + + {headList.map((head, j) => ( + {}} + > + {head.edit === true ? ( + { + 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" ? ( + item.name === head.selectData) + ?.val ?? [] + } + value={tableState[i][head.id] || ""} + className={""} + selectClass={selectClass} + onSelectValue={(val) => { + setTableState((prev) => { + prev[i][head.id] = val; + return [...prev]; + }); + }} + > + ) : head.type === "date" ? ( + { + setTableState((prev) => { + const next = [...prev]; + next[i] = { ...next[i], [head.id]: date }; + return next; + }); + }, + }} + selectClass={"w-full"} + /> + ) : head.type === "checkbox" ? ( + { + console.log("checked", checked); + setTableState((prev) => { + prev[i][head.id] = checked; + return [...prev]; + }); + }} + > + ) : ( + `${item[head.id] || ""}` + )} + + ))} + + ))} + +
+ + ); + } +); +export default RuseMngTable; diff --git a/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx b/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx new file mode 100644 index 000000000..acb923687 --- /dev/null +++ b/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx @@ -0,0 +1,160 @@ +import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { customPbnst as OriginalCustomPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; + +import { + DatePicker, + FormField, + FormUi, + SelectBox, + type SelectDataType, +} from "@common/business_components/ui"; +import { + Checkbox, + Input, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@common/components/ui"; +import React from "react"; + +type RuseMngTableProps = { + tableData: any[]; + headList: TableUpperProps[]; + upHeadList: chHeadType[]; + code?: { name: string; val: SelectDataType[] }[]; + onTableSet?: (item: any) => void; + indexMng?: number; + indexBiz?: number; + form?: any; + formNm?:string +}; + +const RuseMngTableForm = React.forwardRef( + ({ tableData, headList, upHeadList, code, onTableSet, indexMng, form,formNm,indexBiz }, ref) => { + + return ( + <> + + + {upHeadList ? ( + headMakeprfmnc(headList, upHeadList) + ) : ( + + {headList.map((head) => ( + + {head.title} + + ))} + + )} + + + + {tableData.map((item, i) => ( + + {headList.map((head, j) => ( + {}} + > + = 0 && + typeof indexBiz === "number" && + indexBiz >= 0? + `items.${indexMng}.fcBizs.${indexBiz}.${formNm}.${i}.${head.id}`: + typeof indexMng === "number" && indexMng >= 0? + `items.${indexMng}.${formNm}.${i}.${head.id}`: + `items.${indexMng}.${formNm}.${i}.${head.id}`} + render={({ field }) => ( + + {head.edit === true ? ( + { + field.onChange(txt.target.value); + }} + value={ + head.type === "number" + ? (Number(field.value) ?? 0) + : field.value || "" + } + readOnly ={head.readonly ? true : false} + /> + ) : head.type === "select" ? ( + item.name === head.selectData + )?.val ?? [] + } + value={ + field.value || "" + } + className={""} + selectClass={selectClass} + onSelectValue={(val) => { + + if(head.readonly === true){ + return + }else{ + + field.onChange(val); + } + }} + readOnly ={head.readonly ? true : false} + // Disabled prop removed as SelectBox does not support it + > + ) : head.type === "date" ? ( + { + + console.log("date", date); + field.onChange(date); // readonly 상태가 아닐 때만 값 변경 + }, + }} + selectClass={"w-full"} + readOnly ={head.readonly ? true : false} + /> + ) : head.type === "checkbox" ? ( + { + field.onChange(checked); + }} + > + ) : ( + `${item[head.id] || ""}` + )} + + )} + /> + + ))} + + ))} + +
+ + ); + } +); +export default RuseMngTableForm; diff --git a/apps/wr/components/ui/toast/toastFromUi.tsx b/apps/wr/components/ui/toast/toastFromUi.tsx new file mode 100644 index 000000000..5886c2e0b --- /dev/null +++ b/apps/wr/components/ui/toast/toastFromUi.tsx @@ -0,0 +1,46 @@ +import { Button } from "@common/components/ui"; +import Link from "next/link"; +import toast from "react-hot-toast"; + +interface toastFormType { + body: string; + title: string; + url?: string; + buttonName?: string; +} + +export function ToastFormUi({ body, title, url, buttonName }: toastFormType) { + //const router = useRouter(); + + return toast.custom((t) => ( +
+
+
+
+
+

{title}

+

{body}

+
+
+
+
+ {url && buttonName ? ( + + ) : ( + <> + )} +
+
+ +
+
+ )); +} diff --git a/apps/wr/middleware.ts b/apps/wr/middleware.ts new file mode 100644 index 000000000..330252074 --- /dev/null +++ b/apps/wr/middleware.ts @@ -0,0 +1,52 @@ +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; +export function middleware(req: NextRequest) { + const pathname = req.nextUrl.pathname; + console.log(`[Middleware] Request URL: ${req.url}`); + const searchParams = req.nextUrl.searchParams; + + if (pathname.startsWith("/server")) { + const queryParams = searchParams.toString(); + 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.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}` + ); + return NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ["/server/:path*", "/attach-service/:path*"], +}; diff --git a/apps/wr/next.config.ts b/apps/wr/next.config.ts index 99301dab6..d157358c9 100644 --- a/apps/wr/next.config.ts +++ b/apps/wr/next.config.ts @@ -1,40 +1,32 @@ import type { NextConfig } from "next"; -import webpack from "webpack"; + +const subDomain = + process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13001"; const nextConfig: NextConfig = { - eslint: { - ignoreDuringBuilds: true, - }, - webpack: (config) => { - config.plugins.push( - new webpack.DefinePlugin({ - "import.meta.env.VITE_PORT": JSON.stringify( - process.env.NEXT_PUBLIC_PORT - ), - "import.meta.env.VITE_PROXY_HOST": JSON.stringify( - process.env.NEXT_PUBLIC_PROXY_HOST - ), - "import.meta.env.VITE_SERVER_API_URL": JSON.stringify( - process.env.NEXT_PUBLIC_SERVER_API_URL - ), - "import.meta.env.VITE_SITE_ID": JSON.stringify(process.env.SITE_ID), - "import.meta.env.VITE_CLAIM_NAME": JSON.stringify( - process.env.NEXT_PUBLIC_CLAIM_NAME - ), - "import.meta.env.VITE_ACCESS_TOKEN": JSON.stringify( - process.env.NEXT_PUBLIC_ACCESS_TOKEN - ), - "import.meta.env.VITE_REFRESH_TOKEN": JSON.stringify( - process.env.NEXT_PUBLIC_REFRESH_TOKEN - ), - "import.meta.env.VITE_AUTH_USER_ID": JSON.stringify( - process.env.NEXT_PUBLIC_AUTH_USER_ID - ), - "process.env.SITE_ID": JSON.stringify(process.env.NEXT_PUBLIC_SITE_ID), - }) - ); - - return config; + async rewrites() { + return { + beforeFiles: [ + { + source: "/_next/static/:path*", + destination: `${subDomain}/_next/static/:path*`, + has: [ + { + type: "header", + key: "referer", + value: "(.*)/pst(.*)", + }, + ], + }, + ], + afterFiles: [ + { + source: "/pst/:path*", + destination: `${subDomain}/pst/:path*`, + }, + ], + fallback: [], + }; }, }; diff --git a/apps/wr/package.json b/apps/wr/package.json index 82b21fd50..44ba5a06b 100644 --- a/apps/wr/package.json +++ b/apps/wr/package.json @@ -3,23 +3,27 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", + "dev": "next dev -p 3001", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { + "@common/assets": "workspace:*", "@common/business_components": "workspace:*", "@common/components": "workspace:*", "@hookform/resolvers": "^4.1.3", + "@tanstack/react-table": "^8.21.2", "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", "tailwind-merge": "^3.0.1", "tailwindcss-animate": "^1.0.7", + "tus-js-client": "^4.3.1", "zod": "^3.24.2" }, "devDependencies": { diff --git a/apps/wr/public/2024_constarctFeeProgram.zip b/apps/wr/public/2024_constarctFeeProgram.zip new file mode 100644 index 000000000..f399fa091 Binary files /dev/null and b/apps/wr/public/2024_constarctFeeProgram.zip differ diff --git a/apps/wr/public/sample.xlsx b/apps/wr/public/sample.xlsx new file mode 100644 index 000000000..ee4b23edb Binary files /dev/null and b/apps/wr/public/sample.xlsx differ diff --git a/apps/wr/public/sample_coSttn.xlsx b/apps/wr/public/sample_coSttn.xlsx new file mode 100644 index 000000000..836ee002e Binary files /dev/null and b/apps/wr/public/sample_coSttn.xlsx differ diff --git a/apps/wr/public/sample_drgtSttn.xlsx b/apps/wr/public/sample_drgtSttn.xlsx new file mode 100644 index 000000000..7a0c868cd Binary files /dev/null and b/apps/wr/public/sample_drgtSttn.xlsx differ diff --git a/apps/wr/public/sample_htdflSttn.xlsx b/apps/wr/public/sample_htdflSttn.xlsx new file mode 100644 index 000000000..fd1dc5a89 Binary files /dev/null and b/apps/wr/public/sample_htdflSttn.xlsx differ diff --git a/apps/wr/public/sample_ldrmntCoSttn.xlsx b/apps/wr/public/sample_ldrmntCoSttn.xlsx new file mode 100644 index 000000000..646e8e4b8 Binary files /dev/null and b/apps/wr/public/sample_ldrmntCoSttn.xlsx differ 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_pbnstPrfmncSttn.xlsx b/apps/wr/public/sample_pbnstPrfmncSttn.xlsx new file mode 100644 index 000000000..183434984 Binary files /dev/null and b/apps/wr/public/sample_pbnstPrfmncSttn.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/apps/wr/public/sample_rainChkSttn.xlsx b/apps/wr/public/sample_rainChkSttn.xlsx new file mode 100644 index 000000000..dcab1be0c Binary files /dev/null and b/apps/wr/public/sample_rainChkSttn.xlsx differ diff --git a/apps/wr/public/sample_rainStats.xlsx b/apps/wr/public/sample_rainStats.xlsx new file mode 100644 index 000000000..c8f8e69b5 Binary files /dev/null and b/apps/wr/public/sample_rainStats.xlsx differ diff --git a/apps/wr/public/sample_rainSttn.xlsx b/apps/wr/public/sample_rainSttn.xlsx new file mode 100644 index 000000000..f00a520cf Binary files /dev/null and b/apps/wr/public/sample_rainSttn.xlsx differ diff --git a/apps/wr/public/sample_rainSttn_test.xlsx b/apps/wr/public/sample_rainSttn_test.xlsx new file mode 100644 index 000000000..57ea2f222 Binary files /dev/null and b/apps/wr/public/sample_rainSttn_test.xlsx differ diff --git a/apps/wr/public/sample_rainy.xlsx b/apps/wr/public/sample_rainy.xlsx new file mode 100644 index 000000000..73a52d89c Binary files /dev/null and b/apps/wr/public/sample_rainy.xlsx differ diff --git a/apps/wr/public/sample_rs_chk.xlsx b/apps/wr/public/sample_rs_chk.xlsx new file mode 100644 index 000000000..39b0e7d3e Binary files /dev/null and b/apps/wr/public/sample_rs_chk.xlsx differ diff --git a/apps/wr/public/sample_rswtChkSttn.xlsx b/apps/wr/public/sample_rswtChkSttn.xlsx new file mode 100644 index 000000000..a5b2e85a4 Binary files /dev/null and b/apps/wr/public/sample_rswtChkSttn.xlsx differ diff --git a/apps/wr/public/sample_rswtStats.xlsx b/apps/wr/public/sample_rswtStats.xlsx new file mode 100644 index 000000000..702c4cec0 Binary files /dev/null and b/apps/wr/public/sample_rswtStats.xlsx differ diff --git a/apps/wr/public/sample_rswtSttn.xlsx b/apps/wr/public/sample_rswtSttn.xlsx new file mode 100644 index 000000000..f64b37ea3 Binary files /dev/null and b/apps/wr/public/sample_rswtSttn.xlsx differ diff --git a/apps/wr/tailwind.config.ts b/apps/wr/tailwind.config.ts index d3eabd953..00237ea18 100644 --- a/apps/wr/tailwind.config.ts +++ b/apps/wr/tailwind.config.ts @@ -1,66 +1,21 @@ -import baseConfig from "../../packages/common/components/tailwind.config"; - import type { Config } from "tailwindcss"; +import tailwindcssAnimate from "tailwindcss-animate"; +import presetConfig from "../../tailwind.config.preset"; export default { - darkMode: ["class"], - presets: [baseConfig], + presets: [presetConfig], content: [ "./app/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", "../../packages/common/components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", "../../packages/common/business_components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", ], - plugins: [require("tailwindcss-animate")], - theme: { - extend: { - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))' - } - } - } - } -} satisfies Config; + theme: { + extend: { + boxShadow: { + blur: "0 0 10px -1px rgba(0, 0, 0, 0.06)", + }, + }, + }, + plugins: [tailwindcssAnimate], +} satisfies Config; \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx index d666f1d14..39dee3d9e 100644 --- a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx +++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx @@ -1,7 +1,7 @@ "use client" -import { Pie, PieChart } from "recharts"; import type { CSSProperties } from "react"; +import { Pie, PieChart } from "recharts"; import { Card, CardContent @@ -99,7 +99,7 @@ const CustomLegend = (props: LegendProps) => { }; export default function DoughnutChart(props: { - chartConfig: ChartConfig; + chartConfig: any; chartData: ChartDataItem[] | undefined; chartTitle: string }) { diff --git a/packages/common/business_components/src/ui/date_picker/index.tsx b/packages/common/business_components/src/ui/date_picker/index.tsx index d3a6a05c1..9df32b3b5 100644 --- a/packages/common/business_components/src/ui/date_picker/index.tsx +++ b/packages/common/business_components/src/ui/date_picker/index.tsx @@ -12,37 +12,39 @@ import { FormControl } from "../form"; import { ko } from "date-fns/locale"; import { useState } from "react"; -type DatePickerProps = DayPickerSingleProps & { field: { value: Date; onChange: (date: Date | undefined) => void } ,selectClass:string|undefined }; - - -export function DatePicker({ field, selectClass }: DatePickerProps) { +type DatePickerProps = DayPickerSingleProps & { field: { value: Date; onChange: (date: Date | undefined) => void }, selectClass: string | undefined, readOnly?: boolean }; +export function DatePicker({ field, selectClass, readOnly = false, ...props }: DatePickerProps) { const [open, setOpen] = useState(false); const handleSelect = (date: Date | undefined) => { - field.onChange(date); // React Hook Form 업데이트 - setOpen(false); - }; + if (!readOnly) { // readOnly가 아닐 때만 변경 허용 + field.onChange(date); // React Hook Form 업데이트 + setOpen(false); + } + }; + return ( - +