공통 레이아웃 컴포넌트로 등록 후 페이지 구현(i/i 분석 >> 기초정보 설정)

This commit is contained in:
JIeunhuh
2025-04-21 13:29:07 +09:00
parent e79348ea11
commit d61962996c
5 changed files with 625 additions and 0 deletions
+91
View File
@@ -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="!mx-[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
+225
View File
@@ -0,0 +1,225 @@
"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"
// 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 (
<ContentLayout title="모의정보 설정">
<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">모의정보 설정</h3>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="card !m-[1.2rem]">
{/* 기본 정보 입력 폼 */}
<div className="space-y-[1.2rem]">
<FormField
control={form.control}
name="region"
render={({ field }) => (
<FormUi label="○ 처리구역" className="flex items-center">
<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>
</FormUi>
)}
/>
<FormField
control={form.control}
name="rdiiArea"
render={({ field }) => (
<FormUi label="○ RDII 기여면적(ha)" className="flex items-center">
<Input
className="h-10 text-center text-xl w-[150px]"
placeholder=""
type=""
{...field}
/>
</FormUi>
)}
/>
<FormField
control={form.control}
name="rtkSource"
render={({ field }) => (
<FormUi label="○ RTK계수 출처" className="flex items-center">
<Input
className="h-10 text-center text-xl w-[250px]"
placeholder=""
{...field}
/>
</FormUi>
)}
/>
</div>
{/* 테이블 컨테이너 */}
<div className="mt-[1.2rem]">
<div className="grid grid-cols-2">
<div>
<div className="mb-[1.2rem] gap-[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>
<div className="flex justify-end mt-[1.2rem]">
<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">RTX계수 입력현황</h3>
<Button type="submit" className="!mx-[1.2rem]">삭제</Button>
</div>
<UiTable
headName=''
headlist={rtxNmHeadlist}
tableData={[]}
total={0}
pageSize={10}
children={undefined}
cellClick={() => { }}
onPageChange={() => { }}
/>
</ListDiv>
</ContentLayout>
)
}
+114
View File
@@ -0,0 +1,114 @@
"use client"
import { z } from "zod"
import { useForm, useFieldArray } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { FileUpload, Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormUi, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox } from "@common/business_components/ui"
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, Button, DatePicker, Label } from "@common/components/ui"
import { UiTable } from "@common/business_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(),
})
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())
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' }
]
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 }) => (
<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 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>
)
}
+96
View File
@@ -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
+99
View File
@@ -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