Merge remote-tracking branch 'origin/iipbs/sn/dev' into iipbs/sn/map
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
name: iipbs_sn
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [iipbs/sn/dev]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: appleboy/ssh-action@master
|
||||
with:
|
||||
port: 11022
|
||||
host: con.mind-one.co.kr
|
||||
username: mindone
|
||||
password: mindone!23
|
||||
script: |
|
||||
cd ./iipbs/front
|
||||
sh ./frontStartSn.sh
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ContentsDiv, MainDiv, Title } from '@common/business_components/ui'
|
||||
import React, { type ReactNode } from 'react'
|
||||
type LayoutNode = {
|
||||
children : ReactNode
|
||||
title : string
|
||||
}
|
||||
const ContentLayout = ({children, title} : LayoutNode) => {
|
||||
return (
|
||||
<MainDiv>
|
||||
<Title title={title} />
|
||||
<ContentsDiv>
|
||||
{children}
|
||||
</ContentsDiv>
|
||||
</MainDiv>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContentLayout
|
||||
@@ -0,0 +1,58 @@
|
||||
"use client"
|
||||
import { FileUpload, SelectBox } from '@common/business_components/ui';
|
||||
|
||||
import { Button } from '@common/components/ui';
|
||||
import React, { useState } from 'react'
|
||||
|
||||
const FileUploadForm = ({title}:{title:string}) => {
|
||||
const [files, setFiles] = useState<File[] | null>(null);
|
||||
const [selectedYear, setSelectedYear] = useState<string>('');
|
||||
|
||||
// 현재 년도부터 10년 전까지의 년도 옵션 생성
|
||||
const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { text: `${year}년`, val: year.toString() };
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='card-header'>
|
||||
<h3 className="text-2xl font-medium">{title}</h3>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="w-[400px]">
|
||||
<FileUpload
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
fileTypes=".xlsx,.xls"
|
||||
/>
|
||||
</div>
|
||||
<SelectBox
|
||||
className="w-50"
|
||||
selectClass="w-50 rounded-lg bg-white"
|
||||
label="관리년도"
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setSelectedYear(String(value))}
|
||||
/>
|
||||
<Button
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (!files?.[0] || !selectedYear) {
|
||||
alert('파일과 등록년도를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
// TODO: 파일 업로드 처리
|
||||
console.log('Upload file:', files[0], 'Year:', selectedYear);
|
||||
}}
|
||||
>
|
||||
등록
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileUploadForm
|
||||
@@ -0,0 +1,24 @@
|
||||
import { FormField } from '@common/business_components/ui'
|
||||
import React, { type ReactElement } from 'react'
|
||||
|
||||
interface FormSectionProps {
|
||||
children: ReactElement
|
||||
titleNm: string
|
||||
form: any
|
||||
name: string
|
||||
}
|
||||
|
||||
const FormSection = ({ children, titleNm, form, name }: FormSectionProps) => {
|
||||
return (
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">{titleNm}</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => children}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormSection
|
||||
@@ -0,0 +1,91 @@
|
||||
'use client'
|
||||
import ContentLayout from '@/components/ui/contentLayout'
|
||||
import { SearchDiv, SearchFormLeft, SelectBox, SearchFormRight, Form, UiTable } from '@common/business_components/ui'
|
||||
import ListDiv from '@common/business_components/ui/list_div'
|
||||
import { Button } from '@common/components/ui'
|
||||
import React, { type ReactNode } from 'react'
|
||||
type TitleName = {
|
||||
mainTitle: string
|
||||
firstCardTitle: string
|
||||
secondCardTitle: string
|
||||
}
|
||||
type TableInfoData = {
|
||||
headList: { id: string; title: string; align?: string; }[]
|
||||
tableData: { [key: string]: string | number | boolean | object }[];
|
||||
cellClickFunc: Function
|
||||
onPageChangeFunc: Function
|
||||
}
|
||||
type BaseLayout = {
|
||||
children: ReactNode
|
||||
titleName: TitleName
|
||||
form: any
|
||||
onSubmitEvent: Function
|
||||
graphBtn: boolean
|
||||
tableInfoData: TableInfoData
|
||||
}
|
||||
// TODO: 나중에 이벤트 추가되면 파라미터 추가 필요 ?
|
||||
/**
|
||||
*
|
||||
* @param children
|
||||
* @param titleName : 메인 타이틀, 첫번째 요소 타이틀, 두번째 요소 타이틀
|
||||
* @param form : zod 스키마
|
||||
* @param onSubmitEvent : 폼 이벤트 처리
|
||||
* @param graphBtn : 하단에 그래프 버튼 유무 처리
|
||||
* @param tableInfoData : 하단 테이블 관련 props 항목
|
||||
* @returns
|
||||
*/
|
||||
const BaseLayout = ({ children, titleName, form, onSubmitEvent, graphBtn, tableInfoData }: BaseLayout) => {
|
||||
return (
|
||||
<ContentLayout title={titleName.mainTitle}>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<SelectBox className="col-span-3" label='시설검색' selectArray={[{ text: '한강 유역청', val: '1' }]} onSelectValue={(val) => { }} selectClass={undefined} />
|
||||
<SelectBox label='' selectArray={[{ text: '경기도', val: '1' }]} className="col-span-3" onSelectValue={() => { }} selectClass='' />
|
||||
<SelectBox label='' selectArray={[{ text: '양평군', val: '1' }]} className="col-span-3" onSelectValue={() => { }} selectClass='' />
|
||||
<SelectBox label='' selectArray={[{ text: '양평하수처리장', val: '1' }]} className="col-span-3" onSelectValue={() => { }} selectClass='' />
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button>조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">{titleName.firstCardTitle}</h3>
|
||||
</div>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmitEvent)}>
|
||||
<div className="card !m-[1.2rem]">
|
||||
{/* 폼 정보 입력 칸 */}
|
||||
{children}
|
||||
<div className="flex justify-end mt-1">
|
||||
<Button type="submit">자료업로드</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</ListDiv>
|
||||
<ListDiv>
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl font-medium">{titleName.secondCardTitle}</h3>
|
||||
<div>
|
||||
<Button className="!mr-[1.2rem]">삭제</Button>
|
||||
{/* 그래프 있을 시 */}
|
||||
{graphBtn ? <Button className="!mr-[1.2rem]">그래프</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
<UiTable
|
||||
headName=''
|
||||
headlist={tableInfoData.headList}
|
||||
tableData={tableInfoData.tableData}
|
||||
total={0}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={(e) => { tableInfoData.cellClickFunc(e) }}
|
||||
onPageChange={(e) => { tableInfoData.onPageChangeFunc(e) }}
|
||||
/>
|
||||
</ListDiv>
|
||||
</ContentLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default BaseLayout
|
||||
@@ -0,0 +1,175 @@
|
||||
"use client"
|
||||
|
||||
import { z } from "zod"
|
||||
import { useForm, useFieldArray } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormUi, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox } from "@common/business_components/ui"
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, Button } from "@common/components/ui"
|
||||
import { UiTable } from "@common/business_components/ui"
|
||||
import React from "react"
|
||||
import ContentLayout from "@/components/ui/contentLayout"
|
||||
import ListDiv from "@common/business_components/ui/list_div"
|
||||
import BaseLayout from "@/ii/BaseLayout"
|
||||
// TODO: 컴포넌트 호출 ~ 필요 코드 수정해야댐
|
||||
// zod 스키마 정의
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
rdiiArea: z.string(),
|
||||
rtkSource: z.string(),
|
||||
unitFactors: z.array(z.object({
|
||||
response: z.string(),
|
||||
R: z.string(),
|
||||
T: z.string(),
|
||||
K: z.string(),
|
||||
})),
|
||||
initialDepths: z.array(z.object({
|
||||
response: z.string(),
|
||||
Dmax: z.string(),
|
||||
Drec: z.string(),
|
||||
Do: z.string(),
|
||||
}))
|
||||
})
|
||||
|
||||
export default function RTKForm() {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
rdiiArea: "",
|
||||
rtkSource: "",
|
||||
unitFactors: [
|
||||
{ response: "Short-Term", R: "", T: "", K: "" },
|
||||
{ response: "Medium-Term", R: "", T: "", K: "" },
|
||||
{ response: "Long-Term", R: "", T: "", K: "" },
|
||||
],
|
||||
initialDepths: [
|
||||
{ response: "Short-Term", Dmax: "", Drec: "", Do: "" },
|
||||
{ response: "Medium-Term", Dmax: "", Drec: "", Do: "" },
|
||||
{ response: "Long-Term", Dmax: "", Drec: "", Do: "" },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const { fields: unitFactorFields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "unitFactors"
|
||||
})
|
||||
|
||||
const { fields: initialDepthFields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "initialDepths"
|
||||
})
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
console.log("✅ 제출 데이터:", data)
|
||||
}
|
||||
|
||||
// 단위도 계수 테이블 설정
|
||||
const unitFactorHeadlist = [
|
||||
{ id: 'response', title: 'Response' },
|
||||
{ id: 'R', title: 'R' },
|
||||
{ id: 'T', title: 'T' },
|
||||
{ id: 'K', title: 'K' }
|
||||
]
|
||||
|
||||
// 초기 차단 깊이 테이블 설정
|
||||
const initialDepthHeadlist = [
|
||||
{ id: 'response', title: 'Response' },
|
||||
{ id: 'Dmax', title: 'Dmax' },
|
||||
{ id: 'Drec', title: 'Drec' },
|
||||
{ id: 'Do', title: 'Do' }
|
||||
]
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'no', title: '번호' },
|
||||
{ id: 'hasu', title: '하수처리장' },
|
||||
{ id: 'guyeok', title: '처리구역' },
|
||||
{ id: 'cnfcj', title: '출처' },
|
||||
{ id: 'time', title: '입력시간' }
|
||||
]
|
||||
// 테이블 페이지 변경 핸들러
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('Page changed:', page)
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: '모의정보 설정', firstCardTitle: '모의정보 설정', secondCardTitle: 'RTX계수 입력현황' }} form={form} onSubmitEvent={() => { }} graphBtn={false} tableInfoData={{ headList: rtxNmHeadlist, tableData: [{}], cellClickFunc: () => { }, onPageChangeFunc: () => { } }}>
|
||||
<div className="space-y-[1.2rem]">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 처리구역</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<SelectBox label="" selectArray={[{ text: '양평', val: 1 }]} onSelectValue={() => { }} selectClass="" className="w-[150px]" />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ RDII 기여면적(ha)</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rdiiArea"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-10 text-center text-xl w-[150px]"
|
||||
placeholder=""
|
||||
type=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ RTK계수 출처</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-10 text-center text-xl w-[250px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 테이블 컨테이너 */}
|
||||
<div className="mt-[1.2rem]">
|
||||
<div className="grid grid-cols-2">
|
||||
<div>
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ 단위도 계수</div>
|
||||
<UiTable
|
||||
headName=""
|
||||
className="w-[400px]"
|
||||
tableData={unitFactorFields}
|
||||
headlist={unitFactorHeadlist}
|
||||
pageSize={10}
|
||||
total={0}
|
||||
onPageChange={handlePageChange}
|
||||
cellClick={undefined}
|
||||
>
|
||||
{null}
|
||||
</UiTable>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ 초기 차단 깊이</div>
|
||||
<UiTable
|
||||
headName=""
|
||||
className="w-[400px]"
|
||||
tableData={initialDepthFields}
|
||||
headlist={initialDepthHeadlist}
|
||||
pageSize={10}
|
||||
total={0}
|
||||
onPageChange={handlePageChange}
|
||||
cellClick={undefined}
|
||||
>
|
||||
{null}
|
||||
</UiTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout >
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
"use client"
|
||||
|
||||
import { z } from "zod"
|
||||
import { useForm, useFieldArray } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { FormField, SelectBox } from "@common/business_components/ui"
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, DatePicker } from "@common/components/ui"
|
||||
import React, { useState } from "react"
|
||||
import BaseLayout from "@/ii/BaseLayout"
|
||||
|
||||
// zod 스키마 정의
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
})
|
||||
const region:{text:string,val:number}[] = [{text:'양평', val:1},{text:'강남',val:2},{text:'test',val:3}]
|
||||
export default function RTKForm() {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
}
|
||||
})
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'no', title: '번호' },
|
||||
{ id: 'hasu', title: '하수처리장' },
|
||||
{ id: 'guyeok', title: '처리구역' },
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'time', title: '입력시간' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "no": '1', 'hasu': '하수처리장', 'guyeok': '처리구역', 'fileNm': '파일', 'time': '2025-04-18' }
|
||||
]
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('Page changed:', page)
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseLayout titleName={{mainTitle: "관망도 등록",firstCardTitle: "SWMM5 파일 업로드",secondCardTitle: "SWMM 파일 업로드현황"}} form={form} onSubmitEvent={()=>{}} graphBtn={false} tableInfoData={{headList: rtxNmHeadlist, tableData: rtxNmDataList, cellClickFunc: ()=>{}, onPageChangeFunc: ()=>{}}} >
|
||||
{/* 기본 정보 입력 폼 */}
|
||||
<div className="space-y-[1.2rem]">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 처리구역</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={region} onSelectValue={()=>{}} selectClass="" className="w-[150px]"/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 입력일</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inputdate"
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }}
|
||||
mode={"single"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 파일위치</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file"/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 비고</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-17 text-center text-xl w-[200px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { z } from 'zod'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { FormField, SelectBox } from '@common/business_components/ui'
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DatePicker, Input } from '@common/components/ui'
|
||||
import BaseLayout from '@/ii/BaseLayout'
|
||||
// zod 스키마 정의
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
})
|
||||
const region:{text:string,val:number}[] = [{text:'양평', val:1},{text:'강남',val:2},{text:'test',val:3}]
|
||||
const Page = () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
}
|
||||
})
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'no', title: '번호' },
|
||||
{ id: 'hasu', title: '하수처리장' },
|
||||
{ id: 'guyeok', title: '처리구역' },
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'time', title: '입력시간' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "no": '1', 'hasu': '하수처리장', 'guyeok': '처리구역', 'fileNm': '파일', 'time': '2025-04-18' }
|
||||
]
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: '티센 폴리곤 등록', firstCardTitle: '맨홀별 티센 폴리곤(shape파일) 업로드', secondCardTitle: '수용가-맨홀 매칭 파일 업로드 현황' }} form={form} onSubmitEvent={() => { }} graphBtn={false} tableInfoData={{ headList:rtxNmHeadlist, tableData:rtxNmDataList, cellClickFunc: () => { }, onPageChangeFunc: () => { } }}>
|
||||
<div className="space-y-[1.2rem]">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 처리구역</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={region} onSelectValue={() => { } } selectClass={undefined} className='w-[150px]'/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 입력일</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inputdate"
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }}
|
||||
mode={"single"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 파일위치</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 비고</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-17 text-center text-xl w-[200px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,99 @@
|
||||
"use client"
|
||||
import { z } from "zod"
|
||||
import { FormField} from '@common/business_components/ui'
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DatePicker, Input } from '@common/components/ui'
|
||||
import React, { useState } from 'react'
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import BaseLayout from "@/ii/BaseLayout"
|
||||
|
||||
// zod 스키마 정의
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
})
|
||||
|
||||
function RegistForm() {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
}
|
||||
})
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
console.log("✅ 제출 데이터:", data)
|
||||
}
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'no', title: '번호' },
|
||||
{ id: 'hasu', title: '하수처리장' },
|
||||
{ id: 'guyeok', title: '처리구역' },
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'time', title: '입력시간' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "no": '1', 'hasu': '하수처리장', 'guyeok': '처리구역', 'fileNm': '파일', 'time': '2025-04-18' }
|
||||
]
|
||||
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: "수용가-맨홀 등록", firstCardTitle: "수용가-맨홀 매칭 Shape파일 업로드", secondCardTitle: "수용가-맨홀 매칭 Shape파일 업로드 현황" }} form={form} onSubmitEvent={onSubmit} graphBtn={false} tableInfoData={{
|
||||
headList: rtxNmHeadlist, tableData: rtxNmDataList, cellClickFunc: () => { }, onPageChangeFunc: () => { }
|
||||
}}>
|
||||
{/* 기본 정보 입력 폼 */}
|
||||
<div className="space-y-[1.2rem]">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 처리구역</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<SelectTrigger className="w-[150px]">
|
||||
<SelectValue placeholder="선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="양평">양평</SelectItem>
|
||||
<SelectItem value="강남">강남</SelectItem>
|
||||
<SelectItem value="마포">마포</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 입력일</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inputdate"
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }}
|
||||
mode={"single"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 파일위치</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default RegistForm
|
||||
@@ -0,0 +1,114 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import BaseLayout from '../../BaseLayout'
|
||||
import { FormField, SelectBox } from '@common/business_components/ui'
|
||||
import { DatePicker, Input } from '@common/components/ui'
|
||||
import { z } from 'zod'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import FormSection from '@/components/ui/formSection'
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
suyeonga: z.number(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
})
|
||||
const region: { text: string, val: number }[] = [{ text: '양평', val: 1 }, { text: '강남', val: 2 }, { text: 'test', val: 3 }]
|
||||
const IrSetting = () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
suyeonga: 0,
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
}
|
||||
})
|
||||
console.log('test')
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'nm', title: '분석번호' },
|
||||
{ id: 'hasu', title: '하수처리장' },
|
||||
{ id: 'region', title: '처리구역' },
|
||||
{ id: 'sangsu', title: '상수사용량' },
|
||||
{ id: 'wjsghks', title: '오수 전환율' },
|
||||
{ id: 'tnsdhtn', title: '순오수량' },
|
||||
{ id: 'rkddn', title: '강우 유출량' },
|
||||
{ id: 'rdii', title: 'RDII' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "nm": '1', 'hasu': '', 'region': '처리', 'sangsu': '파일', 'wjsghks': '', 'tnsdhtn': '', 'rkddn': '', 'rdii': '' }
|
||||
]
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: '침입수 산정', firstCardTitle: '침입수 산정 기초자료 설정', secondCardTitle: '침입수 산정결과 현황' }} form={form} onSubmitEvent={() => { }} graphBtn={true} tableInfoData={{ headList: rtxNmHeadlist, tableData: rtxNmDataList, cellClickFunc: () => { }, onPageChangeFunc: () => { } }}>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormSection name='region' form={form} titleNm='○ 처리구역'><SelectBox label='' selectArray={region} onSelectValue={() => { }} selectClass={undefined} className='w-[150px]' /></FormSection>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 오수 전환율</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 수용가 수</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="suyeonga"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={[]} onSelectValue={() => { }} selectClass={undefined} className='w-[150px]' />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 수용가-맨홀 매칭</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 상수사용량 정보</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inputdate"
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }}
|
||||
mode={"single"} className='w-[150px]'
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 관망도 자료</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default IrSetting
|
||||
@@ -0,0 +1,98 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import BaseLayout from '../../BaseLayout'
|
||||
import { FormField, SelectBox } from '@common/business_components/ui'
|
||||
import { DatePicker, Input } from '@common/components/ui'
|
||||
import { z } from 'zod'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
})
|
||||
const region: { text: string, val: number }[] = [{ text: '양평', val: 1 }, { text: '강남', val: 2 }, { text: 'test', val: 3 }]
|
||||
const MeasureDataUpload = () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
}
|
||||
})
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'status', title: '상태' },
|
||||
{ id: 'code', title: '지정코드' },
|
||||
{ id: 'cjfl', title: '처리' },
|
||||
{ id: 'add', title: '추가' },
|
||||
{ id: 'update', title: '변경' },
|
||||
{ id: 'error', title: '오류' },
|
||||
{ id: 'skip', title: 'Skip' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "status": '상태', 'code': '123824', 'cjfl': '처리', 'fileNm': '파일', 'add': '', 'update': '', 'error': '', 'skip': '' }
|
||||
]
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: '측정자료 등록', firstCardTitle: '하수측정자료 업로드', secondCardTitle: '하수측정자료 파일 업로드 현황' }} form={form} onSubmitEvent={() => { }} graphBtn={true} tableInfoData={{ headList: rtxNmHeadlist, tableData: rtxNmDataList, cellClickFunc: () => { }, onPageChangeFunc: () => { } }}>
|
||||
<div className="space-y-[1.2rem]">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 처리구역</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={region} onSelectValue={() => { }} selectClass={undefined} className='w-[150px]' />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 입력일</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inputdate"
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }}
|
||||
mode={"single"} className='w-[150px]'
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 관로 관리번호</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 파일위치</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default MeasureDataUpload
|
||||
@@ -0,0 +1,54 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import BaseLayout from '../../BaseLayout'
|
||||
import { FormField, SelectBox } from '@common/business_components/ui'
|
||||
import { DatePicker, Input } from '@common/components/ui'
|
||||
import { z } from 'zod'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
})
|
||||
const region: { text: string, val: number }[] = [{ text: '양평', val: 1 }, { text: '강남', val: 2 }, { text: 'test', val: 3 }]
|
||||
const RainDataUpload = () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
}
|
||||
})
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'status', title: '상태' },
|
||||
{ id: 'code', title: '지정코드' },
|
||||
{ id: 'cjfl', title: '처리' },
|
||||
{ id: 'add', title: '추가' },
|
||||
{ id: 'update', title: '변경' },
|
||||
{ id: 'error', title: '오류' },
|
||||
{ id: 'skip', title: 'Skip' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "status": '상태', 'code': '123824', 'cjfl': '처리', 'fileNm': '파일', 'add': '', 'update': '', 'error': '', 'skip': '' }
|
||||
]
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: '강우자료 등록', firstCardTitle: '강우자료 업로드', secondCardTitle: '강우자료 파일 업로드 현황' }} form={form} onSubmitEvent={() => { }} graphBtn={true} tableInfoData={{ headList: rtxNmHeadlist, tableData: rtxNmDataList, cellClickFunc: () => { }, onPageChangeFunc: () => { } }}>
|
||||
<div className="space-y-[1.2rem]">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 파일위치</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default RainDataUpload
|
||||
@@ -0,0 +1,149 @@
|
||||
'use client'
|
||||
import BaseLayout from '@/ii/BaseLayout'
|
||||
import { FormField, SelectBox } from '@common/business_components/ui'
|
||||
import { DatePicker, Input } from '@common/components/ui'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import React, { useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { z } from 'zod'
|
||||
const formSchema = z.object({
|
||||
region: z.string(),
|
||||
suyeonga:z.number(),
|
||||
meter:z.string(),
|
||||
inputdate: z.coerce.date(),
|
||||
inpSource:z.string(),
|
||||
rtkSource: z.string(),
|
||||
})
|
||||
const region: { text: string, val: number }[] = [{ text: '양평', val: 1 }, { text: '강남', val: 2 }, { text: 'test', val: 3 }]
|
||||
const RdiiSetting = () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
meter:"",
|
||||
suyeonga:0,
|
||||
inputdate: new Date(),
|
||||
inpSource:"",
|
||||
rtkSource: "",
|
||||
}
|
||||
})
|
||||
// TODO: 임시 데이터
|
||||
const rtxNmHeadlist = [
|
||||
{ id: 'nm', title: '분석번호' },
|
||||
{ id: 'hasu', title: '하수처리장' },
|
||||
{ id: 'region', title: '처리구역' },
|
||||
{ id: 'sangsu', title: '상수사용량' },
|
||||
{ id: 'wjsghks', title: '오수 전환율' },
|
||||
{ id: 'tnsdhtn', title: '순오수량' },
|
||||
{ id: 'rkddn', title: '강우 유출량' },
|
||||
{ id: 'rdii', title: 'RDII' }
|
||||
]
|
||||
const rtxNmDataList = [
|
||||
{ "nm": '1', 'hasu': '', 'region': '처리', 'sangsu': '파일', 'wjsghks': '', 'tnsdhtn': '', 'rkddn': '', 'rdii': '' }
|
||||
]
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
return (
|
||||
<BaseLayout titleName={{ mainTitle: 'RDII 산정', firstCardTitle: 'RDII산정 기초자료 설정', secondCardTitle: 'RDII 산정결과 현황' }} form={form} onSubmitEvent={() => { }} graphBtn={true} tableInfoData={{ headList: rtxNmHeadlist, tableData: rtxNmDataList, cellClickFunc: () => { }, onPageChangeFunc: () => { } }}>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 처리구역</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="region"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={region} onSelectValue={() => { }} selectClass={undefined} className='w-[150px]' />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 상수사용량 정보</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inputdate"
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }}
|
||||
mode={"single"} className='w-[150px]'
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 관망도자료</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rtkSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ RDII 기여면적(ha)</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="meter"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 오수 전환율</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inpSource"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 강우자료</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inpSource"
|
||||
render={({ field }) => (
|
||||
<div className="w-[150px]">
|
||||
<Input id="picture" type="file" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 수용가 수</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="suyeonga"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={[]} onSelectValue={() => { }} selectClass={undefined} className='w-[150px] h-[3.2rem]' />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-[120px_1fr] items-center">
|
||||
<div className="text-xl font-medium">○ 수용가-맨홀 매칭</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inpSource"
|
||||
render={({ field }) => (
|
||||
<SelectBox label='' selectArray={[{'text':'매칭(양평).shp','val':'1'}]} onSelectValue={() => { }} selectClass={undefined} className='h-[3.2rem] w-[150px]' />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default RdiiSetting
|
||||
@@ -0,0 +1,81 @@
|
||||
'use client'
|
||||
import { ContentsDiv, MainDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, Title, UiTable } from '@common/business_components/ui'
|
||||
import { Button, DatePicker, Input, Label } from '@common/components/ui'
|
||||
import React, { useRef, useState } from 'react'
|
||||
import FileUploadForm from '../../components/ui/fileUploadForm'
|
||||
import ListDiv from '@common/business_components/ui/list_div'
|
||||
import ContentLayout from '@/components/ui/contentLayout'
|
||||
const tableHeadlist = [
|
||||
{ id: 'no', title: 'NO' },
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'manageYear', title: '기준년도' },
|
||||
{ id: 'registDt', title: '등록 일자' },
|
||||
{ id: 'registUsr', title: '등록자' }
|
||||
|
||||
]
|
||||
const tableData = [
|
||||
{ 'no': '1', 'fileNm': '파일명', 'manageYear': '2023', 'registDt': '2025-04-16', 'registUsr': 'me' },
|
||||
{ 'no': '2', 'fileNm': '파일명', 'manageYear': '2024', 'registDt': '2025-04-16', 'registUsr': 'me2' },
|
||||
{ 'no': '3', 'fileNm': '파일명', 'manageYear': '2025', 'registDt': '2025-04-16', 'registUsr': 'me3' }
|
||||
]
|
||||
const Page = () => {
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
const [company, setCompany] = useState('')
|
||||
const [pjNm, setpjNm] = useState('')
|
||||
return (
|
||||
<ContentLayout title='도시침수 예방사업'>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className='flex items-center gap-4'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">수행기간</Label>
|
||||
<DatePicker mode={'single'}
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }} />
|
||||
<span>~</span>
|
||||
<DatePicker mode={'single'}
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }} />
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">수행사</Label>
|
||||
<Input
|
||||
className='w-[100px]'
|
||||
value={company}
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">사업명</Label>
|
||||
<Input
|
||||
className='w-[100px]'
|
||||
value={pjNm}
|
||||
onChange={(e) => setpjNm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700" size="sm">조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl font-medium">사업정보 등록현황</h3>
|
||||
<div>
|
||||
<Button className="px-4 py-2 !mr-[1.2rem] bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700" size="sm" >파일 다운로드</Button>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 !mr-[1.2rem]" size="sm" >삭제</Button>
|
||||
</div>
|
||||
</div>
|
||||
<UiTable headName='' tableData={tableData} headlist={tableHeadlist} pageSize={20} total={20} children={undefined} cellClick={() => { }} onPageChange={function (page: number): void {
|
||||
throw new Error('Function not implemented.')
|
||||
}}></UiTable>
|
||||
</ListDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<FileUploadForm title='도시침수 예방사업 사업추진현황 등록' />
|
||||
</ListDiv>
|
||||
</ContentLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const ExcelDown = () => {
|
||||
return (
|
||||
<div>excelDown</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExcelDown
|
||||
@@ -0,0 +1,64 @@
|
||||
'use client'
|
||||
import { ContentsDiv, MainDiv, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiTable } from '@common/business_components/ui'
|
||||
import { Button, DatePicker, Label } from '@common/components/ui'
|
||||
import React, { useState } from 'react'
|
||||
import FileUploadForm from '../../components/ui/fileUploadForm'
|
||||
import ListDiv from '@common/business_components/ui/list_div'
|
||||
import ContentLayout from '@/components/ui/contentLayout'
|
||||
// TODO: 임시 id
|
||||
const tableHeadlist = [
|
||||
{ id: 'no', title: 'NO' },
|
||||
{ id: 'fileNm', title: '파일명' },
|
||||
{ id: 'manageYear', title: '관리년도' },
|
||||
{ id: 'registDt', title: '등록 일자' },
|
||||
{ id: 'registUsr', title: '등록자' }
|
||||
|
||||
]
|
||||
const tableData = [
|
||||
{ 'no': '1', 'fileNm': '파일명', 'manageYear': '2023', 'registDt': '2025-04-16', 'registUsr': 'me' },
|
||||
{ 'no': '2', 'fileNm': '파일명', 'manageYear': '2024', 'registDt': '2025-04-16', 'registUsr': 'me2' },
|
||||
{ 'no': '3', 'fileNm': '파일명', 'manageYear': '2025', 'registDt': '2025-04-16', 'registUsr': 'me3' }
|
||||
]
|
||||
const Page = () => {
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
||||
return (
|
||||
<ContentLayout title='노후 하수관로 정비사업'>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className='flex items-center gap-4'>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록기간</Label>
|
||||
<DatePicker mode={'single'}
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }} />
|
||||
<span>~</span>
|
||||
<DatePicker mode={'single'}
|
||||
selected={selectedDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setSelectedDate(date) }} />
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 mr-2" size="sm" >조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl font-medium">사업정보 등록현황</h3>
|
||||
<div>
|
||||
<Button className="px-4 py-2 !mr-[1.2rem] bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700" size="sm" >파일 다운로드</Button>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 !mr-[1.2rem]" size="sm" >삭제</Button>
|
||||
</div>
|
||||
</div>
|
||||
<UiTable headName='' tableData={tableData} headlist={tableHeadlist} pageSize={20} total={20} children={undefined} cellClick={() => { }} onPageChange={function (page: number): void {
|
||||
throw new Error('Function not implemented.')
|
||||
}}></UiTable>
|
||||
</ListDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<FileUploadForm title='노후 하수관로 정비사업 주진현황 등록' />
|
||||
</ListDiv>
|
||||
</ContentLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
+30
-4
@@ -4,10 +4,36 @@ import { Button } from "@common/components/ui";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-2">
|
||||
<div className="col-span-2">
|
||||
<Link href={"/oper/pplines"}><Button>관로시설</Button></Link>
|
||||
</div>
|
||||
<div>
|
||||
<div><h3 className="text-2xl text-bold gap-4 mx-4">운영정보 관리</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/oper/pplines"}><Button>관로시설</Button></Link>
|
||||
<Link href={"/oper/floodPre"}><Button>도시침수 예방사업</Button></Link>
|
||||
<Link href={"/oper/oldPp"}><Button>노후 하수관로 정비사업</Button></Link>
|
||||
</div>
|
||||
</div>
|
||||
<div><h3 className="text-2xl text-bold gap-4 mx-4">I/I 분석(기초정보 설정)</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/ii/baseinfo/mock"}><Button>모의정보 설정</Button></Link>
|
||||
<Link href={"/ii/baseinfo/observe"}><Button>관망도 등록</Button></Link>
|
||||
<Link href={"/ii/baseinfo/polygon"}><Button>티센 폴리곤 등록</Button></Link>
|
||||
<Link href={"/ii/baseinfo/regist"}><Button>수용가-맨홀 등록</Button></Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl text-bold gap-4 mx-4">I/I 분석(침입수 분석)</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/ii/ir/measureDataUpload"}><Button>하수측정자료 업로드</Button></Link>
|
||||
<Link href={"/ii/ir/irsetting"}><Button>침입수 산정</Button></Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl text-bold gap-4 mx-4">I/I 분석(RDII 분석)</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/ii/rdii/rain"}><Button>강우자료 등록</Button></Link>
|
||||
<Link href={"/ii/rdii/rdiisetting"}><Button>rdii 산정</Button></Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"recharts": "^2.15.1",
|
||||
"sonner": "^2.0.1",
|
||||
"tailwind-merge": "^3.0.1",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -63,7 +64,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"eslint-plugin-storybook": "^0.11.2",
|
||||
"globals": "^15.14.0",
|
||||
"postcss": "^8.5.1",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"storybook": "^8.5.3",
|
||||
|
||||
@@ -1,69 +1,10 @@
|
||||
@import "@common/assets/styles/base.css";
|
||||
@import "@common/assets/styles/diveicon.css";
|
||||
@import "@common/assets/styles/components.css";
|
||||
|
||||
@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;
|
||||
@@ -74,4 +15,4 @@
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
|
||||
@layer base {}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,23 @@ import type { DayPickerSingleProps } from "react-day-picker";
|
||||
import { Button, Calendar, Popover, PopoverContent, PopoverTrigger } from "@common/components/ui";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { FormControl } from "../form";
|
||||
import { ko } from "date-fns/locale";
|
||||
import { useState } from "react";
|
||||
|
||||
type DatePickerProps = DayPickerSingleProps & { field: { value: Date; onChange: (date: Date | undefined) => void } ,selectClass:string|undefined };
|
||||
|
||||
|
||||
export function DatePicker({ field, selectClass }: DatePickerProps) {
|
||||
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleSelect = (date: Date | undefined) => {
|
||||
field.onChange(date); // React Hook Form 업데이트
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<Popover>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
@@ -27,7 +38,12 @@ export function DatePicker({ field, selectClass }: DatePickerProps) {
|
||||
):selectClass}
|
||||
>
|
||||
{field?.value ? (
|
||||
format(field?.value, "PPP")
|
||||
format(
|
||||
field.value instanceof Date && !isNaN(field.value.getTime())
|
||||
? field.value
|
||||
: new Date()
|
||||
|
||||
, "PPP", { locale: ko })
|
||||
) : (
|
||||
<span>Pick a date</span>
|
||||
)}
|
||||
@@ -39,8 +55,9 @@ export function DatePicker({ field, selectClass }: DatePickerProps) {
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={field?.value}
|
||||
onSelect={field?.onChange}
|
||||
onSelect={handleSelect}
|
||||
initialFocus
|
||||
locale={ko}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -1,52 +1,46 @@
|
||||
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@common/components";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components";
|
||||
|
||||
type ModalType = {
|
||||
bodyClass: string;
|
||||
butNm: string;
|
||||
title: string;
|
||||
body: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
noHeaderYn?: 'N' | 'Y';
|
||||
noFooterYn?: 'N' | 'Y';
|
||||
headerTitle?: string;
|
||||
footerContent?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function UiModal({ bodyClass, butNm, title, body, open, onOpenChange }: ModalType) {
|
||||
const dialogProps = open !== undefined && onOpenChange !== undefined
|
||||
? { open, onOpenChange }
|
||||
: {};
|
||||
|
||||
export function UiModal({
|
||||
bodyClass,
|
||||
body,
|
||||
open,
|
||||
onOpenChange,
|
||||
noHeaderYn = 'N',
|
||||
noFooterYn = 'N',
|
||||
headerTitle,
|
||||
footerContent
|
||||
}: ModalType) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{!open && (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="mr-4 rounded-lg">{butNm}</Button>
|
||||
</DialogTrigger>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={bodyClass}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{body}
|
||||
<DialogFooter className="sm:justify-start">
|
||||
</DialogFooter>
|
||||
{noHeaderYn === 'N' && (
|
||||
<DialogHeader>
|
||||
<DialogDescription>
|
||||
{headerTitle}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
)}
|
||||
{(open && body)}
|
||||
{noFooterYn === 'N' && (
|
||||
<DialogFooter className="sm:justify-start">
|
||||
{footerContent}
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
{open && (
|
||||
<Dialog {...dialogProps}>
|
||||
<DialogContent className={bodyClass}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{body}
|
||||
<DialogFooter className="sm:justify-start">
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import * as XLSX from 'xlsx';
|
||||
|
||||
type ExcelDownloadProps = {
|
||||
data : JSON | JSON[];
|
||||
fileName : string;
|
||||
sheetNameArray : string[];
|
||||
}
|
||||
|
||||
export const downloadExcel = ({ data, fileName = new Date().getTime().toString()+'.xlsx', sheetNameArray = ["Sheet1"] }: ExcelDownloadProps) => {
|
||||
const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]);
|
||||
//let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" });
|
||||
//XLSX.write(workbook, { bookType: "xlsx", type: "binary" });
|
||||
XLSX.writeFile(workbook, fileName+'.xlsx');
|
||||
};
|
||||
@@ -16,4 +16,5 @@ export * from "./search_form_right";
|
||||
export * from "./table_div";
|
||||
export * from "./list_div";
|
||||
export * from "./tab";
|
||||
export * from "./dialog_ui"
|
||||
export * from "./dialog_ui";
|
||||
export * from "./excel_download";
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
function ListDiv(props: {children: React.ReactNode}) {
|
||||
function ListDiv(props: {children: React.ReactNode, className?: string|undefined}) {
|
||||
return (
|
||||
<div className="card card-border">
|
||||
<div className={`card card-border ${props.className}`}>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
function SearchDiv ({children}:{ children: React.ReactNode; }) {
|
||||
return (
|
||||
<div className="flex card card-border !mt-6">
|
||||
{children}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,317 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { SelectDataType } from '../search_input_select';
|
||||
import { SelectBox } from '../select';
|
||||
|
||||
/**
|
||||
* 검색 폼 왼쪽 컴포넌트 타입
|
||||
*
|
||||
* @param isShowPart 구분 조회 여부
|
||||
* @param children 자식 컴포넌트
|
||||
* @param selectSidoData 시도 데이터
|
||||
* @param selectSigunData 시군구 데이터
|
||||
* @param selectRegionData 유역 데이터
|
||||
* @param selectUnitWaterShedData 단위유역 데이터
|
||||
* @param selectMiddleRegionData 중권역 데이터
|
||||
* @param selectEnvPartData 환경청 데이터
|
||||
* @param selectEnvPartSidoData 환경청 시도 데이터
|
||||
* @param selectEnvPartSigunData 환경청 시군구 데이터
|
||||
* @param setRtnPartValue 부분 값 변경 핸들러
|
||||
*/
|
||||
type SearchFormLeftProps = {
|
||||
isShowPart?: boolean | undefined;
|
||||
children: React.ReactNode;
|
||||
selectSidoData?: SelectDataType[] | [];
|
||||
selectSigunData?: SelectDataType[] | [];
|
||||
selectRegionData?: SelectDataType[] | [];
|
||||
selectUnitWaterShedData?: SelectDataType[] | [];
|
||||
selectMiddleRegionData?: SelectDataType[] | [];
|
||||
selectEnvPartData?: EnvPartSelectDataType[] | [];
|
||||
selectEnvPartSidoData?: SelectDataType[] | [];
|
||||
selectEnvPartSigunData?: SelectDataType[] | [];
|
||||
|
||||
setRtnPartValue?: (value: string) => void;
|
||||
setRtnSidoValue?: (value: string) => void;
|
||||
setRtnSigunValue?: (value: string) => void;
|
||||
setRtnRegionValue?: (value: string) => void;
|
||||
setRtnUnitWaterShedValue?: (value: string) => void;
|
||||
setRtnMiddleRegionValue?: (value: string) => void;
|
||||
setRtnEnvPartValue?: (value: string) => void;
|
||||
setRtnEnvPartSidoValue?: (value: string) => void;
|
||||
setRtnEnvPartSigunValue?: (value: string) => void;
|
||||
resetTrigger?: boolean;
|
||||
}
|
||||
|
||||
type EnvPartSelectDataType = SelectDataType & {
|
||||
adstDliStrCd: string,
|
||||
}
|
||||
|
||||
const selectPartData:SelectDataType[] = [
|
||||
{ text: "행정별", val: '0' },
|
||||
{ text: "유역별", val: '01' },
|
||||
{ text: "환경청별", val: '02' },
|
||||
];
|
||||
|
||||
function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSigunValue, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false }: SearchFormLeftProps) {
|
||||
const [partValue, setPartValue] = useState<string>('0');
|
||||
const [showPart, setShowPart] = useState<string>('');
|
||||
const [sidoValue, setSidoValue] = useState<string>('0');
|
||||
const [sigunValue, setSigunValue] = useState<string>('0');
|
||||
const [regionValue, setRegionValue] = useState<string>('0');
|
||||
const [unitWaterShedValue, setUnitWaterShedValue] = useState<string>('0');
|
||||
const [middleRegionValue, setMiddleRegionValue] = useState<string>('0');
|
||||
const [envPartValue, setEnvPartValue] = useState<string>('0');
|
||||
const [envPartSidoValue, setEnvPartSidoValue] = useState<string>('0');
|
||||
const [envPartSigunValue, setEnvPartSigunValue] = useState<string>('0');
|
||||
|
||||
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
|
||||
const [filteredUnitWaterShedData, setFilteredUnitWaterShedData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
|
||||
const [filteredMiddleRegionData, setFilteredMiddleRegionData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
|
||||
|
||||
const [filteredEnvPartData, setFilteredEnvPartData] = useState<EnvPartSelectDataType[]>([{ text: "전체", val: '0', adstDliStrCd:'' }]);
|
||||
const [filteredEnvPartSidoData, setFilteredEnvPartSidoData] = useState<EnvPartSelectDataType[]>([{ text: "전체", val: '0', adstDliStrCd:'' }]);
|
||||
const [filteredEnvPartSigunData, setFilteredEnvPartSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
|
||||
|
||||
const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState<SelectDataType[]>([]);
|
||||
|
||||
// 시군구 세팅
|
||||
const initializeSigunData = (sidoCd: string) => {
|
||||
if (sidoCd === '0') {
|
||||
setSigunValue('0');
|
||||
setFilteredSigunData([{ text: "전체", val: '0' }]);
|
||||
} else {
|
||||
const newFilteredData = [
|
||||
{ text: "전체", val: '0' },
|
||||
...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd)
|
||||
];
|
||||
setSigunValue('0');
|
||||
setFilteredSigunData(newFilteredData);
|
||||
}
|
||||
}
|
||||
|
||||
// 단위유역 세팅
|
||||
const initializeUnitWaterShedData = (regionCd: string) => {
|
||||
if(regionCd === '0') {
|
||||
setUnitWaterShedValue('0');
|
||||
setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]);
|
||||
} else {
|
||||
const newFilteredData = [
|
||||
{ text: "전체", val: '0' },
|
||||
...selectUnitWaterShedData.filter((item: SelectDataType) => item.val.substring(0, 1) === regionCd)
|
||||
];
|
||||
setUnitWaterShedValue('0');
|
||||
setFilteredUnitWaterShedData(newFilteredData);
|
||||
}
|
||||
}
|
||||
|
||||
// 중권역 세팅
|
||||
const initializeMiddleRegionData = (unitWaterShedCd: string) => {
|
||||
if(unitWaterShedCd === '0') {
|
||||
setMiddleRegionValue('0');
|
||||
setFilteredMiddleRegionData([{ text: "전체", val: '0' }]);
|
||||
} else {
|
||||
const newFilteredData = [
|
||||
{ text: "전체", val: '0' },
|
||||
...selectMiddleRegionData.filter((item: SelectDataType) => item.val.substring(0, 2) === unitWaterShedCd)
|
||||
];
|
||||
setMiddleRegionValue('0');
|
||||
setFilteredMiddleRegionData(newFilteredData);
|
||||
}
|
||||
}
|
||||
|
||||
// 환경청 세팅 - 시도
|
||||
const initializeCityDataByEnvPartSido = (envPartCd: string) => {
|
||||
const foundEnvPart = selectEnvPartData.find((item: EnvPartSelectDataType) => item.val === envPartCd);
|
||||
const cityArray = foundEnvPart?.adstDliStrCd.toString().split('|').join('').split(',');
|
||||
|
||||
// 시군구 데이터
|
||||
const envSigunData = selectSigunData.filter((item: SelectDataType) => cityArray?.includes(item.val));
|
||||
|
||||
// 환경청 시도 데이터
|
||||
const sidoArray = [...new Set(envSigunData.map((item: SelectDataType) => item.val.substring(0, 2)))];
|
||||
const envSidoData = selectSidoData.filter((item: SelectDataType) => sidoArray?.includes(item.val));
|
||||
setFilteredEnvPartSigunDataSelected(envSigunData);
|
||||
|
||||
setFilteredEnvPartSidoData([
|
||||
{ text: "전체", val: '0', adstDliStrCd:'' },
|
||||
...envSidoData.map((item: SelectDataType) => ({ text: item.text, val: item.val, adstDliStrCd:'' }))
|
||||
]);
|
||||
}
|
||||
|
||||
// 환경청 세팅 - 시군구
|
||||
const initializeCityDataByEnvPartSigun = (sidoCd: string) => {
|
||||
const envSigunData = filteredEnvPartSigunDataSelected.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd);
|
||||
setFilteredEnvPartSigunData([
|
||||
{ text: "전체", val: '0'},
|
||||
...envSigunData.map((item: SelectDataType) => ({ text: item.text, val: item.val }))
|
||||
]);
|
||||
}
|
||||
|
||||
const onSelectValue = (val: string, type: string) => {
|
||||
switch(type) {
|
||||
case 'part':
|
||||
setPartValue(val);
|
||||
if (setRtnPartValue) {
|
||||
setRtnPartValue(val);
|
||||
}
|
||||
// 구분
|
||||
if(val == '01') {
|
||||
setShowPart('01');
|
||||
initializeMiddleRegionData('0');
|
||||
initializeUnitWaterShedData('0');
|
||||
} else if(val == '02') {
|
||||
setShowPart('02');
|
||||
setFilteredEnvPartData([
|
||||
{ text: "전체", val: '0', adstDliStrCd:'' },
|
||||
...selectEnvPartData.filter((item: EnvPartSelectDataType) => item.val !== null && item.adstDliStrCd !== '')
|
||||
]);
|
||||
setEnvPartValue('0');
|
||||
initializeCityDataByEnvPartSido('0');
|
||||
} else {
|
||||
setShowPart('0');
|
||||
initializeSigunData('0');
|
||||
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
|
||||
}
|
||||
|
||||
// 다른 selectbox 전체로 초기화
|
||||
onSelectValue(selectSidoData[0]?.val || '0', 'sido');
|
||||
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
|
||||
break;
|
||||
case 'sido':
|
||||
setSidoValue(val);
|
||||
if (setRtnSidoValue) {
|
||||
setRtnSidoValue(val);
|
||||
}
|
||||
initializeSigunData(val);
|
||||
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
|
||||
break;
|
||||
case 'sigun':
|
||||
if (setRtnSigunValue) {
|
||||
setRtnSigunValue(val);
|
||||
}
|
||||
setSigunValue(val);
|
||||
break;
|
||||
|
||||
// 유역 세팅
|
||||
case 'region' :
|
||||
setRegionValue(val);
|
||||
if (setRtnRegionValue) {
|
||||
setRtnRegionValue(val);
|
||||
}
|
||||
initializeUnitWaterShedData(val);
|
||||
onSelectValue(selectUnitWaterShedData[0]?.val || '0', 'unitWaterShed');
|
||||
break;
|
||||
case 'unitWaterShed' :
|
||||
setUnitWaterShedValue(val);
|
||||
if (setRtnUnitWaterShedValue) {
|
||||
setRtnUnitWaterShedValue(val);
|
||||
}
|
||||
initializeMiddleRegionData(val);
|
||||
onSelectValue(selectMiddleRegionData[0]?.val || '0', 'middleRegion');
|
||||
break;
|
||||
case 'middleRegion' :
|
||||
setMiddleRegionValue(val);
|
||||
if (setRtnMiddleRegionValue) {
|
||||
setRtnMiddleRegionValue(val);
|
||||
}
|
||||
break;
|
||||
|
||||
// 환경청 세팅
|
||||
case 'envPart' :
|
||||
setEnvPartValue(val);
|
||||
if (setRtnEnvPartValue) {
|
||||
setRtnEnvPartValue(val);
|
||||
}
|
||||
initializeCityDataByEnvPartSido(val);
|
||||
onSelectValue(filteredEnvPartSidoData[0]?.val || '0', 'envPartSido');
|
||||
break;
|
||||
case 'envPartSido' :
|
||||
setEnvPartSidoValue(val);
|
||||
if (setRtnEnvPartSidoValue) {
|
||||
setRtnEnvPartSidoValue(val);
|
||||
}
|
||||
initializeCityDataByEnvPartSigun(val);
|
||||
onSelectValue(filteredEnvPartSigunData[0]?.val || '0', 'envPartSigun');
|
||||
break;
|
||||
case 'envPartSigun' :
|
||||
setEnvPartSigunValue(val);
|
||||
if (setRtnEnvPartSigunValue) {
|
||||
setRtnEnvPartSigunValue(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 리셋 함수
|
||||
const resetAllValues = () => {
|
||||
setPartValue('0');
|
||||
setShowPart('');
|
||||
setSidoValue('0');
|
||||
setSigunValue('0');
|
||||
setRegionValue('0');
|
||||
setUnitWaterShedValue('0');
|
||||
setMiddleRegionValue('0');
|
||||
setEnvPartValue('0');
|
||||
setEnvPartSidoValue('0');
|
||||
setEnvPartSigunValue('0');
|
||||
|
||||
setFilteredSigunData([{ text: "전체", val: '0' }]);
|
||||
setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]);
|
||||
setFilteredMiddleRegionData([{ text: "전체", val: '0' }]);
|
||||
setFilteredEnvPartData([{ text: "전체", val: '0', adstDliStrCd: '' }]);
|
||||
setFilteredEnvPartSidoData([{ text: "전체", val: '0', adstDliStrCd: '' }]);
|
||||
setFilteredEnvPartSigunData([{ text: "전체", val: '0' }]);
|
||||
|
||||
if (setRtnPartValue) setRtnPartValue('0');
|
||||
if (setRtnSidoValue) setRtnSidoValue('0');
|
||||
if (setRtnSigunValue) setRtnSigunValue('0');
|
||||
if (setRtnRegionValue) setRtnRegionValue('0');
|
||||
if (setRtnUnitWaterShedValue) setRtnUnitWaterShedValue('0');
|
||||
if (setRtnMiddleRegionValue) setRtnMiddleRegionValue('0');
|
||||
if (setRtnEnvPartValue) setRtnEnvPartValue('0');
|
||||
if (setRtnEnvPartSidoValue) setRtnEnvPartSidoValue('0');
|
||||
if (setRtnEnvPartSigunValue) setRtnEnvPartSigunValue('0');
|
||||
};
|
||||
|
||||
// 리셋 트리거 변경 감지
|
||||
useEffect(() => {
|
||||
if (resetTrigger) {
|
||||
resetAllValues();
|
||||
// 리셋 후 트리거를 false로 설정
|
||||
if (setRtnPartValue) {
|
||||
setRtnPartValue('0');
|
||||
}
|
||||
}
|
||||
}, [resetTrigger]);
|
||||
|
||||
function SearchFormLeft(props: {children: React.ReactNode}) {
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-4 w-4/5 mx-4">
|
||||
{props.children}
|
||||
isShowPart ? (
|
||||
<div className="grid grid-cols-12 gap-5 w-11/12 m-4">
|
||||
<SelectBox className="col-span-3" label={'구분'} selectArray={selectPartData} onSelectValue={(val) => onSelectValue(val.toString(), 'part')} value={partValue} selectClass={undefined}/>
|
||||
{showPart === '01' ? (
|
||||
<>
|
||||
<SelectBox className="col-span-3" label={'유역'} selectArray={selectRegionData} onSelectValue={(val) => onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={undefined}/>
|
||||
<SelectBox className="col-span-3" label={'단위유역'} selectArray={filteredUnitWaterShedData} onSelectValue={(val) => onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={undefined}/>
|
||||
<SelectBox className="col-span-3" label={'중권역'} selectArray={filteredMiddleRegionData} onSelectValue={(val) => onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={undefined}/>
|
||||
</>
|
||||
) : showPart === '02' ? (
|
||||
<>
|
||||
<SelectBox className="col-span-3" label={'환경청'} selectArray={filteredEnvPartData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={undefined}/>
|
||||
<SelectBox className="col-span-3" label={'시도'} selectArray={filteredEnvPartSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPartSido')} value={envPartSidoValue} selectClass={undefined}/>
|
||||
<SelectBox className="col-span-3" label={'시군구'} selectArray={filteredEnvPartSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPartSigun')} value={envPartSigunValue} selectClass={undefined}/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<SelectBox className="col-span-3" label={'시도'} selectArray={selectSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined}/>
|
||||
<SelectBox className="col-span-3" label={'시군구'} selectArray={filteredSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined}/>
|
||||
</>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-12 gap-5 w-11/12 m-4">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
## SearchFormLeft 모듈
|
||||
#### 검색영역 좌측 모듈로 행정별, 유역별, 환경청을 선택했을 때 검색 영역을 세팅해주고 childnode를 붙여서 출력해주는 모듈
|
||||
#### 1. 컴포넌트 호출방법
|
||||
```js
|
||||
import { SearchFormLeft } from "@common/business_components";
|
||||
|
||||
.....
|
||||
|
||||
// state 지정
|
||||
const [partValue, setPartValue] = useState<string>("0");
|
||||
const [sidoValue, setSidoValue] = useState<string>("0");
|
||||
const [sigunValue, setSigunValue] = useState<string>("0");
|
||||
const [regionValue, setRegionValue] = useState<string>("0");
|
||||
const [untitWaterShedValue, setUntitWaterShedValue] = useState<string>("0");
|
||||
const [middleRegionValue, setMiddleRegionValue] = useState<string>("0");
|
||||
const [envPartValue, setEnvPartValue] = useState<string>("0");
|
||||
const [envPartSidoValue, setEnvPartSidoValue] = useState<string>("0");
|
||||
const [envPartSigunValue, setEnvPartSigunValue] = useState<string>("0");
|
||||
const [resetTrigger, setResetTrigger] = useState<boolean>(false);
|
||||
.....
|
||||
|
||||
<SearchFormLeft isShowPart={true}
|
||||
selectSidoData={selectSidoData}
|
||||
selectSigunData={selectSigunData}
|
||||
selectRegionData={selectRegionData}
|
||||
selectUnitWaterShedData={selectUnitWaterShedData}
|
||||
selectMiddleRegionData={selectMiddleRegionData}
|
||||
selectEnvPartData={selectEnvPartData}
|
||||
setRtnPartValue={setPartValue}
|
||||
setRtnSidoValue={setSidoValue}
|
||||
setRtnSigunValue={setSigunValue}
|
||||
setRtnRegionValue={setRegionValue}
|
||||
setRtnUnitWaterShedValue={setUntitWaterShedValue}
|
||||
setRtnMiddleRegionValue={setMiddleRegionValue}
|
||||
setRtnEnvPartValue={setEnvPartValue}
|
||||
setRtnEnvPartSidoValue={setEnvPartSidoValue}
|
||||
setRtnEnvPartSigunValue={setEnvPartSigunValue}
|
||||
resetTrigger={resetTrigger}
|
||||
>
|
||||
|
||||
```
|
||||
  ㆍisShowPart : 검색단에서 구분(행정별,유역별,환경청별) 세팅 여부(보일 경우 : Y, 아닐경우 : N 혹은 미첨부)
|
||||
|
||||
  ㆍselectSidoData : 전체 시도 데이터
|
||||
|
||||
  ㆍselectSigunData : 전체 시군구구 데이터
|
||||
|
||||
  ㆍselectRegionData : 전체 유역역 데이터
|
||||
|
||||
  ㆍselectUnitWaterShedData : 전체 단위유역 데이터
|
||||
|
||||
  ㆍselectMiddleRegionData : 전체 중권역 데이터
|
||||
|
||||
  ㆍselectEnvPartData : 환경청 전체 데이터
|
||||
|
||||
  ㆍsetRtnPartValue : 선택한 구분 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnSidoValue : 선택한 시도 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnSigunValue : 선택한 시군구 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnRegionValue : 선택한 유역 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnUnitWaterShedValue : 선택한 단위유역 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnMiddleRegionValue : 선택한 중유역 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnEnvPartValue : 선택한 환경청 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnEnvPartSidoValue : 선택한 환경청의 시도 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍsetRtnEnvPartSigunValue : 선택한 환경청 시도의 시군구 set함수(조회시 페이지에서 사용)
|
||||
|
||||
  ㆍresetTrigger : 초기화 trigger 변수
|
||||
|
||||
|
||||
|
||||
#### 2. 세팅방법
|
||||
```js
|
||||
/**
|
||||
* store - index.js
|
||||
* ※ 업무파트별로 common/폴더에 스토어를 만들어놓는걸 추천!
|
||||
* ※ fetch 구문은 각자가 개발 후 return 된 데이터 타입만 맞추면 된다.
|
||||
*/
|
||||
|
||||
// 시도 API 호출 함수 추가
|
||||
const fetchSidoData = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const res = await getFetch('fo', '/addr/city', null);
|
||||
const data = res.data;
|
||||
selectSidoData = [
|
||||
{ text: "전체", val: '0'},
|
||||
...data.map((item: sidoDataType) => ({
|
||||
text: item.sido || '',
|
||||
val: String(item.adstDstrCd) || ''
|
||||
}))
|
||||
];
|
||||
return selectSidoData;
|
||||
} catch (error) {
|
||||
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
|
||||
return selectSidoData;
|
||||
}
|
||||
};
|
||||
|
||||
// 시군구 API 호출 함수 추가
|
||||
const fetchSigunData = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const res = await getFetch('fo', '/addr/sgg', null);
|
||||
const data = res.data;
|
||||
selectSigunData = [
|
||||
{ text: "전체", val: '0' },
|
||||
...data
|
||||
.map((item: sigunDataType) => ({
|
||||
text: item.gugun || '',
|
||||
val: String(item.adstDstrCd) || ''
|
||||
}))
|
||||
];
|
||||
return selectSigunData;
|
||||
} catch (error) {
|
||||
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
|
||||
return selectSigunData;
|
||||
}
|
||||
};
|
||||
|
||||
// 권역 API 호출 함수 추가
|
||||
const fetchRegionData = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const res = await getFetch('fo', '/addr/sarea', null);
|
||||
const data = res.data;
|
||||
return [
|
||||
{ text: "전체", val: '0'},
|
||||
...data.map((item: regionDataType) => ({
|
||||
text: item.rvbsn || '',
|
||||
val: String(item.rvbsnCd) || ''
|
||||
}))
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('권역 데이터를 가져오는데 실패했습니다:', error);
|
||||
return selectRegionData;
|
||||
}
|
||||
};
|
||||
|
||||
// 중권역 API 호출 함수 추가
|
||||
const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const res = await getFetch('fo', '/addr/midArea', null);
|
||||
const data = res.data;
|
||||
return [
|
||||
{ text: "전체", val: '0' },
|
||||
...data
|
||||
.map((item: middleRegionDataType) => ({
|
||||
text: item.mdmbsn || '',
|
||||
val: String(item.rvbsnCd) || ''
|
||||
}))
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('중권역 데이터를 가져오는데 실패했습니다:', error);
|
||||
return selectMiddleRegionData;
|
||||
}
|
||||
};
|
||||
|
||||
// 단위유역 API 호출 함수 추가
|
||||
const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const res = await getFetch('fo', '/addr/largeArea', null);
|
||||
const data = res.data;
|
||||
return [
|
||||
{ text: "전체", val: '0' },
|
||||
...data
|
||||
.map((item: unitWaterShedDataType) => ({
|
||||
text: item.lgbsn || '',
|
||||
val: String(item.rvbsnCd) || ''
|
||||
}))
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('단위유역 데이터를 가져오는데 실패했습니다:', error);
|
||||
return selectUnitWaterShedData;
|
||||
}
|
||||
};
|
||||
|
||||
// 환경청 데이터
|
||||
const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
|
||||
try {
|
||||
const res = await getFetch('fo', '/addr/govArea', null);
|
||||
const data = res.data;
|
||||
selectEnvPartData = [
|
||||
{ text: "전체", val: '0', adstDliStrCd: '' },
|
||||
...data
|
||||
.map((item: envPartDataType) => ({
|
||||
text: item.lclgvrnmntNm || '',
|
||||
val: String(item.lclgvrnmntCd) || '',
|
||||
adstDliStrCd: item.adstDliStrCd || ''
|
||||
}))
|
||||
];
|
||||
return selectEnvPartData;
|
||||
} catch (error) {
|
||||
console.error('환경청 데이터를 가져오는데 실패했습니다:', error);
|
||||
return selectEnvPartData;
|
||||
}
|
||||
};
|
||||
|
||||
.......
|
||||
|
||||
// 타입 및 state 반환
|
||||
export type CommonType = {
|
||||
Common: {
|
||||
isInit: boolean;
|
||||
selectSidoData: SelectDataType[];
|
||||
selectSigunData: SelectDataType[];
|
||||
selectPartData: SelectDataType[];
|
||||
selectUpdownData: SelectDataType[];
|
||||
selectOperationData: SelectDataType[];
|
||||
selectFacilityPartData: SelectDataType[];
|
||||
selectSearchYear: SelectDataType[];
|
||||
selectUnitWaterShedData: SelectDataType[];
|
||||
selectRegionData: SelectDataType[];
|
||||
selectMiddleRegionData: SelectDataType[];
|
||||
selectEnvPartSidoData: SelectDataType[];
|
||||
selectEnvPartSigunData: SelectDataType[];
|
||||
selectFlowTerm: SelectDataType[];
|
||||
selectFlowPanel: SelectDataType[];
|
||||
selectEnvPartData: envPartSelectDataType[];
|
||||
}
|
||||
}
|
||||
|
||||
export const commonState:CommonType = {
|
||||
Common: {
|
||||
isInit: false,
|
||||
selectSidoData: selectSidoData,
|
||||
selectSigunData: selectSigunData,
|
||||
selectPartData: selectPartData,
|
||||
selectUpdownData: selectUpdownData,
|
||||
selectOperationData: selectOperationData,
|
||||
selectFacilityPartData: selectFacilityPartData,
|
||||
selectSearchYear: selectSearchYear,
|
||||
selectRegionData: selectRegionData,
|
||||
selectUnitWaterShedData: selectUnitWaterShedData,
|
||||
selectMiddleRegionData: selectMiddleRegionData,
|
||||
selectEnvPartSidoData : selectEnvPartSidoData,
|
||||
selectEnvPartSigunData : selectEnvPartSigunData,
|
||||
selectFlowTerm: selectFlowTerm,
|
||||
selectFlowPanel: selectFlowPanel,
|
||||
selectEnvPartData : selectEnvPartData,
|
||||
}
|
||||
}
|
||||
|
||||
.....
|
||||
|
||||
// action export
|
||||
export type CommonAction = {
|
||||
initializeCommonData: () => Promise<void>;
|
||||
}
|
||||
|
||||
.......
|
||||
|
||||
// reducer 세팅
|
||||
export const CommonReducer:(set:any)=>CommonAction=(set: any) => {
|
||||
return {
|
||||
// 시도 변경 이벤트
|
||||
initializeCommonData: async () => {
|
||||
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
|
||||
const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet;
|
||||
set((state: CommonType) => ({
|
||||
Common: {
|
||||
...state.Common,
|
||||
selectSidoData: sidoData,
|
||||
selectSigunData: sggData,
|
||||
selectRegionData: regionData,
|
||||
selectMiddleRegionData: midData,
|
||||
selectUnitWaterShedData: unitData,
|
||||
selectEnvPartData: envData
|
||||
}
|
||||
})
|
||||
)},
|
||||
}
|
||||
}
|
||||
```
|
||||
```js
|
||||
/**
|
||||
* page.tsx
|
||||
*/
|
||||
|
||||
.......
|
||||
|
||||
// store에서 액션 호출 (각 페이지 유형별로 세팅)
|
||||
const {
|
||||
....
|
||||
,initializeCommonData
|
||||
....
|
||||
} = useFoStore((state) => state);
|
||||
|
||||
.......
|
||||
|
||||
// 데이터 반환
|
||||
const {
|
||||
isInit,
|
||||
selectPartData,
|
||||
selectUpdownData,
|
||||
selectOperationData,
|
||||
selectFacilityPartData,
|
||||
selectSidoData,
|
||||
selectSigunData,
|
||||
selectRegionData,
|
||||
selectUnitWaterShedData,
|
||||
selectMiddleRegionData,
|
||||
selectEnvPartData,
|
||||
} = Common;
|
||||
|
||||
.......
|
||||
|
||||
// 페이지 내에서 사용할 수 있게 state 설정
|
||||
const [partValue, setPartValue] = useState<string>("0");
|
||||
const [sidoValue, setSidoValue] = useState<string>("0");
|
||||
const [sigunValue, setSigunValue] = useState<string>("0");
|
||||
const [regionValue, setRegionValue] = useState<string>("0");
|
||||
const [untitWaterShedValue, setUntitWaterShedValue] = useState<string>("0");
|
||||
const [middleRegionValue, setMiddleRegionValue] = useState<string>("0");
|
||||
const [envPartValue, setEnvPartValue] = useState<string>("0");
|
||||
const [envPartSidoValue, setEnvPartSidoValue] = useState<string>("0");
|
||||
const [envPartSigunValue, setEnvPartSigunValue] = useState<string>("0");
|
||||
const [resetTrigger, setResetTrigger] = useState<boolean>(false);
|
||||
|
||||
<!-- 초기화 기능 -->
|
||||
const resetVal = () => {
|
||||
console.log('=== 초기화 ===');
|
||||
console.log(selectSidoData);
|
||||
|
||||
// SelectBox 초기화 - 각 selectArray의 첫 번째 옵션 값 사용
|
||||
const initialPartValue = selectPartData[0]?.val || "0";
|
||||
const initialSidoValue = selectSidoData[0]?.val || "0";
|
||||
const initialSigunValue = selectSigunData[0]?.val || "0";
|
||||
const initialRegionValue = selectRegionData[0]?.val || "0";
|
||||
const initialUnitWaterShedValue = selectUnitWaterShedData[0]?.val || "0";
|
||||
const initialMiddleRegionValue = selectMiddleRegionData[0]?.val || "0";
|
||||
const initialEnvPartValue = selectEnvPartData[0]?.val || "0";
|
||||
const initialEnvPartSidoValue = selectEnvPartData[0]?.val || "0";
|
||||
const initialEnvPartSigunValue = selectEnvPartData[0]?.val || "0";
|
||||
|
||||
// 상태값 설정
|
||||
setPartValue(initialPartValue);
|
||||
setSidoValue(initialSidoValue);
|
||||
setSigunValue(initialSigunValue);
|
||||
setRegionValue(initialRegionValue);
|
||||
setUntitWaterShedValue(initialUnitWaterShedValue);
|
||||
setMiddleRegionValue(initialMiddleRegionValue);
|
||||
setEnvPartValue(initialEnvPartValue);
|
||||
setEnvPartSidoValue(initialEnvPartSidoValue);
|
||||
setEnvPartSigunValue(initialEnvPartSigunValue);
|
||||
|
||||
// 리셋 후 트리거를 false로 설정
|
||||
setTimeout(() => {
|
||||
setResetTrigger(false);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 조회 함수
|
||||
const chkVal = useCallback(() => {
|
||||
setResetPage(true);
|
||||
// 조회 funciton
|
||||
searchFcltyQltyList(1);
|
||||
}, [searchFcltyQltyList]);
|
||||
|
||||
// 그리드 page 넘어갈 때 필요한 함수
|
||||
const handlePageChange = useCallback((page: number) => {
|
||||
setResetPage(false);
|
||||
searchFcltyQltyList(page);
|
||||
}, [searchFcltyQltyList]);
|
||||
|
||||
// 테이블 호출 컴포넌트
|
||||
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 수질현황"} tableData={mainList} headlist={upHeadList2} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange}>
|
||||
<HeadMakeColSpan upHeadList={upHeadList2} layoutNum={layoutNum}/>
|
||||
</UiTable>
|
||||
```
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
function SearchFormRight(props: {children: React.ReactNode}) {
|
||||
return (
|
||||
<div className="w-1/5 mx-5 flex items-center justify-end space-x-2">
|
||||
<div className="w-1/12 mx-5 flex flex-col items-center justify-center space-x-2">
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { SearchFormRight };
|
||||
export { SearchFormRight };
|
||||
|
||||
@@ -3,14 +3,27 @@ import React from "react";
|
||||
|
||||
const SearchInput = React.forwardRef<HTMLInputElement, {className:string, label:string, textValue:string, setText:(val:string)=>void}>(({className, label, textValue, setText}, ref) => {
|
||||
const changeText=(e:React.ChangeEvent<HTMLInputElement>)=>{
|
||||
if(e?.target?.value) setText(e.target.value)
|
||||
}
|
||||
|
||||
if(e?.target?.value){
|
||||
|
||||
setText(e.target.value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const keyUp=(e:any)=>{
|
||||
if(e.keyCode===8 && e.target.value.length==1){
|
||||
setText('')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className+" flex items-center"} >
|
||||
{label && <Label className="w-20 text-base text-gray-600">{label}</Label>}
|
||||
<Input className="flex-1 ml-8 w-3/4 h-10 border rounded" value={textValue} onChange={changeText}></Input>
|
||||
{label && <Label className="w-20 text-xl text-gray-600">{label}</Label>}
|
||||
<Input className="!text-lg flex-1 w-3/4 h-14 border rounded" value={textValue} onChange={changeText} onKeyUp={keyUp}></Input>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
export { SearchInput };
|
||||
export { SearchInput };
|
||||
|
||||
@@ -24,12 +24,12 @@ const SearchInputSelect = React.forwardRef<HTMLInputElement, {className:string,
|
||||
|
||||
return (
|
||||
<div className={className+" flex items-center"} >
|
||||
{label && <Label className="w-20 text-base text-gray-600">{label}</Label>}
|
||||
{label && <Label className="w-20 text-xl text-gray-600">{label}</Label>}
|
||||
<div className="flex w-full items-center">
|
||||
<Input className="w-2/3 text-lg h-10 p-2 border rounded" value={textValue} onChange={changeText} />
|
||||
<Input className="w-2/3 text-xl h-14 p-2 border rounded" value={textValue} onChange={changeText} />
|
||||
<Select onValueChange={onSelectValue} value={selectValue}>
|
||||
<SelectTrigger className="ml-2 w-1/3 text-lg" >
|
||||
<SelectValue placeholder={selectData?.[0]?.text ?? ""} />
|
||||
<SelectTrigger className="ml-2 w-1/3" >
|
||||
<SelectValue placeholder={selectData?.[0]?.text ?? ""} />
|
||||
</SelectTrigger>
|
||||
{/* <SelectContent ref={refs ? (refs as unknown as RefType)?.selectValue : null}> */}
|
||||
<SelectContent >
|
||||
@@ -42,4 +42,4 @@ const SearchInputSelect = React.forwardRef<HTMLInputElement, {className:string,
|
||||
</div>
|
||||
)
|
||||
});
|
||||
export { SearchInputSelect };
|
||||
export { SearchInputSelect };
|
||||
|
||||
@@ -23,7 +23,7 @@ const SelectBox = React.forwardRef<
|
||||
|
||||
return (
|
||||
<div className={className+" flex items-center"} ref={ref}>
|
||||
{label && <Label className="w-20 text-base text-gray-600">{label}</Label>}
|
||||
{label && <Label className="w-20 text-xl text-gray-600">{label}</Label>}
|
||||
<Select onValueChange={onSelectValue} value={value !== undefined ? String(value) : undefined}>
|
||||
<SelectTrigger className={selectClass?selectClass:"flex-1 p-2 border rounded"}>
|
||||
<SelectValue placeholder={placeHolder} />
|
||||
@@ -38,4 +38,4 @@ const SelectBox = React.forwardRef<
|
||||
)
|
||||
})
|
||||
|
||||
export { SelectBox };
|
||||
export { SelectBox };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "../../../lib/utils"
|
||||
|
||||
@@ -17,7 +17,7 @@ const SelectTrigger = React.forwardRef<
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
"flex h-13 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-xl ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -103,7 +103,7 @@ const SelectLabel = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -116,7 +116,7 @@ const SelectItem = React.forwardRef<
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-lg outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -145,14 +145,6 @@ const SelectSeparator = React.forwardRef<
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,57 @@
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
|
||||
import { useEffect, useState } from "react";
|
||||
import UiPageNations from "./uiPageNations";
|
||||
|
||||
type UiTableProps = {
|
||||
headName: React.ReactNode | undefined;
|
||||
tableData: { [key: string]: string | number | boolean }[];
|
||||
tableData: { [key: string]: string | number | boolean | object}[];
|
||||
headlist: UiHeadProps[];
|
||||
pageSize: number;
|
||||
total: number;
|
||||
children: React.ReactNode | undefined;
|
||||
cellClick : ((index:number|undefined)=>void) | undefined;
|
||||
cellClick : ((index:number|undefined, headId:string)=>void )| undefined;
|
||||
onPageChange: (page: number) => void;
|
||||
className?: string;
|
||||
resetPageNo?: boolean;
|
||||
}
|
||||
|
||||
type UiHeadProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
align?: string;
|
||||
}
|
||||
|
||||
export function UiTable({ tableData, headlist, pageSize, total, children, headName, cellClick }: UiTableProps) {
|
||||
const cur = 1;
|
||||
export function UiTable({ tableData, headlist, pageSize, total, children, headName, cellClick, onPageChange, className = '', resetPageNo = false }: UiTableProps) {
|
||||
const [cur, setCur] = useState<number>(1);
|
||||
const [idx, setIdx] = useState<number>(-1);
|
||||
|
||||
useEffect(() => {
|
||||
if (resetPageNo) {
|
||||
setCur(1);
|
||||
onPageChange(1);
|
||||
}
|
||||
}, [onPageChange, resetPageNo]);
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
setCur(page);
|
||||
onPageChange(page);
|
||||
}
|
||||
|
||||
const handleCellClick = (index: number,headId:string )=> {
|
||||
if (cellClick) {
|
||||
cellClick(index,headId);
|
||||
setIdx(index);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{headName ? <div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">{headName}</h3>
|
||||
</div> : <></>}
|
||||
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<div className={`card !m-[1.2rem] !p-0r ${className}`}>
|
||||
<Table variant="secondary">
|
||||
<TableHeader className="font-bold">
|
||||
{children ? <>{children}</> : <TableRow>
|
||||
{headlist?.map((head) => (
|
||||
<TableHead key={head.id}>{head.title}</TableHead>
|
||||
@@ -34,17 +59,26 @@ export function UiTable({ tableData, headlist, pageSize, total, children, headNa
|
||||
</TableRow>}
|
||||
</TableHeader>
|
||||
<TableBody className='text-center'>
|
||||
{tableData?.map((el: { [key: string]: string | number | boolean }, index) => (
|
||||
<TableRow key={index + "row"} >
|
||||
{headlist.map((head) => (
|
||||
<TableCell className="cursor-pointer" onClick={()=>cellClick && cellClick(index)} key={head.id + index}>{el[head.id]}</TableCell>
|
||||
{tableData?.map((el: { [key: string]: string | number | boolean | object}, index) => (
|
||||
<TableRow key={index + "row"}>
|
||||
{headlist?.map((head) => (
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
|
||||
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={() => handleCellClick(index, head.id)}
|
||||
key={head.id + index}
|
||||
>
|
||||
{typeof el[head.id] === 'number' ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") : String(el[head.id] ?? '')}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{total <= 0 ? <></> : <UiPageNations total={total} cur={cur} pageSize={pageSize} />}
|
||||
{total <= 0 ? <></> : <UiPageNations total={total} cur={cur} pageSize={pageSize} onPageChange={handlePageChange} />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,77 @@
|
||||
'use client'
|
||||
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Pagination, PaginationContent, PaginationItem, PaginationPrevious, PaginationLink, PaginationNext } from "./lib/pagination";
|
||||
|
||||
import { PaginationNext } from "./lib/pagination";
|
||||
import { PaginationLink } from "./lib/pagination";
|
||||
import { PaginationPrevious } from "./lib/pagination";
|
||||
import { PaginationItem } from "./lib/pagination";
|
||||
import { PaginationContent } from "./lib/pagination";
|
||||
import { Pagination } from "./lib/pagination";
|
||||
|
||||
interface CommonPageNationsProps {
|
||||
total: number;
|
||||
cur: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
cur: number;
|
||||
pageSize: number;
|
||||
onPageChange: (page: number) => void;
|
||||
}
|
||||
|
||||
export default function UiPageNations(commonPageNationsProps: CommonPageNationsProps) {
|
||||
|
||||
const [pageProps, setPageProps] = useState(commonPageNationsProps);
|
||||
const [pageProps, setPageProps] = useState(commonPageNationsProps);
|
||||
|
||||
useEffect(() => {
|
||||
setPageProps(commonPageNationsProps);
|
||||
}, [commonPageNationsProps]);
|
||||
|
||||
useEffect(() => {
|
||||
setPageProps(commonPageNationsProps);
|
||||
} ,[commonPageNationsProps]);
|
||||
const totalPages = Math.ceil(pageProps.total / pageProps.pageSize);
|
||||
const pagesPerGroup = 10;
|
||||
const currentGroup = Math.floor((pageProps.cur - 1) / pagesPerGroup);
|
||||
const startPage = currentGroup * pagesPerGroup + 1;
|
||||
const endPage = Math.min(startPage + pagesPerGroup - 1, totalPages);
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
pageProps.onPageChange(page);
|
||||
};
|
||||
|
||||
return(
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious href="#" />
|
||||
</PaginationItem>
|
||||
return (
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
|
||||
{/* 이전 그룹으로 이동 */}
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href="#"
|
||||
onClick={() => {
|
||||
if (startPage > 1) handlePageChange(startPage - 1);
|
||||
}}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
{/* 현재 그룹 페이지들 표시 */}
|
||||
{
|
||||
Array.from({length:Math.ceil(commonPageNationsProps.total/commonPageNationsProps.pageSize)},(_,i)=>i+1).map((el,index)=>(
|
||||
<PaginationItem key={index}>
|
||||
<PaginationLink href= '#'>{el}</PaginationLink>
|
||||
</PaginationItem>
|
||||
))
|
||||
}
|
||||
<PaginationItem>
|
||||
<PaginationNext href="#" />
|
||||
Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i).map((pageNum) => (
|
||||
<PaginationItem key={pageNum}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
isActive={pageNum === pageProps.cur}
|
||||
onClick={() => handlePageChange(pageNum)}
|
||||
>
|
||||
{pageNum}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
|
||||
|
||||
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
);
|
||||
))
|
||||
}
|
||||
|
||||
{/* 다음 그룹으로 이동 */}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href="#"
|
||||
onClick={() => {
|
||||
if (endPage < totalPages) handlePageChange(endPage + 1);
|
||||
}}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
);
|
||||
}
|
||||
@@ -1,93 +1,8 @@
|
||||
import presetConfig from "../../../tailwind.config.preset";
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: ["class", "[data-mode='dark']"],
|
||||
content: ["./src/**/*.{ts,tsx,js,jsx}", "./.storybook/**/*.{ts,tsx,js,jsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
card: {
|
||||
DEFAULT: "var(--card)",
|
||||
foreground: "var(--card-foreground)",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "var(--popover)",
|
||||
foreground: "var(--popover-foreground)",
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: "var(--primary)",
|
||||
foreground: "var(--primary-foreground)",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "var(--secondary)",
|
||||
foreground: "var(--secondary-foreground)",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "var(--muted)",
|
||||
foreground: "var(--muted-foreground)",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "var(--accent)",
|
||||
foreground: "var(--accent-foreground)",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "var(--destructive)",
|
||||
foreground: "var(--destructive-foreground)",
|
||||
},
|
||||
border: "var(--border)",
|
||||
input: "var(--input)",
|
||||
label: "var(--label)",
|
||||
ring: "var(--ring)",
|
||||
chart: {
|
||||
1: "var(--chart-1)",
|
||||
2: "var(--chart-2)",
|
||||
3: "var(--chart-3)",
|
||||
4: "var(--chart-4)",
|
||||
5: "var(--chart-5)",
|
||||
},
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" },
|
||||
},
|
||||
"spin-parent": {
|
||||
from: { transform: "translate(-50%,-50%) rotate(0deg)" },
|
||||
to: { transform: "translate(-50%,-50%) rotate(360deg)" },
|
||||
},
|
||||
"spin-child": {
|
||||
from: { transform: "translate(0,0) rotate(0deg)" },
|
||||
to: { transform: "translate(0,0) rotate(360deg)" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"spin-parent": "spin-parent 6s linear infinite",
|
||||
"spin-child": "spin-child 1s ease-in-out infinite",
|
||||
},
|
||||
backgroundImage: {
|
||||
"gradient-blue": "linear-gradient(.25turn, #2094fa, #00e5dd)",
|
||||
"gradient-blue-hover":
|
||||
"linear-gradient(.25turn, #2298ff, #01dcda, #00e8b4)",
|
||||
"gradient-green": "linear-gradient(.25turn, #00bad0, #00eb6f)",
|
||||
"gradient-green-hover": "linear-gradient(.25turn, #00bad0, #12fe81)",
|
||||
"gradient-red": "linear-gradient(.25turn, #ff487c, #ffc425)",
|
||||
"gradient-red-hover": "linear-gradient(.25turn, #ff487c, #ffea3a)",
|
||||
"gradient-purple": "linear-gradient(.25turn, #be30d7, #ff9264)",
|
||||
"gradient-purple-hover": "linear-gradient(.25turn, #be30d7, #ffaf5a)",
|
||||
},
|
||||
},
|
||||
},
|
||||
presets: [presetConfig],
|
||||
content: ["./src/**/*.{ts,tsx,js,jsx}"],
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
||||
<thead ref={ref} className={cn("sticky top-0 bg-secondary [&_tr]:border-b", className)} {...props} />
|
||||
));
|
||||
TableHeader.displayName = "TableHeader";
|
||||
|
||||
|
||||
Generated
+166
-2
@@ -140,6 +140,106 @@ importers:
|
||||
specifier: ^5
|
||||
version: 5.6.3
|
||||
|
||||
apps/sn:
|
||||
dependencies:
|
||||
'@common/assets':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/common/assets
|
||||
'@common/business_components':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/common/business_components
|
||||
'@common/components':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/common/components
|
||||
'@frontend-opensource/use-react-hooks':
|
||||
specifier: ^1.1.4
|
||||
version: 1.1.4
|
||||
'@hookform/resolvers':
|
||||
specifier: ^4.1.3
|
||||
version: 4.1.3(react-hook-form@7.54.2)
|
||||
'@radix-ui/react-label':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2(@types/react@18.3.18)(react@18.3.1)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.67.1
|
||||
version: 5.67.1(react@18.3.1)
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.21.2
|
||||
version: 8.21.2(react-dom@18.3.1)(react@18.3.1)
|
||||
axios:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1
|
||||
chart.js:
|
||||
specifier: ^4.4.8
|
||||
version: 4.4.8
|
||||
dayjs:
|
||||
specifier: ^1.11.13
|
||||
version: 1.11.13
|
||||
js-cookie:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
next:
|
||||
specifier: 15.1.7
|
||||
version: 15.1.7(react-dom@18.3.1)(react@18.3.1)
|
||||
react-chartjs-2:
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0(chart.js@4.4.8)(react@18.3.1)
|
||||
react-hook-form:
|
||||
specifier: ^7.54.2
|
||||
version: 7.54.2(react@18.3.1)
|
||||
tailwindcss-animate:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(tailwindcss@3.4.17)
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
zustand:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(@types/react@18.3.18)(react@18.3.1)
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3
|
||||
version: 3.3.0
|
||||
'@faker-js/faker':
|
||||
specifier: ^9.5.0
|
||||
version: 9.5.0
|
||||
'@types/js-cookie':
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6
|
||||
'@types/node':
|
||||
specifier: ^22.13.1
|
||||
version: 22.13.9
|
||||
'@types/webpack':
|
||||
specifier: ^5.28.5
|
||||
version: 5.28.5
|
||||
eslint:
|
||||
specifier: ^9
|
||||
version: 9.21.0
|
||||
eslint-config-next:
|
||||
specifier: 15.1.7
|
||||
version: 15.1.7(eslint@9.21.0)(typescript@5.6.3)
|
||||
postcss:
|
||||
specifier: ^8
|
||||
version: 8.5.3
|
||||
prettier:
|
||||
specifier: ^3.4.2
|
||||
version: 3.5.2
|
||||
prettier-plugin-tailwindcss:
|
||||
specifier: ^0.6.11
|
||||
version: 0.6.11(prettier@3.5.2)
|
||||
steiger:
|
||||
specifier: ^0.5.5
|
||||
version: 0.5.5(typescript@5.6.3)
|
||||
tailwindcss:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.17
|
||||
typescript:
|
||||
specifier: ^5
|
||||
version: 5.6.3
|
||||
|
||||
apps/wr:
|
||||
dependencies:
|
||||
'@common/business_components':
|
||||
@@ -266,6 +366,9 @@ importers:
|
||||
tailwind-merge:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.2
|
||||
xlsx:
|
||||
specifier: ^0.18.5
|
||||
version: 0.18.5
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
@@ -325,7 +428,7 @@ importers:
|
||||
specifier: ^15.14.0
|
||||
version: 15.15.0
|
||||
postcss:
|
||||
specifier: ^8.5.1
|
||||
specifier: ^8.5.3
|
||||
version: 8.5.3
|
||||
prettier:
|
||||
specifier: ^3.4.2
|
||||
@@ -1320,6 +1423,7 @@ packages:
|
||||
/@faker-js/faker@9.5.0:
|
||||
resolution: {integrity: sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==}
|
||||
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
|
||||
deprecated: Please update to a newer version
|
||||
dev: true
|
||||
|
||||
/@feature-sliced/filesystem@2.4.0:
|
||||
@@ -3853,6 +3957,11 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/adler-32@1.3.1:
|
||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/ajv-formats@2.1.1(ajv@8.17.1):
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
@@ -4236,6 +4345,14 @@ packages:
|
||||
/caniuse-lite@1.0.30001701:
|
||||
resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==}
|
||||
|
||||
/cfb@1.2.2:
|
||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||
engines: {node: '>=0.8'}
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
crc-32: 1.2.2
|
||||
dev: false
|
||||
|
||||
/chai@5.2.0:
|
||||
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4325,6 +4442,11 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/codepage@1.15.0:
|
||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
@@ -4402,6 +4524,12 @@ packages:
|
||||
typescript: 5.6.3
|
||||
dev: true
|
||||
|
||||
/crc-32@1.2.2:
|
||||
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -5516,6 +5644,11 @@ packages:
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/frac@1.1.2:
|
||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/fraction.js@4.3.7:
|
||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||
dev: true
|
||||
@@ -6083,7 +6216,7 @@ packages:
|
||||
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/node': 22.13.5
|
||||
'@types/node': 22.13.9
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
dev: true
|
||||
@@ -7571,6 +7704,13 @@ packages:
|
||||
whatwg-url: 7.1.0
|
||||
dev: true
|
||||
|
||||
/ssf@0.11.2:
|
||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||
engines: {node: '>=0.8'}
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
dev: false
|
||||
|
||||
/stable-hash@0.0.4:
|
||||
resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
|
||||
dev: true
|
||||
@@ -8535,11 +8675,21 @@ packages:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
/wmf@1.0.2:
|
||||
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/word-wrap@1.2.5:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/word@0.3.0:
|
||||
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -8569,6 +8719,20 @@ packages:
|
||||
optional: true
|
||||
dev: true
|
||||
|
||||
/xlsx@0.18.5:
|
||||
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
cfb: 1.2.2
|
||||
codepage: 1.15.0
|
||||
crc-32: 1.2.2
|
||||
ssf: 0.11.2
|
||||
wmf: 1.0.2
|
||||
word: 0.3.0
|
||||
dev: false
|
||||
|
||||
/y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
Reference in New Issue
Block a user