diff --git a/.gitignore b/.gitignore index a355a3402..40f0d864b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ # turbo -.turbo +.turbo/ # dependencies -node_modules +node_modules/ + +# build +.next/ +dist/ # build .next @@ -10,3 +14,6 @@ dist # misc .DS_Store + +# env +.env* diff --git a/apps/cs/src/pages/site/ui/site-page.tsx b/apps/cs/src/pages/site/ui/site-page.tsx index 1e026c6ec..b693de757 100644 --- a/apps/cs/src/pages/site/ui/site-page.tsx +++ b/apps/cs/src/pages/site/ui/site-page.tsx @@ -6,11 +6,11 @@ import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; -import { Button, Input, DataTable } from "@common/components"; -import { ENDPOINT } from "@/shared/config"; +import { Button, DataTable, Input } from "@common/components"; +import { ENDPOINT } from "@/shared/config/api"; import { SiteApi } from "../api/site-service"; -import { type ISite, type ISiteParams } from "../model/site-interface"; import { columns } from "../model/table-columns"; +import { type ISite, type ISiteParams } from "../model/site-interface"; import { SitePageLayout } from "./layout"; function SitePage() { diff --git a/apps/wr/.gitignore b/apps/wr/.gitignore new file mode 100644 index 000000000..5ef6a5207 --- /dev/null +++ b/apps/wr/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/wr/app/favicon.ico b/apps/wr/app/favicon.ico new file mode 100644 index 000000000..718d6fea4 Binary files /dev/null and b/apps/wr/app/favicon.ico differ diff --git a/apps/wr/app/globals.css b/apps/wr/app/globals.css new file mode 100644 index 000000000..f30aa3031 --- /dev/null +++ b/apps/wr/app/globals.css @@ -0,0 +1,70 @@ +@import "../../../packages/common/components/src/base.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; + } +} diff --git a/apps/wr/app/layout.tsx b/apps/wr/app/layout.tsx new file mode 100644 index 000000000..a0c14576d --- /dev/null +++ b/apps/wr/app/layout.tsx @@ -0,0 +1,21 @@ +import { BaseLayout } from "@common/components"; +import "./globals.css"; +import { StoreProvider } from "./store"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + {children} + + + + + ); +} diff --git a/apps/wr/app/matMng/matDmd/coReg/page.tsx b/apps/wr/app/matMng/matDmd/coReg/page.tsx new file mode 100644 index 000000000..0c6ceccaa --- /dev/null +++ b/apps/wr/app/matMng/matDmd/coReg/page.tsx @@ -0,0 +1,84 @@ +"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>({ + resolver: zodResolver(formSchema), + defaultValues: { + coNm:'', + bznmNo:'', + addr:'', + telNo:'', + }, + }); + + function onSubmit(values: z.infer) { + try { + toast.dismiss(); + ToastFormUi("수요신청 화면으로 이동하시겠습니까?", "수요처 등록 완료", "./dmdReg", "이동") + + } catch (error) { + console.error("Form submission error", error); + toast.error("수요처 등록이 실패하였습니다."); + } + } + + return ( + <> +
+

+ 수요처 정보입력 +

+
+ +
+
+
+
+
+ + {form} + + + +
+ +
+
+
+ +
+
+ + ); +} + diff --git a/apps/wr/app/matMng/matDmd/dmdReg/page.tsx b/apps/wr/app/matMng/matDmd/dmdReg/page.tsx new file mode 100644 index 000000000..d405d45a6 --- /dev/null +++ b/apps/wr/app/matMng/matDmd/dmdReg/page.tsx @@ -0,0 +1,106 @@ +"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>({ + 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) { + try { + + toast.success("✅ 수요신청이 완료되었습니다."); + await new Promise((resolve) => setTimeout(resolve, 3000)); + router.push("../../") + } catch (error) { + toast.error("수요신청을 다시입력해주시길 바랍니다."); + } + } + + return ( + <> +
+

+ 수요 신청정보입력 +

+
+ +
+
+
+
+
+ + {form} + + +
+ +
+
+
+ +
+
+ + ); +} diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx new file mode 100644 index 000000000..356cfe8c3 --- /dev/null +++ b/apps/wr/app/page.tsx @@ -0,0 +1,26 @@ +import Link from 'next/link'; +import { Button } from '@common/components/ui'; + + + +export default function Home() { + return ( + <> +
+ + + + + +
+ + ); +} diff --git a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert/page.tsx b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert/page.tsx new file mode 100644 index 000000000..a122d99c3 --- /dev/null +++ b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/publicReuseFaciltyInsert/page.tsx @@ -0,0 +1,52 @@ +'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 ( +

○공공 하수처리시설 등록

+ ) + } + const onSelectValue = (val: string) => { + console.log(val) + } + return ( + <> +
+
+

하수재이용시설 등록

+
+ +
+ +
+
+ + +
+
+
+
+
+ +
+
+ + + {/* */} + +
+
+ + ) +} \ No newline at end of file diff --git a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert/page.tsx b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert/page.tsx new file mode 100644 index 000000000..c94a93075 --- /dev/null +++ b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rainReuseFaciltyInsert/page.tsx @@ -0,0 +1,67 @@ +'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 (<> + { + 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 default function Home() { + const { rain } = useWrStore((state) => state); + const nodeList = headMakeColSpan(rain.upHeadList); + + + const headName = () => { + return ( +

○건축물 목록

+ ) + } + return ( + <> +
+
+

빗물이용시설 등록

+
+ +
+ +
+
+ + + +
+
+
+
+ +
+ + {nodeList} + +
+
+ + + +
+ +
+ + ); +} diff --git a/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert/page.tsx b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert/page.tsx new file mode 100644 index 000000000..cecd4177c --- /dev/null +++ b/apps/wr/app/reuseFaciltyManage/reuseFacilityInsert/rswtReuseFacilityInsert/page.tsx @@ -0,0 +1,69 @@ +'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 (<> + { + 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 default function Home() { + const {rswt} = useWrStore((state) => state); + const nodeList = headMakeColSpan(rswt.upHeadList); + const total = 100; + + + const headName = () => { + return ( +

○건축물 목록

+ ) + } + + return ( + <> +
+
+

중수도시설 등록

+
+ +
+ +
+
+ + + +
+
+
+
+ +
+ + {nodeList} + +
+
+ + + +
+ +
+ + ); +} diff --git a/apps/wr/app/store/index.tsx b/apps/wr/app/store/index.tsx new file mode 100644 index 000000000..1b759c30e --- /dev/null +++ b/apps/wr/app/store/index.tsx @@ -0,0 +1,41 @@ +'use client' + +import { type ReactNode, createContext, useContext, useRef } from 'react' +import { useStore } from 'zustand' +import { type WrStore, StoreZus } from './store' + + +export type StoreApi = ReturnType + +export const StoreContext = createContext( + undefined, +) + +export interface StoreProviderProps { + children: ReactNode +} + +export const StoreProvider = ({ + children, +}: StoreProviderProps) => { + const storeRef = useRef(null) + if (!storeRef.current) { + storeRef.current = StoreZus() + } + return ( + + {children} + + ) +} + +export const useWrStore = ( + selector: (store: WrStore) => T, +): T => { + const storeContext = useContext(StoreContext) + if (!storeContext) { + throw new Error(`useStore must be used within StoreProvider`) + } + + return useStore(storeContext, selector) +} \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/index.tsx b/apps/wr/app/store/matDmd/index.tsx new file mode 100644 index 000000000..2ed933348 --- /dev/null +++ b/apps/wr/app/store/matDmd/index.tsx @@ -0,0 +1,55 @@ +import type { selectArray } from "../rainReuseFacilityInsert"; + + + +const selectSido = [ + { text: '서울', val: "1" }, + { text: "인천", val: "2" }, + { text: "부산", val: "3" }, + ]; +const selectSgg = [ + { text: '종로구', val: "1" }, + { text: "성북구", val: "2" }, + { text: "강남구", val: "3" }, + ]; + const selectUsg = [ + { text: '민간', val: "1" }, + { text: "공공", val: "2" }, + ]; + const selectYN= [ + { text: 'Y', val: "1" }, + { text: 'N', val: "2" }, + ]; + +export type matDmdState ={ dmd:{ + isInit: boolean; + selectSido:selectArray[]; + selectSgg:selectArray[]; + selectUsg:selectArray[]; + selectYN:selectArray[]; +}} +type matDmdActions = { + decrementList: () => void +} + +export type matDmdType = matDmdState & matDmdActions; + +export const matDmdStateExport:matDmdState= {dmd:{ + isInit:false, + selectSido:selectSido, + selectSgg: selectSgg, + selectUsg: selectUsg, + selectYN:selectYN, + + +}} +export const matDmdActionsExport:(set:any)=>matDmdActions=(set: any) => { + return { + decrementList:()=>set( + (state:matDmdState) => { + return ( + {rain:{ + }})} + ) + } +} diff --git a/apps/wr/app/store/publicReuseFaciltyInsert/index.tsx b/apps/wr/app/store/publicReuseFaciltyInsert/index.tsx new file mode 100644 index 000000000..131132b85 --- /dev/null +++ b/apps/wr/app/store/publicReuseFaciltyInsert/index.tsx @@ -0,0 +1,178 @@ +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 new file mode 100644 index 000000000..0fd5194f9 --- /dev/null +++ b/apps/wr/app/store/rainReuseFacilityInsert/index.tsx @@ -0,0 +1,134 @@ + +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/readme.md b/apps/wr/app/store/readme.md new file mode 100644 index 000000000..0710aa69b --- /dev/null +++ b/apps/wr/app/store/readme.md @@ -0,0 +1,121 @@ +# Zustand store +## Zustand 등록방법 +**-- index.tsx --** +### context 등록 +기본적으로 createContext를 통해 react에 context를 생성해서 react에서 사용할 수 있도록 해야함. +``` js +//StoreZus는 내부적으로 사용하기 위한 store파일에서 따로 생성함. +export type StoreApi = ReturnType +//react에서 사용하기 위한 context 생성. +export const StoreContext = createContext( + undefined, +) +``` + +### component로 작성 +등록된 context를 component화 시키기 위한 작업 +```js +//외부에서 호출시 형태로 호출. +//app이나 home에서 전체적인 사용이 되도록 작성해야함. +export const StoreProvider = ({ + children, +}: StoreProviderProps) => { + const storeRef = useRef(null) + if (!storeRef.current) { + //StoreZus는 store.tsx를 봐야함. + storeRef.current = StoreZus() + } + return ( + + {children} + + ) +} +``` + +### store 등록 +react에서 사용하는 store에 현재 context 등록 +```js +export const useWrStore = ( + selector: (store: WrStore) => T, +): T => { + const storeContext = useContext(StoreContext) + if (!storeContext) { + throw new Error(`useStore must be used within StoreProvider`) + } + + return useStore(storeContext, selector) +} +``` + +## Zustand 사용방법 +**-- store.tsx, 폴더별 index.tsx --** +### 기본 store 생성 +index.tsx의 context에 사용되는 StoreZus를 추가하여 사용됨. + +외부에서 생성된 state, actions를 사용 가능한 구조 +```js +import { createStore } from 'zustand/vanilla'; +import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index';//외부 파일 위치. +export type WrStore = tableType;//typescript이므로 사용되는 스크립트를 type1 & type2 형식으로 넣어야함. +//index.tsx에서 사용하는 store 생성. +export const StoreZus = (initState: tableState = tableStateExport) => { + return createStore()((set) => { + return { + //아래에서 사용하는 state & actions은 외부 폴더에서 가져다가 사용. + ...initState,//외부에 있는 기본 초기값 state + ...tableActionsExport(set)//외부에 있는 actions + } + }) +} +``` +### 외부 스토어 작성 방법 +외부 스토어는 export를 어떻게 할건지만 정하면됨. + +사용하는 store.tsx에서 import를 받기만 하면 사용가능. +#### 타입을 외부로 빼는 방법 +```js +export type tableState = { + isInit: boolean; + upHeadList: TableUpperProps[]; + publicReuseFacilityUper: PublicReuseFacility[]; +} +type tableActions = { + decrementList: () => void +} + +export type tableType = tableState & tableActions; +``` +#### 초기값 state를 외부로 빼는 방법 +```js +export const tableStateExport:tableState= { + isInit:false, + upHeadList: upHeadList, + publicReuseFacilityUper: publicReuseFacilityUper, +} +``` +#### actions를 외부로 빼는 방법 +```js +//tableActionsExport를 import 받아서 실행하여 return 된 값을 사용 +export const tableActionsExport:(set:any)=>tableActions= +//set을 통해 react store에 등록 +(set: any) => { + return { + //decrementList Object 형태로 전달 + decrementList: + //set 함수를 실행해서 react store에 등록 + ()=>set( + // store에 등록되는 함수 구조를 가지고 있음. + (state:tableState) => { + //actions 실질적인 로직 작성하는 곳. + return ( + { + upHeadList: state.upHeadList, + publicReuseFacilityUper: + state.publicReuseFacilityUper.slice(0, state.publicReuseFacilityUper.length - 1) + })} + ) + } +} +``` + diff --git a/apps/wr/app/store/rswtrReuseFaciltyInsert/index.tsx b/apps/wr/app/store/rswtrReuseFaciltyInsert/index.tsx new file mode 100644 index 000000000..b2621631b --- /dev/null +++ b/apps/wr/app/store/rswtrReuseFaciltyInsert/index.tsx @@ -0,0 +1,94 @@ +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/store.tsx b/apps/wr/app/store/store.tsx new file mode 100644 index 000000000..68ca9db7e --- /dev/null +++ b/apps/wr/app/store/store.tsx @@ -0,0 +1,25 @@ +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'; + + +export type WrStore = pubFacState & rainFacType &rswtFacType &matDmdType; +export const StoreZus = (initState: rainFacState& pubFacState & rswtFacState &matDmdState= { + ...pubFacStateExport, + ...rainFacStateExport, + ...rswtFacStateExport , + ...matDmdStateExport +}) => { + return createStore()((set) => { + return { + ...initState, + ...pubFacActionsExport(set), + ...rainFacActionsExport(set), + ...rswtFacActionsExport(set), + ...matDmdActionsExport(set) + } + }) +} +//store 등록하는 부분 \ No newline at end of file diff --git a/apps/wr/app/style/PublicReuseFaciltyInsert.module.css b/apps/wr/app/style/PublicReuseFaciltyInsert.module.css new file mode 100644 index 000000000..6fc6f75ee --- /dev/null +++ b/apps/wr/app/style/PublicReuseFaciltyInsert.module.css @@ -0,0 +1,9 @@ +.formInsideTitle{ + background-color: gray; + text-align: center; +} + + + +.smallTitle{ +} \ No newline at end of file diff --git a/apps/wr/components.json b/apps/wr/components.json new file mode 100644 index 000000000..d9ef0ae53 --- /dev/null +++ b/apps/wr/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/apps/wr/components/toastFromUi.tsx b/apps/wr/components/toastFromUi.tsx new file mode 100644 index 000000000..5270ca5ec --- /dev/null +++ b/apps/wr/components/toastFromUi.tsx @@ -0,0 +1,56 @@ + +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) => ( + +
+
+
+
+ +
+
+

+ {title} +

+

+ {body} +

+
+
+
+
+ +
+
+ +
+
+ )) + ) +} \ No newline at end of file diff --git a/apps/wr/components/ui/IteratreForm.tsx b/apps/wr/components/ui/IteratreForm.tsx new file mode 100644 index 000000000..9d35d865e --- /dev/null +++ b/apps/wr/components/ui/IteratreForm.tsx @@ -0,0 +1,54 @@ +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 ? {...form} : null) + }else if(data.type==='date'){ + tag =() + }else{ + tag=() + } + return tag; + } + + return ( + <> + {formData.map((data: typeOfForm, index) => ( + ( + + {data.labelName} + + {formItem(data,field)} + + + + )} + /> + ))} + + ); + }; \ No newline at end of file diff --git a/apps/wr/components/ui/matDmd/coRegForm/component.tsx b/apps/wr/components/ui/matDmd/coRegForm/component.tsx new file mode 100644 index 000000000..f27639251 --- /dev/null +++ b/apps/wr/components/ui/matDmd/coRegForm/component.tsx @@ -0,0 +1,91 @@ +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( + + <> + ( + + + + + )} + /> + + ( + + + + + )} + /> + + ( + + + + )} + /> + + ( + + + + )} + /> + + ( + + + + )} + /> + + ( + + + + + )} + /> + + + + ) +} \ 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 new file mode 100644 index 000000000..4e6bf0a38 --- /dev/null +++ b/apps/wr/components/ui/matDmd/dmdRegform/component.tsx @@ -0,0 +1,316 @@ +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 ( + <> + ( + + + + )} + /> + + ( + + 요구시기 + + + )} + /> + + ( + + + + + )} + /> + + ( + + + + + )} + /> + + ( + + + + + )} + /> + +
+

+ 요구수질 +

+
+
+ +
+ ( + + + + )} + /> +
+ +
+ ( + + + + )} + /> +
+
+ +
+
+ ( + + + + )} + /> +
+ +
+ ( + + + + )} + /> +
+
+ +
+
+ ( + + + + )} + /> +
+ +
+ ( + + + + )} + /> +
+
+ +
+
+ ( + + + + )} + /> +
+ +
+ ( + + + + )} + /> +
+
+ +
+
+ ( + + + + )} + /> +
+ +
+ ( + + + + )} + /> +
+
+ +
+
+ ( + + + + )} + /> +
+ +
+ ( + + + + )} + /> +
+
+
+

+ 공급후보지 선택 +

+
+ +
+
+ +
+ +
+
+
+ ( + + + + )} + /> +
+
+ ( + + + + )} + /> +
+
+ ( + + + + )} + /> +
+
+
+

+ 수요신청 비밀번호 +

+ +
+ ( + + + + )} + /> +
+
+ + ); +} diff --git a/apps/wr/components/ui/publicReuseInsertForm.tsx b/apps/wr/components/ui/publicReuseInsertForm.tsx new file mode 100644 index 000000000..a521e8a59 --- /dev/null +++ b/apps/wr/components/ui/publicReuseInsertForm.tsx @@ -0,0 +1,497 @@ +"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 new file mode 100644 index 000000000..57e30c551 --- /dev/null +++ b/apps/wr/components/ui/rainReuseInsertForm.tsx @@ -0,0 +1,240 @@ +"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 new file mode 100644 index 000000000..e4a540f20 --- /dev/null +++ b/apps/wr/components/ui/rswtrReuseInsetForm.tsx @@ -0,0 +1,240 @@ +"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/eslint.config.mjs b/apps/wr/eslint.config.mjs new file mode 100644 index 000000000..c85fb67c4 --- /dev/null +++ b/apps/wr/eslint.config.mjs @@ -0,0 +1,16 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), +]; + +export default eslintConfig; diff --git a/apps/wr/next-env.d.ts b/apps/wr/next-env.d.ts new file mode 100644 index 000000000..1b3be0840 --- /dev/null +++ b/apps/wr/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// 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/next.config.ts b/apps/wr/next.config.ts new file mode 100644 index 000000000..99301dab6 --- /dev/null +++ b/apps/wr/next.config.ts @@ -0,0 +1,41 @@ +import type { NextConfig } from "next"; +import webpack from "webpack"; + +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; + }, +}; + +export default nextConfig; diff --git a/apps/wr/package.json b/apps/wr/package.json new file mode 100644 index 000000000..82b21fd50 --- /dev/null +++ b/apps/wr/package.json @@ -0,0 +1,34 @@ +{ + "name": "wr", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@common/business_components": "workspace:*", + "@common/components": "workspace:*", + "@hookform/resolvers": "^4.1.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.474.0", + "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", + "zod": "^3.24.2" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@types/webpack": "^5.28.5", + "eslint": "^9", + "eslint-config-next": "15.1.7", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/apps/wr/postcss.config.mjs b/apps/wr/postcss.config.mjs new file mode 100644 index 000000000..1a69fd2a4 --- /dev/null +++ b/apps/wr/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/apps/wr/public/file.svg b/apps/wr/public/file.svg new file mode 100644 index 000000000..004145cdd --- /dev/null +++ b/apps/wr/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/wr/public/globe.svg b/apps/wr/public/globe.svg new file mode 100644 index 000000000..567f17b0d --- /dev/null +++ b/apps/wr/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/wr/public/next.svg b/apps/wr/public/next.svg new file mode 100644 index 000000000..5174b28c5 --- /dev/null +++ b/apps/wr/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/wr/public/vercel.svg b/apps/wr/public/vercel.svg new file mode 100644 index 000000000..770539603 --- /dev/null +++ b/apps/wr/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/wr/public/window.svg b/apps/wr/public/window.svg new file mode 100644 index 000000000..b2b2a44f6 --- /dev/null +++ b/apps/wr/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/wr/tailwind.config.ts b/apps/wr/tailwind.config.ts new file mode 100644 index 000000000..d3eabd953 --- /dev/null +++ b/apps/wr/tailwind.config.ts @@ -0,0 +1,66 @@ +import baseConfig from "../../packages/common/components/tailwind.config"; + +import type { Config } from "tailwindcss"; + +export default { + darkMode: ["class"], + presets: [baseConfig], + content: [ + "./app/**/*.{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; diff --git a/apps/wr/tsconfig.json b/apps/wr/tsconfig.json new file mode 100644 index 000000000..d897e147f --- /dev/null +++ b/apps/wr/tsconfig.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "verbatimModuleSyntax": true, + "strictNullChecks": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"], + "@common/components/*": ["../../packages/common/components/src/*"], + "@common/components": ["../../packages/common/components/src/index"], + "@common/business_components/*": [ + "../../packages/common/business_components/src/*" + ], + "@common/business_components": [ + "../../packages/common/business_components/src/index" + ], + "@common/network": ["../../packages/common/network"] + } + }, + "include": [ + ".eslintrc.*", + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.js", + "**/*.cjs", + "**/*.mjs", + "**/*.jsx", + "**/*.json", + ".next/types/**/*.ts" + ], + "exclude": ["**/node_modules", "**/.*/*"] +} diff --git a/package.json b/package.json index 00d85914e..e5ac02e0c 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,14 @@ "build": "turbo run build", "dev": "turbo run dev", "dev:cs": "turbo run dev --filter=cs", + "dev:wr": "turbo run dev --filter=wr", "preview:components": "turbo run storybook --filter=@common/components", "preinstall": "npx only-allow pnpm" }, "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "zustand": "^5.0.3" }, "devDependencies": { "@types/node": "^22.13.9", diff --git a/packages/common/business_components/.gitignore b/packages/common/business_components/.gitignore new file mode 100644 index 000000000..8efffcff1 --- /dev/null +++ b/packages/common/business_components/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*storybook.log +storybook-static +.vercel + +.env.production \ No newline at end of file diff --git a/packages/common/business_components/.prettierrc b/packages/common/business_components/.prettierrc new file mode 100644 index 000000000..b4bfed357 --- /dev/null +++ b/packages/common/business_components/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/packages/common/business_components/README.md b/packages/common/business_components/README.md new file mode 100644 index 000000000..670a8ac47 --- /dev/null +++ b/packages/common/business_components/README.md @@ -0,0 +1,33 @@ +## business_components 패키지 +### 원앤올에서 제공받는 컴포넌트 외에 업무 파트에서 사용할 수 있는 별도 components 패키지지 +  1) 환경설정 + +    (1) tsconfig.json 'path'에 '@common/business_components' 변수를 추가 +```json +"paths": { + "@/*": ["./*"], + "@common/components/*": ["../../packages/common/components/src/*"], + "@common/components": ["../../packages/common/components/src/index"], + //아래에 해당하는 부분을 추가해줘야함. + "@common/business_components/*": ["../../packages/common/business_components/src/*"], + "@common/business_components": ["../../packages/common/business_components/src/index"] +} +``` +    (2) package.json 'dependencies'에 '@common/business_components' 추가 +```json +"dependencies": { + "@common/business_components": "workspace:*", + "next": "15.1.7" +}, +``` + +    (3) tailwind.config.ts 추가 +``` json +content: [ + "./app/**/*.{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}", +], +``` + diff --git a/packages/common/business_components/components.json b/packages/common/business_components/components.json new file mode 100644 index 000000000..b8c9e23f0 --- /dev/null +++ b/packages/common/business_components/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/packages/common/business_components/eslint.config.js b/packages/common/business_components/eslint.config.js new file mode 100644 index 000000000..0bbf074e3 --- /dev/null +++ b/packages/common/business_components/eslint.config.js @@ -0,0 +1,28 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + } +); diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json new file mode 100644 index 000000000..ce78d2a6b --- /dev/null +++ b/packages/common/business_components/package.json @@ -0,0 +1,83 @@ +{ + "name": "@common/business_components", + "version": "0.0.0", + "private": true, + "sideEffects": false, + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist/**" + ], + "scripts": { + "build": "tsup", + "storybook": "storybook dev -p 6006", + "build:storybook": "storybook build", + "lint": "eslint .", + "clean": "rm -rf dist" + }, + "dependencies": { + "@common/components": "workspace:*", + "@hookform/resolvers": "^4.1.3", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-slot": "^1.1.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "lucide-react": "^0.474.0", + "path": "^0.12.7", + "react-day-picker": "^8.10.1", + "react-dropzone": "^14.3.8", + "react-hook-form": "^7.54.2", + "recharts": "^2.15.1", + "sonner": "^2.0.1", + "tailwind-merge": "^3.0.1", + "zod": "^3.24.2" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@storybook/addon-essentials": "^8.5.3", + "@storybook/addon-interactions": "^8.5.3", + "@storybook/addon-onboarding": "^8.5.3", + "@storybook/addon-themes": "^8.5.4", + "@storybook/blocks": "^8.5.3", + "@storybook/manager-api": "^8.5.5", + "@storybook/react": "^8.5.3", + "@storybook/react-vite": "^8.5.3", + "@storybook/test": "^8.5.3", + "@storybook/theming": "^8.5.5", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.20", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.16", + "eslint-plugin-storybook": "^0.11.2", + "globals": "^15.14.0", + "postcss": "^8.5.1", + "prettier": "^3.4.2", + "prettier-plugin-tailwindcss": "^0.6.11", + "storybook": "^8.5.3", + "tailwindcss": "^3.4.17", + "tsup": "8.3.6", + "typescript": "~5.6.2", + "typescript-eslint": "^8.18.2", + "vite": "^6.0.5", + "vite-tsconfig-paths": "5.1.4" + }, + "eslintConfig": { + "extends": [ + "plugin:storybook/recommended" + ] + }, + "packageManager": "pnpm@8.15.5+sha1.a58c038faac410c947dbdb93eb30994037d0fce2" +} \ No newline at end of file diff --git a/packages/common/business_components/postcss.config.js b/packages/common/business_components/postcss.config.js new file mode 100644 index 000000000..2aa7205d4 --- /dev/null +++ b/packages/common/business_components/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/common/business_components/src/base.css b/packages/common/business_components/src/base.css new file mode 100644 index 000000000..6b56f5eff --- /dev/null +++ b/packages/common/business_components/src/base.css @@ -0,0 +1,73 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: #ffffff; + --foreground: #0a0a0a; + --card: #ffffff; + --card-foreground: #0a0a0a; + --popover: #ffffff; + --popover-foreground: #0a0a0a; + --primary: #2094fa; + --primary-foreground: #fafafa; + --secondary: #f5f5f5; + --secondary-foreground: #0f1e33; + --muted: #f5f5f5; + --muted-foreground: #737373; + --accent: #f5f5f5; + --accent-foreground: #0f1e33; + --destructive: #ff4141; + --destructive-foreground: #fafafa; + --border: #e5e5e5; + --input: #e5e5e5; + --label: #666666; + --ring: #0a0a0a; + --chart-1: #d95b43; + --chart-2: #299392; + --chart-3: #263e4d; + --chart-4: #dfb938; + --chart-5: #f08634; + --radius: 1rem; + } + [data-mode="dark"] { + --background: #0a0a0a; + --foreground: #fafafa; + --card: #0a0a0a; + --card-foreground: #fafafa; + --popover: #0a0a0a; + --popover-foreground: #fafafa; + --primary: #2094fa; + --primary-foreground: #171717; + --secondary: #262626; + --secondary-foreground: #fafafa; + --muted: #262626; + --muted-foreground: #a2a2a2; + --accent: #262626; + --accent-foreground: #fafafa; + --destructive: #b22222; + --destructive-foreground: #fafafa; + --border: #262626; + --input: #262626; + --label: #f1f1f1; + --ring: #d4d4d4; + --chart-1: #3399ff; + --chart-2: #339966; + --chart-3: #ff9933; + --chart-4: #cc66ff; + --chart-5: #ff3399; + } +} + +@layer base { + * { + @apply border-border; + } + html { + @apply text-[10px]; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/packages/common/business_components/src/chart.md b/packages/common/business_components/src/chart.md new file mode 100644 index 000000000..f2d989375 --- /dev/null +++ b/packages/common/business_components/src/chart.md @@ -0,0 +1,54 @@ +## Chart 모듈 +### 공통 모듈 중 Chart를 다양한 테마로 적용시킬 수 있도록 해주는 모듈 +  1. 차트 모듈 사용법 + +    1-1) 호출방법 + +      (1) 파일에 차트생성에 필요한 form, MainChart, MainConfig 임폴트 +```js +import {type ChartConfig, MainChart, CHART_FORM } from "@common/business_components"; +``` +      (2) ChartForm 지정 +```js +/** + * CHART_FORM.BAR : Bar차트 + * CHART_FORM.LINE : Line차트 + * DN : Daughnut차트 +**/ +const chartForm = CHART_FORM.BAR; +``` +      (3) chartData 정의 +``` js +/** + * label : 차트항목 / val : 데이터값 +**/ +const chartData = [ + { label: "January", val: 186}, + { label: "March", val: 237}, + { label: "February", val: 305}, + { label: "April", val: 73}, + { label: "May", val: 209}, + { label: "June", val: 214}, +] +``` +      (4) Chart생성에 필요한 chartConfig 정의 +``` js +/** + * 차트 Config + */ +const chartConfig = chartData.reduce((config,>>element) => { + label: element.label, + config[element.label] = { + color : "hsl(var(--chart-1))" + }; + return config; + }, {} as Record) satisfies ChartConfig; +``` +      (5) Chart 객체 생성 +```html +return ( +
+ +
+ ); +``` \ No newline at end of file diff --git a/packages/common/business_components/src/diveicon.css b/packages/common/business_components/src/diveicon.css new file mode 100644 index 000000000..cce8cdaef --- /dev/null +++ b/packages/common/business_components/src/diveicon.css @@ -0,0 +1,1799 @@ +@font-face { + font-family: "diveicon"; + src: url("../public/assets/fonts/diveicon.eot?stp8eo"); + src: + url("../public/assets/fonts/diveicon.eot?stp8eo#iefix") + format("embedded-opentype"), + url("../public/assets/fonts/diveicon.ttf?stp8eo") format("truetype"), + url("../public/assets/fonts/diveicon.woff?stp8eo") format("woff"), + url("../public/assets/fonts/diveicon.svg?stp8eo#diveicon") format("svg"); + font-weight: normal; + font-style: normal; + font-display: block; +} + +i.diveicon { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: "diveicon" !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.di-account-box:before { + content: "\e8f3"; +} +.di-account-circle:before { + content: "\e901"; +} +.di-add-image:before { + content: "\e902"; +} +.di-add-Note:before { + content: "\e903"; +} +.di-alarm-add:before { + content: "\e904"; +} +.di-alarm-clock:before { + content: "\e905"; +} +.di-alarm-off:before { + content: "\e906"; +} +.di-alarm-on:before { + content: "\e908"; +} +.di-alarm:before { + content: "\e909"; +} +.di-alternate:before { + content: "\e90a"; +} +.di-analytics:before { + content: "\e90b"; +} +.di-android:before { + content: "\e90c"; +} +.di-announcement:before { + content: "\e90d"; +} +.di-app-indicator:before { + content: "\e90e"; +} +.di-app-list:before { + content: "\e90f"; +} +.di-app-square:before { + content: "\e910"; +} +.di-apple:before { + content: "\e911"; +} +.di-apps-square:before { + content: "\e912"; +} +.di-apps:before { + content: "\e913"; +} +.di-archive-fill:before { + content: "\e914"; +} +.di-archive:before { + content: "\e915"; +} +.di-arrow-90left-down:before { + content: "\e916"; +} +.di-arrow-90left-up:before { + content: "\e917"; +} +.di-arrow-90left:before { + content: "\e918"; +} +.di-arrow-90right-down:before { + content: "\e919"; +} +.di-arrow-90right-up:before { + content: "\e91a"; +} +.di-arrow-90right:before { + content: "\e91b"; +} +.di-arrow-back-square-fill:before { + content: "\e91c"; +} +.di-arrow-back:before { + content: "\e91d"; +} +.di-arrow-down-square-fill:before { + content: "\e91e"; +} +.di-arrow-down-square:before { + content: "\e91f"; +} +.di-arrow-down:before { + content: "\e920"; +} +.di-arrow-forward-square-fill:before { + content: "\e921"; +} +.di-arrow-forward:before { + content: "\e922"; +} +.di-arrow-left-right:before { + content: "\e923"; +} +.di-arrow-left-square-fill:before { + content: "\e924"; +} +.di-arrow-left-square:before { + content: "\e925"; +} +.di-arrow-left:before { + content: "\e926"; +} +.di-arrow-lr-up-down:before { + content: "\e927"; +} +.di-arrow-reflash:before { + content: "\e928"; +} +.di-arrow-reset:before { + content: "\e929"; +} +.di-arrow-right-square-fill:before { + content: "\e92a"; +} +.di-arrow-right-square:before { + content: "\e92b"; +} +.di-arrow-right:before { + content: "\e92c"; +} +.di-arrow-up-down:before { + content: "\e92d"; +} +.di-arrow-up-square-fill:before { + content: "\e92e"; +} +.di-arrow-up-square:before { + content: "\e92f"; +} +.di-arrow-up:before { + content: "\e930"; +} +.di-arrows-angle-contract:before { + content: "\e931"; +} +.di-arrows-angle-expand:before { + content: "\e932"; +} +.di-arrows-fullscreen:before { + content: "\e933"; +} +.di-aspect-ratio-fill:before { + content: "\e934"; +} +.di-aspect-ratio:before { + content: "\e935"; +} +.di-assignment-ind:before { + content: "\e936"; +} +.di-assignment-turned-in:before { + content: "\e937"; +} +.di-assignment:before { + content: "\e938"; +} +.di-asterisk:before { + content: "\e939"; +} +.di-atm:before { + content: "\e93a"; +} +.di-badge-4k-fill:before { + content: "\e93b"; +} +.di-badge-4k:before { + content: "\e93c"; +} +.di-badge-8k-fill:before { + content: "\e93d"; +} +.di-badge-8k:before { + content: "\e93e"; +} +.di-badge-ad-fill:before { + content: "\e93f"; +} +.di-badge-ad:before { + content: "\e940"; +} +.di-badge-cc-fill:before { + content: "\e941"; +} +.di-badge-cc:before { + content: "\e942"; +} +.di-badge-hd-fill:before { + content: "\e943"; +} +.di-badge-hd:before { + content: "\e944"; +} +.di-badge-tm-fill:before { + content: "\e945"; +} +.di-badge-tm:before { + content: "\e946"; +} +.di-bag-check-fill:before { + content: "\e947"; +} +.di-bag-check:before { + content: "\e948"; +} +.di-bag-fill:before { + content: "\e949"; +} +.di-bag:before { + content: "\e94a"; +} +.di-bags:before { + content: "\e94b"; +} +.di-bar-chart-fill:before { + content: "\e94c"; +} +.di-bar-chart:before { + content: "\e94d"; +} +.di-battery-charging:before { + content: "\e94e"; +} +.di-battery-emty:before { + content: "\e94f"; +} +.di-battery-full:before { + content: "\e950"; +} +.di-battery-harf:before { + content: "\e951"; +} +.di-bell-alert-box-fill:before { + content: "\e952"; +} +.di-bell-alert-box:before { + content: "\e953"; +} +.di-bell-alert-fill:before { + content: "\e954"; +} +.di-bell-alert:before { + content: "\e955"; +} +.di-bell-notification-fill:before { + content: "\e956"; +} +.di-bell-notification-on-fill:before { + content: "\e957"; +} +.di-bell-notification-on:before { + content: "\e958"; +} +.di-bell-notification:before { + content: "\e959"; +} +.di-bike:before { + content: "\e95a"; +} +.di-bluetooth:before { + content: "\e95b"; +} +.di-boat:before { + content: "\e95c"; +} +.di-book:before { + content: "\e95d"; +} +.di-bookmark-check-fill:before { + content: "\e95e"; +} +.di-bookmark-check:before { + content: "\e95f"; +} +.di-bookmark-fill:before { + content: "\e960"; +} +.di-bookmark-new-fill:before { + content: "\e961"; +} +.di-bookmark-new:before { + content: "\e962"; +} +.di-bookmark-star-fill:before { + content: "\e963"; +} +.di-bookmark-star:before { + content: "\e964"; +} +.di-bookmark:before { + content: "\e965"; +} +.di-boostzone:before { + content: "\e966"; +} +.di-box-arrow-backup:before { + content: "\e967"; +} +.di-box-arrow-download:before { + content: "\e968"; +} +.di-box-arrow-link-blank:before { + content: "\e969"; +} +.di-box-arrow-upload:before { + content: "\e96a"; +} +.di-box-seam:before { + content: "\e96b"; +} +.di-box:before { + content: "\e96c"; +} +.di-braces:before { + content: "\e96d"; +} +.di-briefcase-fill:before { + content: "\e96e"; +} +.di-briefcase:before { + content: "\e96f"; +} +.di-brightness-fill:before { + content: "\e970"; +} +.di-brightness:before { + content: "\e971"; +} +.di-broadcast-pin:before { + content: "\e972"; +} +.di-broadcast:before { + content: "\e973"; +} +.di-bug-fill:before { + content: "\e974"; +} +.di-bug:before { + content: "\e975"; +} +.di-building:before { + content: "\e976"; +} +.di-bus:before { + content: "\e977"; +} +.di-calculator-fill:before { + content: "\e978"; +} +.di-calculator:before { + content: "\e979"; +} +.di-calendar-check-fill:before { + content: "\e97a"; +} +.di-calendar-check:before { + content: "\e97b"; +} +.di-calendar-day-fill:before { + content: "\e97c"; +} +.di-calendar-day:before { + content: "\e97d"; +} +.di-calendar-event-fill:before { + content: "\e97e"; +} +.di-calendar-event:before { + content: "\e97f"; +} +.di-calendar-fill:before { + content: "\e980"; +} +.di-calendar-week-fill:before { + content: "\e981"; +} +.di-calendar-week:before { + content: "\e982"; +} +.di-calendar:before { + content: "\e983"; +} +.di-call-end:before { + content: "\e984"; +} +.di-call:before { + content: "\e985"; +} +.di-camera-fill:before { + content: "\e986"; +} +.di-camera-video-fill:before { + content: "\e987"; +} +.di-camera-video:before { + content: "\e988"; +} +.di-camera:before { + content: "\e989"; +} +.di-car:before { + content: "\e98a"; +} +.di-card-fill:before { + content: "\e98b"; +} +.di-card:before { + content: "\e98c"; +} +.di-caret-down-fill:before { + content: "\e98d"; +} +.di-caret-down-square-fill:before { + content: "\e98e"; +} +.di-caret-down-square:before { + content: "\e98f"; +} +.di-caret-left-fill:before { + content: "\e990"; +} +.di-caret-left-square-fill:before { + content: "\e991"; +} +.di-caret-left-square:before { + content: "\e992"; +} +.di-caret-right-fill:before { + content: "\e993"; +} +.di-caret-right-square-fill:before { + content: "\e994"; +} +.di-caret-right-square:before { + content: "\e995"; +} +.di-caret-up-fill:before { + content: "\e996"; +} +.di-caret-up-square-fill:before { + content: "\e997"; +} +.di-caret-up-square:before { + content: "\e998"; +} +.di-cart-fill:before { + content: "\e999"; +} +.di-cart:before { + content: "\e99a"; +} +.di-cash-stack:before { + content: "\e99b"; +} +.di-cash:before { + content: "\e99c"; +} +.di-cast:before { + content: "\e99d"; +} +.di-cctv:before { + content: "\e99e"; +} +.di-chat-box-left-dots-fill:before { + content: "\e99f"; +} +.di-chat-box-left-dots:before { + content: "\e9a0"; +} +.di-chat-box-left-fill:before { + content: "\e9a1"; +} +.di-chat-box-left-text-fill:before { + content: "\e9a2"; +} +.di-chat-box-left-text:before { + content: "\e9a3"; +} +.di-chat-box-left:before { + content: "\e9a4"; +} +.di-chat-box-right-dots-fill:before { + content: "\e9a5"; +} +.di-chat-box-right-dots:before { + content: "\e9a6"; +} +.di-chat-box-right-fill:before { + content: "\e9a7"; +} +.di-chat-box-right-text-fill:before { + content: "\e9a8"; +} +.di-chat-box-right-text:before { + content: "\e9a9"; +} +.di-chat-box-right:before { + content: "\e9aa"; +} +.di-chat-circle-left-dot-fill:before { + content: "\e9ab"; +} +.di-chat-circle-left-dot:before { + content: "\e9ac"; +} +.di-chat-circle-left-fill:before { + content: "\e9ad"; +} +.di-chat-circle-left-text-fill:before { + content: "\e9ae"; +} +.di-chat-circle-left-text:before { + content: "\e9af"; +} +.di-chat-circle-left:before { + content: "\e9b0"; +} +.di-chat-circle-right-dot-fill:before { + content: "\e9b1"; +} +.di-chat-circle-right-dot:before { + content: "\e9b2"; +} +.di-chat-circle-right-fill:before { + content: "\e9b3"; +} +.di-chat-circle-right-text-fill:before { + content: "\e9b4"; +} +.di-chat-circle-right-text:before { + content: "\e9b5"; +} +.di-chat-circle-right:before { + content: "\e9b6"; +} +.di-check-box:before { + content: "\e9b7"; +} +.di-check-circle-fill:before { + content: "\e9b8"; +} +.di-check-circle-line:before { + content: "\e9b9"; +} +.di-check-circle:before { + content: "\e9ba"; +} +.di-check-square-fill:before { + content: "\e9bb"; +} +.di-check-square-line:before { + content: "\e9bc"; +} +.di-check-square:before { + content: "\e9bd"; +} +.di-check:before { + content: "\e9be"; +} +.di-chevron-bar-contract:before { + content: "\e9bf"; +} +.di-chevron-bar-down:before { + content: "\e9c0"; +} +.di-chevron-bar-expand:before { + content: "\e9c1"; +} +.di-chevron-bar-left:before { + content: "\e9c2"; +} +.di-chevron-bar-right:before { + content: "\e9c3"; +} +.di-chevron-bar-up:before { + content: "\e9c4"; +} +.di-chevron-contract:before { + content: "\e9c5"; +} +.di-chevron-double-down:before { + content: "\e9c6"; +} +.di-chevron-double-left:before { + content: "\e9c7"; +} +.di-chevron-double-right:before { + content: "\e9c8"; +} +.di-chevron-double-up:before { + content: "\e9c9"; +} +.di-chevron-down:before { + content: "\e9ca"; +} +.di-chevron-expand:before { + content: "\e9cb"; +} +.di-chevron-left:before { + content: "\e9cc"; +} +.di-chevron-right:before { + content: "\e9cd"; +} +.di-chevron-up:before { + content: "\e9ce"; +} +.di-clipboard-check:before { + content: "\e9cf"; +} +.di-clipboard-data:before { + content: "\e9d0"; +} +.di-clipboard-text:before { + content: "\e9d1"; +} +.di-clipboard:before { + content: "\e9d2"; +} +.di-clock-fill:before { + content: "\e9d3"; +} +.di-clock-history:before { + content: "\e9d4"; +} +.di-clock:before { + content: "\e9d5"; +} +.di-cloud-arrow-down-fill:before { + content: "\e9d6"; +} +.di-cloud-arrow-down:before { + content: "\e9d7"; +} +.di-cloud-arrow-up-fill:before { + content: "\e9d8"; +} +.di-cloud-arrow-up:before { + content: "\e9d9"; +} +.di-cloud-check-fill:before { + content: "\e9da"; +} +.di-cloud-check:before { + content: "\e9db"; +} +.di-cloud-fill:before { + content: "\e9dc"; +} +.di-cloud:before { + content: "\e9dd"; +} +.di-code-square:before { + content: "\e9de"; +} +.di-code:before { + content: "\e9df"; +} +.di-collection-fill:before { + content: "\e9e0"; +} +.di-collection:before { + content: "\e9e1"; +} +.di-compass-fill:before { + content: "\e9e2"; +} +.di-compass:before { + content: "\e9e3"; +} +.di-cone:before { + content: "\e9e4"; +} +.di-controller-fill:before { + content: "\e9e5"; +} +.di-controller:before { + content: "\e9e6"; +} +.di-copyright-sign:before { + content: "\e9e7"; +} +.di-copyright-symbol:before { + content: "\e9e8"; +} +.di-COVID-19-fill:before { + content: "\e9e9"; +} +.di-COVID-19:before { + content: "\e9ea"; +} +.di-cpu-fill:before { + content: "\e9eb"; +} +.di-cpu:before { + content: "\e9ec"; +} +.di-crop:before { + content: "\e9ed"; +} +.di-cup-fill:before { + content: "\e9ee"; +} +.di-cup-straw:before { + content: "\e9ef"; +} +.di-cup:before { + content: "\e9f0"; +} +.di-cursor-fill:before { + content: "\e9f1"; +} +.di-cursor-text:before { + content: "\e9f2"; +} +.di-cursor:before { + content: "\e9f3"; +} +.di-dash-circle-fill:before { + content: "\e9f4"; +} +.di-dash-circle:before { + content: "\e9f5"; +} +.di-dash-square-fill:before { + content: "\e9f6"; +} +.di-dash-square:before { + content: "\e9f7"; +} +.di-dash:before { + content: "\e9f8"; +} +.di-dashboard:before { + content: "\e9f9"; +} +.di-diagram:before { + content: "\e9fa"; +} +.di-diamond-fill:before { + content: "\e9fb"; +} +.di-diamond:before { + content: "\e9fc"; +} +.di-diamond2:before { + content: "\e9fd"; +} +.di-display-fill:before { + content: "\e9fe"; +} +.di-display:before { + content: "\e9ff"; +} +.di-dns:before { + content: "\ea00"; +} +.di-door-closed-fill:before { + content: "\ea01"; +} +.di-door-closed:before { + content: "\ea02"; +} +.di-download_app:before { + content: "\ea03"; +} +.di-download:before { + content: "\ea04"; +} +.di-email-fill-notification:before { + content: "\ea05"; +} +.di-email-fill-open:before { + content: "\ea06"; +} +.di-email-fill:before { + content: "\ea07"; +} +.di-email-notification:before { + content: "\ea08"; +} +.di-email-open:before { + content: "\ea09"; +} +.di-email:before { + content: "\ea0a"; +} +.di-emergenc_call:before { + content: "\ea0b"; +} +.di-emergenc:before { + content: "\ea0c"; +} +.di-exclamation-chat-fill:before { + content: "\ea0d"; +} +.di-exclamation-chat:before { + content: "\ea0e"; +} +.di-exclamation-circle-fill:before { + content: "\ea0f"; +} +.di-exclamation-circle:before { + content: "\ea10"; +} +.di-exclamation-diamond-fill:before { + content: "\ea11"; +} +.di-exclamation-diamond:before { + content: "\ea12"; +} +.di-exclamation-square-circle:before { + content: "\ea13"; +} +.di-exclamation-square-fill:before { + content: "\ea14"; +} +.di-exclamation-square:before { + content: "\ea15"; +} +.di-exclamation-triangle-fill:before { + content: "\ea16"; +} +.di-exclamation-triangle:before { + content: "\ea17"; +} +.di-exclamation:before { + content: "\ea18"; +} +.di-exclude:before { + content: "\ea19"; +} +.di-face-id:before { + content: "\ea1a"; +} +.di-facebook-circle:before { + content: "\ea1b"; +} +.di-facebook-square:before { + content: "\ea1c"; +} +.di-favorite-hart:before { + content: "\ea1d"; +} +.di-favorite-star:before { + content: "\ea1e"; +} +.di-file-copy:before { + content: "\ea1f"; +} +.di-file-excel:before { + content: "\ea20"; +} +.di-file-image:before { + content: "\ea21"; +} +.di-file-keynote:before { + content: "\ea22"; +} +.di-file-numbers:before { + content: "\ea23"; +} +.di-file-powerpoint:before { + content: "\ea24"; +} +.di-file-text:before { + content: "\ea25"; +} +.di-file-word:before { + content: "\ea26"; +} +.di-file:before { + content: "\ea27"; +} +.di-filter-circle-fill:before { + content: "\ea28"; +} +.di-filter-circle:before { + content: "\ea29"; +} +.di-filter-left:before { + content: "\ea2a"; +} +.di-filter-right:before { + content: "\ea2b"; +} +.di-filter-square-fill:before { + content: "\ea2c"; +} +.di-filter-square:before { + content: "\ea2d"; +} +.di-filter:before { + content: "\ea2e"; +} +.di-finder-folder:before { + content: "\ea2f"; +} +.di-finder:before { + content: "\ea30"; +} +.di-fingerprint:before { + content: "\ea31"; +} +.di-flag-fill:before { + content: "\ea32"; +} +.di-flag:before { + content: "\ea33"; +} +.di-flashlight:before { + content: "\ea34"; +} +.di-folder-check:before { + content: "\ea35"; +} +.di-folder-delet:before { + content: "\ea36"; +} +.di-folder-fill:before { + content: "\ea37"; +} +.di-folder-open-fill:before { + content: "\ea38"; +} +.di-folder-open:before { + content: "\ea39"; +} +.di-folder-plus:before { + content: "\ea3a"; +} +.di-folder-share:before { + content: "\ea3b"; +} +.di-folder-x:before { + content: "\ea3c"; +} +.di-folder:before { + content: "\ea3d"; +} +.di-font:before { + content: "\ea3e"; +} +.di-fullscreen-exit:before { + content: "\ea3f"; +} +.di-fullscreen:before { + content: "\ea40"; +} +.di-gear-fill:before { + content: "\ea41"; +} +.di-gear-square-fill:before { + content: "\ea42"; +} +.di-gear-system-update:before { + content: "\ea43"; +} +.di-gear:before { + content: "\ea44"; +} +.di-gem:before { + content: "\ea45"; +} +.di-geo-alt:before { + content: "\ea46"; +} +.di-geo-location:before { + content: "\ea47"; +} +.di-geo-my-location:before { + content: "\ea48"; +} +.di-gift-fill:before { + content: "\ea49"; +} +.di-gift:before { + content: "\ea4a"; +} +.di-graph-down:before { + content: "\ea4b"; +} +.di-graph-up:before { + content: "\ea4c"; +} +.di-grid-2-fill:before { + content: "\ea4d"; +} +.di-grid-3-fill:before { + content: "\ea4e"; +} +.di-grid:before { + content: "\ea4f"; +} +.di-grip-horizontal:before { + content: "\ea50"; +} +.di-grip-vertical:before { + content: "\ea51"; +} +.di-group:before { + content: "\ea52"; +} +.di-group2:before { + content: "\ea53"; +} +.di-hand-index:before { + content: "\ea54"; +} +.di-harf:before { + content: "\ea55"; +} +.di-hart-fill:before { + content: "\ea56"; +} +.di-hart-half:before { + content: "\ea57"; +} +.di-hash:before { + content: "\ea58"; +} +.di-hdd-network:before { + content: "\ea59"; +} +.di-hdd:before { + content: "\ea5a"; +} +.di-hexagon-fill:before { + content: "\ea5b"; +} +.di-hexagon-harf:before { + content: "\ea5c"; +} +.di-hexagon:before { + content: "\ea5d"; +} +.di-history:before { + content: "\ea5e"; +} +.di-home-fill:before { + content: "\ea5f"; +} +.di-home:before { + content: "\ea60"; +} +.di-hotel_bed:before { + content: "\ea61"; +} +.di-hotel:before { + content: "\ea62"; +} +.di-image-fill:before { + content: "\ea63"; +} +.di-image:before { + content: "\ea64"; +} +.di-images:before { + content: "\ea65"; +} +.di-info-box:before { + content: "\ea66"; +} +.di-info-circle:before { + content: "\ea67"; +} +.di-info-fill:before { + content: "\ea68"; +} +.di-info:before { + content: "\ea69"; +} +.di-instagram-circle:before { + content: "\ea6a"; +} +.di-instagram-fill:before { + content: "\ea6b"; +} +.di-instagram:before { + content: "\ea6c"; +} +.di-intersect:before { + content: "\ea6d"; +} +.di-justify-left:before { + content: "\ea6e"; +} +.di-justify-right:before { + content: "\ea6f"; +} +.di-justify:before { + content: "\ea70"; +} +.di-kakao-alim-talk:before { + content: "\ea71"; +} +.di-kakao-circle:before { + content: "\ea72"; +} +.di-kakao-firend-talk:before { + content: "\ea73"; +} +.di-kakao-story-circle:before { + content: "\ea74"; +} +.di-kakao-story:before { + content: "\ea75"; +} +.di-kakao-talk:before { + content: "\ea76"; +} +.di-key:before { + content: "\ea77"; +} +.di-keyboard:before { + content: "\ea78"; +} +.di-label:before { + content: "\ea79"; +} +.di-language:before { + content: "\ea7a"; +} +.di-laptop-fill:before { + content: "\ea7b"; +} +.di-laptop:before { + content: "\ea7c"; +} +.di-layer-back:before { + content: "\ea7d"; +} +.di-layer-front:before { + content: "\ea7e"; +} +.di-layer:before { + content: "\ea7f"; +} +.di-lightning-fill:before { + content: "\ea80"; +} +.di-lightning:before { + content: "\ea81"; +} +.di-list-check:before { + content: "\ea82"; +} +.di-list:before { + content: "\ea83"; +} +.di-local-airport-land:before { + content: "\ea84"; +} +.di-local-airport:before { + content: "\ea85"; +} +.di-local-atm:before { + content: "\ea86"; +} +.di-local-bar:before { + content: "\ea87"; +} +.di-local-cafe:before { + content: "\ea88"; +} +.di-local-dining:before { + content: "\ea89"; +} +.di-local-ev-station:before { + content: "\ea8a"; +} +.di-local-fastfood:before { + content: "\ea8b"; +} +.di-local-gas-station:before { + content: "\ea8c"; +} +.di-local-hospital:before { + content: "\ea8d"; +} +.di-local-laundry:before { + content: "\ea8e"; +} +.di-local-library:before { + content: "\ea8f"; +} +.di-local-museum:before { + content: "\ea90"; +} +.di-local-restaurant:before { + content: "\ea91"; +} +.di-local-store:before { + content: "\ea92"; +} +.di-lock-open:before { + content: "\ea93"; +} +.di-lock:before { + content: "\ea94"; +} +.di-map-fill:before { + content: "\ea95"; +} +.di-map:before { + content: "\ea96"; +} +.di-menu-close-fill:before { + content: "\ea97"; +} +.di-menu-close:before { + content: "\ea98"; +} +.di-menu-open-fill:before { + content: "\ea99"; +} +.di-menu-open:before { + content: "\ea9a"; +} +.di-mic-fill:before { + content: "\ea9b"; +} +.di-mic-mute-fill:before { + content: "\ea9c"; +} +.di-mic-mute:before { + content: "\ea9d"; +} +.di-mic:before { + content: "\ea9e"; +} +.di-minus:before { + content: "\ea9f"; +} +.di-mobile-android:before { + content: "\eaa0"; +} +.di-mobile-flip:before { + content: "\eaa1"; +} +.di-mobile-fold:before { + content: "\eaa2"; +} +.di-mobile-ios:before { + content: "\eaa3"; +} +.di-mobile-pad:before { + content: "\eaa4"; +} +.di-mobile-store:before { + content: "\eaa5"; +} +.di-mobile:before { + content: "\eaa6"; +} +.di-moon-crescent:before { + content: "\eaa7"; +} +.di-moon-half:before { + content: "\eaa8"; +} +.di-moon:before { + content: "\eaa9"; +} +.di-more-horiz:before { + content: "\eaaa"; +} +.di-more-vert:before { + content: "\eaab"; +} +.di-movie-fill:before { + content: "\eaac"; +} +.di-movie:before { + content: "\eaad"; +} +.di-music-backward-fill:before { + content: "\eaae"; +} +.di-music-backward:before { + content: "\eaaf"; +} +.di-music-forward-fill:before { + content: "\eab0"; +} +.di-music-forward:before { + content: "\eab1"; +} +.di-music-list:before { + content: "\eab2"; +} +.di-music-pause-fill:before { + content: "\eab3"; +} +.di-music-pause:before { + content: "\eab4"; +} +.di-music-play-fill:before { + content: "\eab5"; +} +.di-music-play:before { + content: "\eab6"; +} +.di-music-stop-fill:before { + content: "\eab7"; +} +.di-music-stop:before { + content: "\eab8"; +} +.di-music:before { + content: "\eab9"; +} +.di-naver-blog:before { + content: "\eaba"; +} +.di-naver-circle:before { + content: "\eabb"; +} +.di-naver:before { + content: "\eabc"; +} +.di-news:before { + content: "\eabd"; +} +.di-option:before { + content: "\eabe"; +} +.di-paperclip:before { + content: "\eabf"; +} +.di-payment:before { + content: "\eac0"; +} +.di-pen-fill:before { + content: "\eac1"; +} +.di-pen:before { + content: "\eac2"; +} +.di-pencil-fill:before { + content: "\eac3"; +} +.di-pencil-square:before { + content: "\eac4"; +} +.di-pencil:before { + content: "\eac5"; +} +.di-person-fill:before { + content: "\eac6"; +} +.di-person:before { + content: "\eac7"; +} +.di-personal-information:before { + content: "\eac8"; +} +.di-pet:before { + content: "\eac9"; +} +.di-power:before { + content: "\eaca"; +} +.di-print:before { + content: "\eacb"; +} +.di-problem:before { + content: "\eacc"; +} +.di-profile-smile:before { + content: "\eacd"; +} +.di-profile:before { + content: "\eace"; +} +.di-push-assemble:before { + content: "\eacf"; +} +.di-push-fill:before { + content: "\ead0"; +} +.di-push-notification:before { + content: "\ead1"; +} +.di-push:before { + content: "\ead2"; +} +.di-question-answer:before { + content: "\ead3"; +} +.di-question-circle-fill:before { + content: "\ead4"; +} +.di-question-circle:before { + content: "\ead5"; +} +.di-question-diamond-fill:before { + content: "\ead6"; +} +.di-question-diamond:before { + content: "\ead7"; +} +.di-question-square-fill:before { + content: "\ead8"; +} +.di-question-square:before { + content: "\ead9"; +} +.di-question:before { + content: "\eada"; +} +.di-radio:before { + content: "\eadb"; +} +.di-receipt:before { + content: "\eadc"; +} +.di-refresh-fill:before { + content: "\eadd"; +} +.di-refresh:before { + content: "\eade"; +} +.di-reset-fill:before { + content: "\eadf"; +} +.di-reset:before { + content: "\eae0"; +} +.di-rotation-lock:before { + content: "\eae1"; +} +.di-rotation:before { + content: "\eae2"; +} +.di-rwis:before { + content: "\eae3"; +} +.di-search:before { + content: "\eae4"; +} +.di-server:before { + content: "\eae5"; +} +.di-share:before { + content: "\eae6"; +} +.di-shield:before { + content: "\eae7"; +} +.di-shift-fill:before { + content: "\eae8"; +} +.di-shift:before { + content: "\eae9"; +} +.di-shuffle:before { + content: "\eaea"; +} +.di-silent:before { + content: "\eaeb"; +} +.di-sim:before { + content: "\eaec"; +} +.di-slash:before { + content: "\eaed"; +} +.di-sliders:before { + content: "\eaee"; +} +.di-smart-watch:before { + content: "\eaef"; +} +.di-sort-alpha-down-alt:before { + content: "\eaf0"; +} +.di-sort-alpha-down:before { + content: "\eaf1"; +} +.di-sort-alpha-up-alt:before { + content: "\eaf2"; +} +.di-sort-alpha-up:before { + content: "\eaf3"; +} +.di-sort-down-alt:before { + content: "\eaf4"; +} +.di-sort-down:before { + content: "\eaf5"; +} +.di-sort-numeric-down-alt:before { + content: "\eaf6"; +} +.di-sort-numeric-down:before { + content: "\eaf7"; +} +.di-sort-numeric-up-alt:before { + content: "\eaf8"; +} +.di-sort-numeric-up:before { + content: "\eaf9"; +} +.di-sort-up-alt:before { + content: "\eafa"; +} +.di-sort-up:before { + content: "\eafb"; +} +.di-sound:before { + content: "\eafc"; +} +.di-soundwave:before { + content: "\eafd"; +} +.di-square-fill:before { + content: "\eafe"; +} +.di-square:before { + content: "\eaff"; +} +.di-star-circle:before { + content: "\eb00"; +} +.di-star-fill:before { + content: "\eb01"; +} +.di-star:before { + content: "\eb02"; +} +.di-statistics-circle:before { + content: "\eb03"; +} +.di-statistics:before { + content: "\eb04"; +} +.di-sticky-fill:before { + content: "\eb05"; +} +.di-sticky:before { + content: "\eb06"; +} +.di-stoplights-fill:before { + content: "\eb07"; +} +.di-stoplights:before { + content: "\eb08"; +} +.di-sunny:before { + content: "\eb09"; +} +.di-tab:before { + content: "\eb0a"; +} +.di-table:before { + content: "\eb0b"; +} +.di-terminal-fill:before { + content: "\eb0c"; +} +.di-terminal:before { + content: "\eb0d"; +} +.di-ternnel:before { + content: "\eb0e"; +} +.di-textarea-t:before { + content: "\eb0f"; +} +.di-textarea:before { + content: "\eb10"; +} +.di-thermometer-full:before { + content: "\eb11"; +} +.di-thermometer-half:before { + content: "\eb12"; +} +.di-thermometer:before { + content: "\eb13"; +} +.di-time-end:before { + content: "\eb14"; +} +.di-time-full:before { + content: "\eb15"; +} +.di-time-start:before { + content: "\eb16"; +} +.di-toggle-off:before { + content: "\eb17"; +} +.di-toggle-on:before { + content: "\eb18"; +} +.di-toggle:before { + content: "\eb19"; +} +.di-train:before { + content: "\eb1a"; +} +.di-trash-fill:before { + content: "\eb1b"; +} +.di-trash:before { + content: "\eb1c"; +} +.di-truck-fill:before { + content: "\eb1d"; +} +.di-truck:before { + content: "\eb1e"; +} +.di-ui-checks-grid:before { + content: "\eb1f"; +} +.di-ui-radios-grid:before { + content: "\eb20"; +} +.di-upload:before { + content: "\eb21"; +} +.di-vibration:before { + content: "\eb22"; +} +.di-vms:before { + content: "\eb23"; +} +.di-volume-down-fill:before { + content: "\eb24"; +} +.di-volume-down:before { + content: "\eb25"; +} +.di-volume-mute-fill:before { + content: "\eb26"; +} +.di-volume-mute:before { + content: "\eb27"; +} +.di-volume-off-fill:before { + content: "\eb28"; +} +.di-volume-off:before { + content: "\eb29"; +} +.di-volume-up-fill:before { + content: "\eb2a"; +} +.di-volume-up:before { + content: "\eb2b"; +} +.di-weather-cloud-fill:before { + content: "\eb2c"; +} +.di-weather-cloud:before { + content: "\eb2d"; +} +.di-weather-cloudy-gusts:before { + content: "\eb2e"; +} +.di-weather-cloudy-windy:before { + content: "\eb2f"; +} +.di-weather-cloudy:before { + content: "\eb30"; +} +.di-weather-fahrenheit:before { + content: "\eb31"; +} +.di-weather-fog:before { + content: "\eb32"; +} +.di-weather-hail:before { + content: "\eb33"; +} +.di-weather-humidity:before { + content: "\eb34"; +} +.di-weather-hurricane:before { + content: "\eb35"; +} +.di-weather-lightning:before { + content: "\eb36"; +} +.di-weather-rain:before { + content: "\eb37"; +} +.di-weather-rainny:before { + content: "\eb38"; +} +.di-weather-snow:before { + content: "\eb39"; +} +.di-weather-snowflake:before { + content: "\eb3a"; +} +.di-weather-sunny-overcast:before { + content: "\eb3b"; +} +.di-weather-sunny:before { + content: "\eb3c"; +} +.di-weather-thunderstorm:before { + content: "\eb3d"; +} +.di-weather-umbrella:before { + content: "\eb3e"; +} +.di-weather-windy:before { + content: "\eb3f"; +} +.di-whater:before { + content: "\eb40"; +} +.di-whather-celsius:before { + content: "\eb41"; +} +.di-wifi-1:before { + content: "\eb42"; +} +.di-wifi-2:before { + content: "\eb43"; +} +.di-wifi-off:before { + content: "\eb44"; +} +.di-wifi:before { + content: "\eb45"; +} +.di-window:before { + content: "\eb46"; +} +.di-x-circle-fill:before { + content: "\eb47"; +} +.di-x-circle:before { + content: "\eb48"; +} +.di-x-square-fill:before { + content: "\eb49"; +} +.di-x-square:before { + content: "\eb4a"; +} +.di-x:before { + content: "\eb4b"; +} +.di-zoom-in:before { + content: "\eb4c"; +} +.di-zoom-out:before { + content: "\eb4d"; +} +.di-server-line:before { + content: "\e907"; +} diff --git a/packages/common/business_components/src/env.ts b/packages/common/business_components/src/env.ts new file mode 100644 index 000000000..85dcfa2e2 --- /dev/null +++ b/packages/common/business_components/src/env.ts @@ -0,0 +1 @@ +export const BASE_ASSETS_PATH = process.env.BASE_ASSETS_PATH ?? "/assets"; diff --git a/packages/common/business_components/src/form.md b/packages/common/business_components/src/form.md new file mode 100644 index 000000000..4e9b786cf --- /dev/null +++ b/packages/common/business_components/src/form.md @@ -0,0 +1,93 @@ +## form 모듈 + +### 공통 모듈 중 chart를 다양한 테마로 적용시킬 수 있도록 해주는 모듈 + +  1. 폼 모듈 사용법 + +    1-1) 패키지 사용법 + +      (1) zod 사용법 +https://zod.dev/?id=introduction + +    1-2) 호출방법 + +      (1) form에 필요한 Form,FormField,useForm useForm,z, zodResolver import + +```js +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"; +``` + +      (2)zod 정의 + +```js +const formSchema = z.object({ + facilityName: z.string().min(2).max(50), + instlCmptnDt:z.string(), // FormField 내 name의 타입및 min max 구성 가능 + +}); + + +const form = useForm>({ + resolver: zodResolver(formSchema), + //초기값 세팅 z.object에 등록한 값은 반드시 세팅해야함. + defaultValues: { + facilityName :'', + instlCmptnDt:'', // + + }, + }); + + function onSubmit(values: z.infer) { + // form submit 함수 + console.log(values);// zod.object에 등록하고 form에 구현한 값이 출력됨. + } + + + +``` + +      (3) form구성 + +```js +
+ +
+ ( + + // zod에 등록된 name 값의 폼 객체 생성 + //select box 삽입시 + + + + )} + /> +
+
+ ( + + // zod에 등록된 name 값의 폼 객체 생성 + //input 삽입시 + + + + )} + /> +
+
+ +``` diff --git a/packages/common/business_components/src/index.css b/packages/common/business_components/src/index.css new file mode 100644 index 000000000..46896e552 --- /dev/null +++ b/packages/common/business_components/src/index.css @@ -0,0 +1,11 @@ + +@layer base {} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/packages/common/business_components/src/index.ts b/packages/common/business_components/src/index.ts new file mode 100644 index 000000000..4aedf5936 --- /dev/null +++ b/packages/common/business_components/src/index.ts @@ -0,0 +1 @@ +export * from "./ui"; diff --git a/packages/common/business_components/src/lib/utils.ts b/packages/common/business_components/src/lib/utils.ts new file mode 100644 index 000000000..a5ef19350 --- /dev/null +++ b/packages/common/business_components/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx new file mode 100644 index 000000000..82499306c --- /dev/null +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -0,0 +1,42 @@ +"use client" +import type { CSSProperties } from "react"; +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig +} from "./lib/chart"; +import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; + +export default function Chart(props: { chartConfig: ChartConfig; chartData: any[] | undefined; chartTitle: string}) { + return ( + + + {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {props.chartTitle} +
): null} +
+ + + + value.slice(0, 3)} + /> + }/> + + + +
+
+
+ ) +} \ 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 new file mode 100644 index 000000000..f88b35476 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx @@ -0,0 +1,47 @@ +"use client" + +import { Pie, PieChart } from "recharts"; + +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig +} from "./lib/chart"; + +// eslint-disable-next-line react-refresh/only-export-components +export default (props: { chartConfig: ChartConfig; chartData: never[] | undefined; chartTitle:string}) =>{ + return ( + + + {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {props.chartTitle} +
): null} +
+ + + } + /> + + + +
+
+
+ ) +} diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx new file mode 100644 index 000000000..5f2ae4715 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -0,0 +1,108 @@ +"use client" +import type { CSSProperties } from "react"; +import { Bar, CartesianGrid, ComposedChart, Legend, Line, Tooltip, XAxis, YAxis } from "recharts"; +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + type ChartConfig +} from "./lib/chart"; +import React from "react"; + +// 툴팁 커스텀 css +/** 차트 기본 스타일 */ +const styles = { + tooltipDiv: { + borderRadius: "5px", + backgroundColor: "white", + padding: "10px", + boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", + width: "180px", // 적절한 크기로 설정 + } as React.CSSProperties, + tooltipLabel: { + fontWeight: "bold", + fontSize: "12px", + marginBottom: "5px", + borderBottom: "1px solid #ddd", + paddingBottom: "3px", + } as React.CSSProperties, + tooltipText: { + fontSize: "11px", + fontWeight:"bold", + } as React.CSSProperties, + legendContainer: { + display: "flex", + justifyContent: "center", + gap: "20px", + fontSize: "9px", + fontWeight : "bold", + marginTop: "10px", + } as CSSProperties, +}; + +// 커스텀 툴팁 +const CustomTooltip = ({ active, payload, label }) => { + if (active && payload && payload.length) { + return ( +
+

{label}

+ {payload.map((item, index) => ( +
+ {/* 색상 박스 추가 (범례 색상과 동일) */} +
+ {`${item.name}: ${item.value}`} +
+ ))} +
+ ); + } + return null; +}; + +// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) +const CustomLegend = (props: any) => { + const { payload } = props; + return ( +
+ {payload.map((entry, index) => ( +
+
+ {entry.value} +
+ ))} +
+ ); +}; + +export default function LineBarchart(props: { chartConfig: ChartConfig; chartData: any[] | undefined; chartTitle:string }) { + return ( + + + {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {props.chartTitle} +
): null} +
+ + + + + {/* 왼쪽 Y축: lineVal (정상) */} + + {/* 오른쪽 Y축: barVal (위에서 아래로 반전) */} + + } /> + + {/* 바 차트 (오른쪽 Y축 사용, 위에서 아래로) */} + + {/* 라인 차트 (왼쪽 Y축 사용) */} + + + +
+
+
+ ) +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx new file mode 100644 index 000000000..136034e00 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -0,0 +1,121 @@ +"use client" +import type { CSSProperties } from "react"; +import { + Card, + CardContent +} from "./lib/card"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig +} from "./lib/chart"; +import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; + +/** 차트 기본 스타일 */ +const styles = { + tooltipDiv: { + borderRadius: "5px", + backgroundColor: "white", + padding: "10px", + boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", + width: "180px", // 적절한 크기로 설정 + } as React.CSSProperties, + tooltipLabel: { + fontWeight: "bold", + fontSize: "12px", + marginBottom: "5px", + borderBottom: "1px solid #ddd", + paddingBottom: "3px", + } as React.CSSProperties, + tooltipText: { + fontSize: "11px", + fontWeight:"bold", + } as React.CSSProperties, + legendContainer: { + display: "flex", + justifyContent: "center", + gap: "20px", + fontSize: "9px", + fontWeight : "bold", + marginTop: "10px", + } as CSSProperties, +}; + +// 커스텀 툴팁 +const CustomTooltip = ({ active, payload, label }) => { + if (active && payload && payload.length) { + return ( +
+

{label}

+ {payload.map((item, index) => ( +
+ {/* 색상 박스 추가 (범례 색상과 동일) */} +
+ {`${item.name}: ${item.value}`} +
+ ))} +
+ ); + } + return null; +}; + +// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) +const CustomLegend = (props: any) => { + const { payload } = props; + return ( +
+ {payload.map((entry, index) => ( +
+
+ {entry.value} +
+ ))} +
+ ); +}; + +type DataType = {label : string; val:number}; +type ChartDataType = { + chartData1: DataType[]; + chartData2: DataType[]; +}; + +export default function Linechart(props: { chartConfig: ChartConfig; chartData: ChartDataType; chartTitle:string}) { + const mergedData = Object.values(props.chartData).reduce((acc, dataset) => { + dataset.forEach((entry, index) => { + if (!acc[index]) { + acc[index] = { label: entry.label }; // label 추가 + } + const key = Object.keys(props.chartData).find(k => props.chartData[k] === dataset); + if (key) acc[index][key] = entry.val; + }); + return acc; + }, [] as any[]); + + return ( + + + {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {props.chartTitle} +
): null} +
+ + + + + + } /> + + {Object.keys(props.chartData).map((key, index) => ( + + ))} + + +
+
+
+ ) +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx new file mode 100644 index 000000000..fc5747f3a --- /dev/null +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -0,0 +1,48 @@ + +import React from 'react'; +import BarChart from './BarChart'; +import DoughnutChart from './DoughnutChart'; +import { type ChartConfig } from './lib/chart'; +import LineChart from './LineChart'; +import LineBarchart from './LineBarChart'; + +/**차트 FORM */ +const CHART_FORM = { + /**바 차트 형태*/ + BAR : 'bar', + /**라인 차트 형태*/ + LINE:'line', + /**도넛 차트 형태*/ + DN :'daughnut', + /**LINE,BAR차트 */ + LINEBAR : 'linebar' +}; + +function MainChart(props: { chartForm: string; chartConfig: any; chartData: any; chartTitle:string; }) { + if( props.chartForm == CHART_FORM.BAR ){ + return ( + + + ); + } else if( props.chartForm == CHART_FORM.LINE ){ + return ( + + + ); + } else if( props.chartForm == CHART_FORM.DN ){ + return ( + + + ); + } else if (props.chartForm == CHART_FORM.LINEBAR){ + return ( + + + ); + } +} + +export { MainChart }; +// eslint-disable-next-line react-refresh/only-export-components + export { CHART_FORM }; + export type { ChartConfig }; diff --git a/packages/common/business_components/src/ui/chart/lib/card.tsx b/packages/common/business_components/src/ui/chart/lib/card.tsx new file mode 100644 index 000000000..351d6566b --- /dev/null +++ b/packages/common/business_components/src/ui/chart/lib/card.tsx @@ -0,0 +1,77 @@ +import * as React from "react" + +import { cn } from "../../../lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } + diff --git a/packages/common/business_components/src/ui/chart/lib/chart.tsx b/packages/common/business_components/src/ui/chart/lib/chart.tsx new file mode 100644 index 000000000..3a69fd454 --- /dev/null +++ b/packages/common/business_components/src/ui/chart/lib/chart.tsx @@ -0,0 +1,364 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "../../../lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +