270 lines
9.3 KiB
TypeScript
270 lines
9.3 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;
|
|
isIr?: boolean;
|
|
isRdii?: boolean;
|
|
}
|
|
|
|
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,
|
|
isIr = false,
|
|
isRdii = false,
|
|
}: 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>
|
|
)}
|
|
{/* 침입수 분석 */}
|
|
{isIr && (
|
|
<Button
|
|
size="sm"
|
|
className="mx-4 py-2 text-white rounded-sm text-lg font-medium green"
|
|
>
|
|
<a href="/samples/ir_setting_sample.zip" download="침입수 분석 업로드 파일 양식.zip">양식 받기</a>
|
|
</Button>
|
|
)}
|
|
{/* RDII 분석 */}
|
|
{isRdii && (
|
|
<Button
|
|
size="sm"
|
|
className="mx-4 py-2 text-white rounded-sm text-lg font-medium green"
|
|
>
|
|
<a href="/samples/rdii_sample.zip" download="RDII 분석 업로드 파일 양식.zip">양식 받기</a>
|
|
</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 |