Files
frontend/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx
T
swcho fa7a7d1bd9 fix:
- 침입수 분석, RDII 분석 실행 후 하수처리시설명 안 보이는 에러 수정
- reportTab.tsx에 불필요 log 삭제
2025-08-12 09:45:48 +09:00

248 lines
8.4 KiB
TypeScript

'use client'
import { DateMonthPicker, Form, SelectBox } from '@common/business_components/ui'
import ListDiv from '@common/business_components/ui/list_div'
import { Button, Label } from '@common/components/ui'
import { useEffect, type ReactNode } from 'react'
import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'
import { confirmToast } from '@/components/ui/toast'
import { toast, Toaster } from "react-hot-toast";
type TitleName = {
mainTitle: string
firstCardTitle: string
secondCardTitle?: string
}
type SelectBox = {
labelNm?: Label
searchChk?: boolean
title?: string;
onSearchClick?: () => Promise<void>;
}
type Label = {
first: string
second: string
third: string
fourth: string
}
// API로부터 받아올 데이터 타입을 정의 (SelectBox에 사용될 형태)
interface ApiSelectOption {
text: string;
val: string;
}
type BaseLayout = {
children?: ReactNode[]
titleName: TitleName
form: any
onSubmitEvent: Function
graphBtn?: boolean
analysisBtn?: string
firstDivBtn?: boolean
selectBox?: SelectBox
showDatePicker?: boolean;
ctpvCd: string;
sggCd: string;
sggName?: string;
sigungu?: ApiSelectOption
selectedSwagePrcsFclt?: string;
onSwagePrcsFcltChange?: (value: string) => void;
aplcnYmFrom?: string
setAplcnYmFrom?: (aplcnYmFrom: string) => void;
aplcnYmTo?: string
setAplcnYmTo?: (aplcnYmTo: string) => void;
setCtpvCd: (ctpv: string) => void;
setSggCd: (sgg: string) => void;
setSggName?: (sggName: string) => void;
setSwtrmtFcltNm?: (swtrmtFcltNm: string) => void;
setIsLoading?: (isLoading: boolean) => void;
onExcelDownload?: () => void;
onDeleteFile?: () => void;
onAnalysis?: () => void;
selectedRowData?: any;
}
const BaseLayout = ({
children,
titleName,
form,
onSubmitEvent,
graphBtn = false,
analysisBtn,
selectBox,
showDatePicker = false,
ctpvCd,
sggCd,
sggName,
sigungu,
selectedSwagePrcsFclt,
onSwagePrcsFcltChange,
aplcnYmFrom,
setAplcnYmFrom,
aplcnYmTo,
setAplcnYmTo,
setCtpvCd,
setSggCd,
setSggName,
setSwtrmtFcltNm,
setIsLoading,
onExcelDownload,
onDeleteFile,
onAnalysis,
}: BaseLayout) => {
// 컴포넌트 언마운트 시 confirmToast 정리
useEffect(() => {
return () => {
// 컴포넌트가 언마운트될 때 global-confirm-toast ID를 가진 Toast 제거
toast.dismiss("global-confirm-toast");
};
}, []);
// 파일 다운로드
const isChckFile = () => {
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
if (onExcelDownload) {
onExcelDownload();
return;
}
return true
}
// 파일 삭제
const isDeleteFile = async () => {
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
if (onDeleteFile) {
onDeleteFile();
return;
}
}
const handleConfirm = () => {
// 기존 confirmToast가 있다면 먼저 제거
toast.dismiss("global-confirm-toast");
confirmToast(
"분석에는 2분 내외의 시간이 소요될 수 있습니다.\n분석을 진행하시겠습니까?",
onAnalysis ?? (() => { }),
"text-xl font-medium text-gray-800"
);
};
return (
<>
<Form {...form} className="min-h-screen flex flex-col">
<ListDiv>
{/* searchBox.title이 있으면 h3 태그로 제목을 표시합니다. */}
{selectBox?.title && (
<div className="card-header">
<h3 className="text-2xl font-medium">{selectBox.title}</h3>
</div>
)}
<div className="ml-2 mt-2 flex items-center w-full">
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
<>
<Label className="mr-4 w-20 text-xl font-medium text-gray-600 whitespace-nowrap">상수사용기간</Label>
<DateMonthPicker
field={{
value: aplcnYmFrom || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
const now = new Date();
const year = now.getFullYear();
// const month = String(now.getMonth() + 1).padStart(2, '0');
// return `${year}${month}`;
return `${year}01`;
})(), // yyyyMM 형식
onChange: (value: string) => {
setAplcnYmFrom?.(value); // string 값을 직접 전달
}
}}
selectClass="ml-8 w-[100px]"
startYear={1900} // 시작 연도 설정
/>
<span className="font-semibold text-xl mx-4">~</span>
<DateMonthPicker
field={{
value: aplcnYmTo || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}${month}`;
})(), // yyyyMM 형식
onChange: (value: string) => {
setAplcnYmTo?.(value); // string 값을 직접 전달
}
}}
selectClass="w-[100px] mr-8"
startYear={1900} // 시작 연도 설정
/>
</>
)}
<CtpvSggPrcsFcltSelectBox
sido={ctpvCd}
sigungu={sggCd}
sggName={sggName}
onSidoChange={(sidoId: string) => {
setCtpvCd(sidoId);
setSggCd('0');
}}
onSigunguChange={setSggCd}
setSggName={setSggName}
selectedSwagePrcsFclt={selectedSwagePrcsFclt}
onSwagePrcsFcltChange={onSwagePrcsFcltChange}
setSwtrmtFcltNm={setSwtrmtFcltNm}
setIsLoading={setIsLoading}
/>
{selectBox?.searchChk && (
<div className='flex flex-1 justify-end mr-6'>
<Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button>
</div>
)}
</div>
</ListDiv>
<ListDiv>
<div className="card-header">
<h3 className="text-2xl font-medium">{titleName.firstCardTitle}</h3>
<div>
{graphBtn && (
<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" onClick={isChckFile}>파일 다운로드</Button>
)}
{/* 그래프 있을 시 */}
{graphBtn && (
<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" onClick={isDeleteFile}>삭제</Button>
)}
{/* 분석 버튼 */}
{analysisBtn && (
<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" onClick={handleConfirm}>{analysisBtn}</Button>
)}
</div>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmitEvent)} className="flex-grow flex flex-col">
<div className="card !m-[1.2rem] flex-grow flex flex-col">
{/* 폼 정보 입력 칸 */}
{children?.[0]}
</div>
</form>
</Form>
</ListDiv>
{children?.[1] != null && (
<ListDiv>
<div className="card-header flex items-center">
<h3 className="text-2xl font-medium">{titleName?.secondCardTitle}</h3>
<div className='flex flex-1 justify-end mr-8'>
{graphBtn && (
<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>
)}
{/* 그래프 있을 시 */}
{graphBtn && (
<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>
<div className="card !m-[1.2rem]">{children?.[1]}</div>
</ListDiv>
)}
</Form>
<Toaster position="top-center"></Toaster>
</>
)
}
export default BaseLayout