- SelectBox Width 고정
- I/I분석 JSON 데이터에 파일명 추가
- 시도시군구 매핑 오류 수정
This commit is contained in:
swcho
2025-05-28 17:10:56 +09:00
parent 5c5a92747a
commit f446dde591
6 changed files with 83 additions and 107 deletions
@@ -15,7 +15,7 @@ interface SidoSigunguSelectBoxProps {
}
const sidoList: { id: number, name: string }[] = [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 26, name: '부산광역시' },
{ id: 28, name: '인천광역시' },
{ id: 30, name: '대전광역시' },
@@ -29,19 +29,19 @@ const sidoList: { id: number, name: string }[] = [
];
const sigunguMap: Record<number, { id: number; name: string }[]> = {
26: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
// ...부산산광역시의 시군구 추가
],
28: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
// ...인천광역시의 시군구 추가
],
30: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
// ...대전광역시의 시군구 추가
],
41: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 41820, name: '가평군' },
{ id: 41550, name: '안성시' },
{ id: 41830, name: '양평군' },
@@ -54,19 +54,19 @@ const sigunguMap: Record<number, { id: number; name: string }[]> = {
// ...경기도의 시군구 추가
],
42: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 42170, name: '동해시' },
{ id: 42110, name: '춘천시' },
{ id: 42720, name: '홍천군' },
// ...강원도의 시군구 추가
],
44: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 44770, name: '서천군' },
// ...충청남도의 시군구 추가
],
45: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 45130, name: '군산시' },
{ id: 45190, name: '남원시' },
{ id: 45730, name: '무주군' },
@@ -79,7 +79,7 @@ const sigunguMap: Record<number, { id: number; name: string }[]> = {
// ...전라북도의 시군구 추가
],
46: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 46810, name: "강진군" },
{ id: 46770, name: "고흥군" },
{ id: 46720, name: "곡성군" },
@@ -95,7 +95,7 @@ const sigunguMap: Record<number, { id: number; name: string }[]> = {
// ...전라남도의 시군구 추가
],
47: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 47130, name: "경주시" },
{ id: 47830, name: "고령군" },
{ id: 47190, name: "구미시" },
@@ -116,7 +116,7 @@ const sigunguMap: Record<number, { id: number; name: string }[]> = {
// ...경상북도의 시군구 추가
],
48: [
{ id: 0, name: '선택' },
{ id: 0, name: '전체' },
{ id: 48310, name: "거제시" },
{ id: 48250, name: "김해시" },
{ id: 48270, name: "밀양시" },
@@ -134,23 +134,23 @@ export function SidoSigunguSelectBox({
className = "",
}: SidoSigunguSelectBoxProps) {
// 선택된 시도에 따라 시군구 리스트를 가져옴
const sigunguList = useMemo(() => sigunguMap[sido] || [{ id: 0, name: '선택' }], [sido]);
const sigunguList = useMemo(() => sigunguMap[sido] || [{ id: 0, name: '전체' }], [sido]);
return (
<div className={`flex items-center ${className}`}>
<SelectBox2
className="w-50 mr-8"
selectClass="rounded-lg bg-white"
className="w-48 mr-8"
selectClass="w-48 rounded-lg bg-white truncate"
label="시도"
selectArray={sidoList.map(opt => ({ val: opt.id, text: opt.name }))}
value={sido}
onSelectValue={val => onSidoChange(Number(val))}
/>
<SelectBox2
className="w-50 mr-8"
selectClass="rounded-lg bg-white"
className="w-48 mr-8"
selectClass="w-48 rounded-lg bg-white truncate"
label="시군구"
selectArray={sidoList.map(opt => ({ val: opt.id, text: opt.name }))}
selectArray={sigunguList.map(opt => ({ val: opt.id, text: opt.name }))}
value={sigungu}
onSelectValue={val => onSigunguChange(Number(val))}
/>
@@ -64,7 +64,7 @@ export function YearRangeSelectBox({
minYear = 1900,
maxYear = new Date().getFullYear(),
className = "",
selectClass = "rounded-lg bg-white"
selectClass = "w-24 rounded-lg bg-white"
}: YearRangeSelectBoxProps) {
// 시작년도 배열
const getStartYearArray = () => {
@@ -90,7 +90,7 @@ export function YearRangeSelectBox({
return (
<div className={`flex items-center ${className}`}>
<SelectBox2
className="w-50 mr-3"
className="w-50"
selectClass={selectClass}
label={startLabel}
selectArray={getStartYearArray()}
+1 -1
View File
@@ -10,7 +10,7 @@ interface FormSectionProps {
const FormSection = ({ children, titleNm, form, name }: FormSectionProps) => {
return (
<div className="grid grid-cols-[130px_1fr] items-center">
<div className="grid grid-cols-[200px_1fr] items-center">
<div className="text-xl font-medium">{titleNm}</div>
<FormField
control={form.control}
+4 -5
View File
@@ -95,11 +95,6 @@ const IrSetting = () => {
setSelect4('');
setSelect5('');
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
// FileUploadForm 내부의 파일명 표시 등을 초기화하려면
// FileUploadForm 컴포넌트 자체에 reset 로직 또는 key 변경을 통한 재마운트 전략이 필요할 수 있습니다.
// 현재 구조에서는 FileUploadForm의 내부 상태를 직접 초기화하기 어렵습니다.
// 가장 간단한 방법은 FileUploadForm에 key prop을 주고, 초기화 시 key를 변경하여 강제로 재렌더링하는 것입니다.
// 예: <FileUploadForm key={formResetKey} ... /> setFormResetKey(prev => prev + 1)
};
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
@@ -212,6 +207,10 @@ const IrSetting = () => {
anlsInfo: input2, // 분석정보
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
atflInpNm: file1?.file?.name, // 오수관망도 파일명
atflHrPttnNm: file3?.file?.name, // 오수량 시간패턴 파일명
atflMpngNm: file2?.file?.name, // 수용가-맨홀 매칭결과 파일명
atflOilqNm: file4?.file?.name, // 유량자료 파일명
tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과
tbSnHrPttn: timePatternData, // 오수량 시간패턴
tbSnOilqData: flowRateData, // 유량자료
+47 -76
View File
@@ -4,18 +4,17 @@ import { postData } from '@/components/lib/api'
import FileUploadForm from "@/components/ui/fileUploadForm"
import FormSection from "@/components/ui/formSection"
import BaseLayout from "@/ii/BaseLayout"
import { UiTable, filePicked } from "@common/business_components/ui"
import { filePicked } from "@common/business_components/ui"
import { Button, DatePicker, Input } from "@common/components/ui"
import { zodResolver } from "@hookform/resolvers/zod"
import { Label } from '@radix-ui/react-label'
import { useState } from "react"
import { useFieldArray, useForm } from "react-hook-form"
import { z } from "zod"
import { excelToMatchingResultData, excelToRainData } from '../../components/lib/utils/excelToJson'
import { EditableFormTable, type EditableTableHeadItem } from '../../components/ui/EditableFormTable';
import { Label } from '@radix-ui/react-label'
import { EditableFormTable, type EditableTableHeadItem } from '../../components/ui/EditableFormTable'
// TODO: 컴포넌트 호출 ~ 필요 코드 수정해야댐
// zod 스키마 정의
const formSchema = z.object({
enoCd: z.string(), // 환경청코드
@@ -46,9 +45,8 @@ const formSchema = z.object({
date: z.date(),
// date: z.coerce.date(),
})
// zod 스키마 타입 추론
type FormSchemaType = z.infer<typeof formSchema>;
// 기본 폼 값 정의
const defaultFormValues: FormSchemaType = {
enoCd: "",
@@ -82,8 +80,6 @@ interface UploadedFileInfo {
file: File | null;
atflCd: string | null;
atflId: string | null;
// 필요하다면 파일 이름 등 추가 정보 저장 가능
// fileName: string;
}
// 초기 파일 정보 상태
@@ -94,7 +90,6 @@ export default function RTKForm() {
// FileUploadForm에서 전달받은 파일 정보를 저장할 상태 배열
// 각 인덱스는 FileUploadForm의 fileIdx와 매칭됨
// 예: 0: 오수관망도, 1: 수용가-맨홀, 2: 강우자료
const [uploadedFilesData, setUploadedFilesData] = useState<UploadedFileInfo[]>([
initialFileInfo, // 0: 오수관망도
initialFileInfo, // 1: 수용가-맨홀 매칭결과
@@ -106,34 +101,6 @@ export default function RTKForm() {
resolver: zodResolver(formSchema),
defaultValues: defaultFormValues
});
// const form = useForm<z.infer<typeof formSchema>>({
// resolver: zodResolver(formSchema),
// defaultValues: {
// enoCd: "", // 환경청코드
// ctpvCd: "", // 시도코드
// sggCd: "", // 시군구코드
// swageTmplntCd: "", // 하수처리장코드
// swtrmtZoneCd: "", // 하수처리구역코드
// rtkSource: "", // RTK계수 출처
// rdiiArea: 0, // RDII 기여면적
// unitFactors: [
// { response: "Short-Term", R: 0, T: 0, K: 0 },
// { response: "Medium-Term", R: 0, T: 0, K: 0 },
// { response: "Long-Term", R: 0, T: 0, K: 0 },
// ],
// initialDepths: [
// { response: "Short-Term", Dmax: 0, Drec: 0, Do: 0 },
// { response: "Medium-Term", Dmax: 0, Drec: 0, Do: 0 },
// { response: "Long-Term", Dmax: 0, Drec: 0, Do: 0 },
// ],
// // osumap: undefined,
// // matching: undefined,
// aplcnYm: "",
// rate: 0,
// // rain: undefined,
// analysisInfo: "",
// }
// });
// 폼 초기화 함수
const resetAllForms = () => {
@@ -246,34 +213,37 @@ export default function RTKForm() {
swtrmtZoneCd: "", // 하수처리구역코드
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
atflInpNm: file1?.file?.name || "", // 오수관망도 파일명
atflMpngNm: file2?.file?.name || "", // 수용가-맨홀 매칭결과 파일명
atflRainNm: file3?.file?.name || "", // 강우자료 파일명
cntbArea: parseFloat("rdiiArea") || null, // RDII 기여면적(ha)
srcNm: "", // 출처이름 (RTK계수 출처)
shortPrdR: parseFloat(formData.unitFactors[0]?.R !== undefined ? String(formData.unitFactors[0]?.R) : "0") || null, // 단기간R
shortPrdT: parseFloat(formData.unitFactors[0]?.T !== undefined ? String(formData.unitFactors[0]?.T) : "0") || null, // 단기간T
shortPrdK: parseFloat(formData.unitFactors[0]?.K !== undefined ? String(formData.unitFactors[0]?.K) : "0") || null, // 단기간K
shortPrdR: parseFloat(formData.unitFactors[0]?.R !== undefined ? formData.unitFactors[0]?.R : "0") || null, // 단기간R
shortPrdT: parseFloat(formData.unitFactors[0]?.T !== undefined ? formData.unitFactors[0]?.T : "0") || null, // 단기간T
shortPrdK: parseFloat(formData.unitFactors[0]?.K !== undefined ? formData.unitFactors[0]?.K : "0") || null, // 단기간K
mdPrdR: parseFloat(formData.unitFactors[1]?.R !== undefined ? String(formData.unitFactors[1]?.R) : "0") || null, // 중간기간R
mdPrdT: parseFloat(formData.unitFactors[1]?.T !== undefined ? String(formData.unitFactors[1]?.T) : "0") || null, // 중간기간T
mdPrdK: parseFloat(formData.unitFactors[1]?.K !== undefined ? String(formData.unitFactors[1]?.K) : "0") || null, // 중간기간K
mdPrdR: parseFloat(formData.unitFactors[1]?.R !== undefined ? formData.unitFactors[1]?.R : "0") || null, // 중간기간R
mdPrdT: parseFloat(formData.unitFactors[1]?.T !== undefined ? formData.unitFactors[1]?.T : "0") || null, // 중간기간T
mdPrdK: parseFloat(formData.unitFactors[1]?.K !== undefined ? formData.unitFactors[1]?.K : "0") || null, // 중간기간K
longPrdR: parseFloat(formData.unitFactors[2]?.R !== undefined ? String(formData.unitFactors[2]?.R) : "0") || null, // 장기간R
longPrdT: parseFloat(formData.unitFactors[2]?.T !== undefined ? String(formData.unitFactors[2]?.T) : "0") || null, // 장기간T
longPrdK: parseFloat(formData.unitFactors[2]?.K !== undefined ? String(formData.unitFactors[2]?.K) : "0") || null, // 장기간K
longPrdR: parseFloat(formData.unitFactors[2]?.R !== undefined ? formData.unitFactors[2]?.R : "0") || null, // 장기간R
longPrdT: parseFloat(formData.unitFactors[2]?.T !== undefined ? formData.unitFactors[2]?.T : "0") || null, // 장기간T
longPrdK: parseFloat(formData.unitFactors[2]?.K !== undefined ? formData.unitFactors[2]?.K : "0") || null, // 장기간K
shortPrdMaxDpth: parseFloat(formData.initialDepths[0]?.Dmax !== undefined ? String(formData.initialDepths[0]?.Dmax) : "0") || null, // 단기간최대깊이
shortPrdRcvryRt: parseFloat(formData.initialDepths[0]?.Drec !== undefined ? String(formData.initialDepths[0]?.Drec) : "0") || null, // 단기간회복비율
shortPrdBgngDpth: parseFloat(formData.initialDepths[0]?.Do !== undefined ? String(formData.initialDepths[0]?.Do) : "0") || null, // 단기간시작깊이
shortPrdMaxDpth: parseFloat(formData.initialDepths[0]?.Dmax !== undefined ? formData.initialDepths[0]?.Dmax : "0") || null, // 단기간최대깊이
shortPrdRcvryRt: parseFloat(formData.initialDepths[0]?.Drec !== undefined ? formData.initialDepths[0]?.Drec : "0") || null, // 단기간회복비율
shortPrdBgngDpth: parseFloat(formData.initialDepths[0]?.Do !== undefined ? formData.initialDepths[0]?.Do : "0") || null, // 단기간시작깊이
mdPrdMaxDpth: parseFloat(formData.initialDepths[1]?.Dmax !== undefined ? String(formData.initialDepths[1]?.Dmax) : "0") || null, // 중간기간최대깊이
mdPrdRcvryRt: parseFloat(formData.initialDepths[1]?.Drec !== undefined ? String(formData.initialDepths[1]?.Drec) : "0") || null, // 중간기간회복비율
mdPrdBgngDpth: parseFloat(formData.initialDepths[1]?.Do !== undefined ? String(formData.initialDepths[1]?.Do) : "0") || null, // 중간기간시작깊이
mdPrdMaxDpth: parseFloat(formData.initialDepths[1]?.Dmax !== undefined ? formData.initialDepths[1]?.Dmax : "0") || null, // 중간기간최대깊이
mdPrdRcvryRt: parseFloat(formData.initialDepths[1]?.Drec !== undefined ? formData.initialDepths[1]?.Drec : "0") || null, // 중간기간회복비율
mdPrdBgngDpth: parseFloat(formData.initialDepths[1]?.Do !== undefined ? formData.initialDepths[1]?.Do : "0") || null, // 중간기간시작깊이
longTermMaxDpth: parseFloat(formData.initialDepths[2]?.Dmax !== undefined ? String(formData.initialDepths[2]?.Dmax) : "0") || null, // 장기간최대깊이
longTermRcvryRt: parseFloat(formData.initialDepths[2]?.Drec !== undefined ? String(formData.initialDepths[2]?.Drec) : "0") || null, // 장기간회복비율
longTermBgngDpth: parseFloat(formData.initialDepths[2]?.Do !== undefined ? String(formData.initialDepths[2]?.Do) : "0") || null, // 장기간시작깊이
longTermMaxDpth: parseFloat(formData.initialDepths[2]?.Dmax !== undefined ? formData.initialDepths[2]?.Dmax : "0") || null, // 장기간최대깊이
longTermRcvryRt: parseFloat(formData.initialDepths[2]?.Drec !== undefined ? formData.initialDepths[2]?.Drec : "0") || null, // 장기간회복비율
longTermBgngDpth: parseFloat(formData.initialDepths[2]?.Do !== undefined ? formData.initialDepths[2]?.Do : "0") || null, // 장기간시작깊이
dtrwSwtcRt: parseFloat(formData.rate !== undefined ? String(formData.rate) : "0") || null, // 오수전환율
dtrwSwtcRt: parseFloat(formData.rate !== undefined ? formData.rate : "0") || null, // 오수전환율
anlsInfo: "", // 분석정보
tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과
@@ -332,10 +302,10 @@ export default function RTKForm() {
{[<div className="space-y-[1.2rem] overflow-x-auto" key="form-section">
<div className="grid grid-cols-2 gap-4">
<FormSection name="rtkSource" form={form} titleNm="○ RTK계수 출처">
<Input {...form.register("rtkSource")} className="h-10 text-center text-xl w-[150px]" />
<Input {...form.register("rtkSource")} className="h-12 text-center text-xl w-[225px]" />
</FormSection>
<FormSection name="rdiiArea" form={form} titleNm="○ RDII 기여면적(ha)">
<Input {...form.register("rdiiArea")} className="h-10 text-center text-xl w-[150px]" />
<Input {...form.register("rdiiArea")} className="h-12 text-center text-xl w-[225px]" />
</FormSection></div>
<div className="grid grid-cols-2 gap-4">
<div>
@@ -360,7 +330,7 @@ export default function RTKForm() {
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<FormSection name="suyeonga" form={form} titleNm="○ 오수관망도"><div className="w-[150px]">
<FormSection name="suyeonga" form={form} titleNm="○ 오수관망도"><div className="w-[350px]">
<FileUploadForm
key={`file-upload-0-${formResetKey}`} // 폼 초기화를 위한 key
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
@@ -369,7 +339,7 @@ export default function RTKForm() {
accept={""}
fileIdx={0} />
</div></FormSection>
<FormSection name="regopm" form={form} titleNm="○ 수용가-맨홀 매칭결과"><div className="w-[150px]">
<FormSection name="regopm" form={form} titleNm="○ 수용가-맨홀 매칭결과"><div className="w-[350px]">
<FileUploadForm
key={`file-upload-1-${formResetKey}`} // 폼 초기화를 위한 key
onFileSelect={(file, cd, id) => handleFileSelection(1, file, cd, id)}
@@ -378,21 +348,7 @@ export default function RTKForm() {
accept={".xls, .xlsx, .xlsm, .xltx"}
fileIdx={1} />
</div></FormSection>
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용기간'>
<DatePicker
selected={form.watch("date")}
onSelect={(date: Date | undefined) => {
if (date) {
form.setValue("date", date, { shouldValidate: true });
}
}}
mode={"single"}
/>
</FormSection>
<FormSection name='rate' form={form} titleNm='○ 오수전환율'>
<Input {...form.register("rate")} className="h-[3.2rem] text-center text-xl w-[150px]" />
</FormSection>
<FormSection name="rain" form={form} titleNm="○ 강우자료"><div className="w-[150px]">
<FormSection name="rain" form={form} titleNm="○ 강우자료"><div className="w-[350px]">
<FileUploadForm
key={`file-upload-2-${formResetKey}`} // 폼 초기화를 위한 key
onFileSelect={(file, cd, id) => handleFileSelection(2, file, cd, id)}
@@ -401,8 +357,23 @@ export default function RTKForm() {
accept={".xls, .xlsx, .xlsm, .xltx"}
fileIdx={2} />
</div></FormSection>
<FormSection name='rate' form={form} titleNm='○ 오수전환율'>
<Input {...form.register("rate")} className="h-12 text-center text-xl w-[225px] ml-5" />
</FormSection>
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용기간'>
<DatePicker
selected={form.watch("date")}
onSelect={(date: Date | undefined) => {
if (date) {
form.setValue("date", date, { shouldValidate: true });
}
}}
mode={"single"}
className="ml-5"
/>
</FormSection>
<FormSection name='rate' form={form} titleNm='○ 분석 정보'>
<Input {...form.register("analysisInfo")} className="h-[3.2rem] text-center text-xl w-[150px]" />
<Input {...form.register("analysisInfo")} className="h-12 text-center text-xl w-[225px] ml-5" />
</FormSection>
</div>
<div className="flex justify-end mt-1">
+12 -6
View File
@@ -438,7 +438,7 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
<div className="flex justify-between items-center mb-3 gap-4">
<div className="flex items-center">
<SelectBox2
className="w-auto mr-3"
className="w-52 mr-3"
selectClass="rounded-lg bg-white"
label="구분"
selectArray={BASIC_TYPES}
@@ -616,7 +616,10 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
<SidoSigunguSelectBox
sido={시도}
sigungu={시군구}
onSidoChange={set시도}
onSidoChange={(sidoId) => {
set시도(sidoId);
set시군구(0);
}}
onSigunguChange={set시군구}
/>
<Label className="w-auto text-xl text-gray-600 mr-2">하수처리구역명</Label>
@@ -851,7 +854,7 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
<div className="border rounded-lg p-6 mb-4 w-full h-[22vh]">
<div className="flex items-center mb-6">
<SelectBox2
className="w-auto mr-8"
className="w-60 mr-8"
selectClass="rounded-lg bg-white text-xl"
label="업무구분"
selectArray={WORK_TYPES.map(w => ({ val: w.val, text: w.text }))}
@@ -896,12 +899,15 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
<SidoSigunguSelectBox
sido={시도}
sigungu={시군구}
onSidoChange={set시도}
onSidoChange={(sidoId) => {
set시도(sidoId);
set시군구(0);
}}
onSigunguChange={set시군구}
/>
<SelectBox2
className="w-auto mr-2"
selectClass="rounded-lg bg-white text-xl"
selectClass="w-32 rounded-lg bg-white text-xl" // width값 수정 필요
label="하수처리구역"
selectArray={처리구역List.map(opt => ({ val: opt.val, text: opt.text }))}
onSelectValue={val => set처리구역(String(val))}
@@ -1010,7 +1016,7 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
z-[101]
`}
body={renderBody()}
headerTitle="검색"
// headerTitle="검색"
// 필요시 footerContent 등 추가
/>
);