346 lines
11 KiB
TypeScript
346 lines
11 KiB
TypeScript
/**
|
|
* ➡️ 경로 : I/I 분석 모든 페이지
|
|
*
|
|
* 📄 Base Layout
|
|
* 분석 페이지 기본 레이아웃 컴포넌트
|
|
*
|
|
* - 검색 및 필터링 UI 제공 (시도/시군구/하수처리시설 선택)
|
|
* - 날짜 범위 선택 기능
|
|
* - 파일 다운로드 및 삭제 버튼 지원
|
|
* - 분석 실행 버튼 및 확인 토스트 처리
|
|
* - 양식 파일 다운로드 링크 제공
|
|
* - 폼 제출 및 이벤트 핸들링
|
|
*/
|
|
"use client";
|
|
import { DateMonthPicker, Form } from "@common/business_components/ui";
|
|
import { Button, Label } from "@common/components/ui";
|
|
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
|
import type { ReactNode } from "react";
|
|
import { CtpvSggPrcsFcltSelectBox } from "@/components/ui/ctpvSggPrcsFcltSelectBox";
|
|
|
|
type TitleName = {
|
|
mainTitle: string;
|
|
firstCardTitle: string;
|
|
secondCardTitle?: string;
|
|
};
|
|
type SelectBox = {
|
|
labelNm?: Label;
|
|
searchChk?: boolean;
|
|
title?: string;
|
|
onSearchClick?: () => Promise<void>;
|
|
onResetClick?: () => void | 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;
|
|
resultViewBtn?: string;
|
|
firstDivBtn?: boolean;
|
|
selectBox?: SelectBox;
|
|
selectionComponent?: ReactNode;
|
|
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;
|
|
defaultSwtrmtFcltNm?: string;
|
|
setIsLoading?: (isLoading: boolean) => void;
|
|
onExcelDownload?: () => void;
|
|
onDeleteFile?: () => void;
|
|
onAnalysis?: () => void | Promise<void>;
|
|
onResultView?: () => void;
|
|
selectedRowData?: any;
|
|
isIr?: boolean;
|
|
isRdii?: boolean;
|
|
};
|
|
|
|
const BaseLayout = ({
|
|
children,
|
|
titleName,
|
|
form,
|
|
onSubmitEvent,
|
|
graphBtn = false,
|
|
analysisBtn,
|
|
resultViewBtn,
|
|
selectBox,
|
|
selectionComponent,
|
|
showDatePicker = false,
|
|
ctpvCd,
|
|
sggCd,
|
|
sggName,
|
|
sigungu,
|
|
selectedSwagePrcsFclt,
|
|
onSwagePrcsFcltChange,
|
|
aplcnYmFrom,
|
|
setAplcnYmFrom,
|
|
aplcnYmTo,
|
|
setAplcnYmTo,
|
|
setCtpvCd,
|
|
setSggCd,
|
|
setSggName,
|
|
setSwtrmtFcltNm,
|
|
defaultSwtrmtFcltNm,
|
|
setIsLoading,
|
|
onExcelDownload,
|
|
onDeleteFile,
|
|
onAnalysis,
|
|
onResultView,
|
|
isIr = false,
|
|
isRdii = false,
|
|
}: BaseLayout) => {
|
|
// 분석 실행 전 CS와 동일한 전역 확인 대화상자를 호출한다.
|
|
const { confirm } = useConfirm();
|
|
|
|
// 파일 다운로드
|
|
const isChckFile = () => {
|
|
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
|
if (onExcelDownload) {
|
|
onExcelDownload();
|
|
return;
|
|
}
|
|
return true;
|
|
};
|
|
// 파일 삭제
|
|
const isDeleteFile = async () => {
|
|
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
|
if (onDeleteFile) {
|
|
onDeleteFile();
|
|
return;
|
|
}
|
|
};
|
|
|
|
const handleConfirm = async () => {
|
|
const shouldRunAnalysis = await confirm(
|
|
"분석에는 2분 내외의 시간이 소요될 수 있습니다.\n분석을 진행하시겠습니까?",
|
|
);
|
|
if (!shouldRunAnalysis) return;
|
|
|
|
await onAnalysis?.();
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Form {...form} className="flex min-h-screen flex-col">
|
|
<section className="card card-border">
|
|
{/* searchBox.title이 있으면 h3 태그로 제목을 표시합니다. */}
|
|
{selectBox?.title && (
|
|
<div className="card-header">
|
|
<h3 className="text-2xl font-medium text-label">
|
|
{selectBox.title}
|
|
</h3>
|
|
</div>
|
|
)}
|
|
<div className="flex w-full flex-wrap items-center gap-3 p-[1.2rem]">
|
|
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
|
<>
|
|
<Label className="whitespace-nowrap text-label">
|
|
상수사용기간
|
|
</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="w-[10rem]"
|
|
startYear={1900} // 시작 연도 설정
|
|
/>
|
|
<span className="text-muted-foreground">~</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-[10rem]"
|
|
startYear={1900} // 시작 연도 설정
|
|
/>
|
|
</>
|
|
)}
|
|
{/* 침입수 분석 실행처럼 선택 구조가 다른 화면은 커스텀 selector를 주입하고, 나머지는 기존 시설 selector를 유지한다. */}
|
|
{selectionComponent ?? (
|
|
<CtpvSggPrcsFcltSelectBox
|
|
sido={ctpvCd}
|
|
sigungu={sggCd}
|
|
sggName={sggName}
|
|
onSidoChange={(sidoId: string) => {
|
|
setCtpvCd(sidoId);
|
|
setSggCd("0");
|
|
}}
|
|
onSigunguChange={setSggCd}
|
|
setSggName={setSggName}
|
|
selectedSwagePrcsFclt={selectedSwagePrcsFclt}
|
|
onSwagePrcsFcltChange={onSwagePrcsFcltChange}
|
|
setSwtrmtFcltNm={setSwtrmtFcltNm}
|
|
defaultSwtrmtFcltNm={defaultSwtrmtFcltNm}
|
|
setIsLoading={setIsLoading}
|
|
/>
|
|
)}
|
|
{selectBox?.searchChk && (
|
|
<div className="ml-auto flex items-center gap-2">
|
|
<Button
|
|
type="button"
|
|
onClick={() => selectBox.onSearchClick?.()}
|
|
>
|
|
조회
|
|
</Button>
|
|
{selectBox.onResetClick ? (
|
|
<Button
|
|
type="button"
|
|
color="white"
|
|
onClick={() => selectBox.onResetClick?.()}
|
|
>
|
|
초기화
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
<section className="card card-border">
|
|
<div className="card-header">
|
|
<h3 className="text-2xl font-medium text-label">
|
|
{titleName.firstCardTitle}
|
|
</h3>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
{graphBtn && (
|
|
<Button type="button" onClick={isChckFile}>
|
|
엑셀다운로드
|
|
</Button>
|
|
)}
|
|
{/* 침입수 분석 */}
|
|
{isIr && (
|
|
<Button type="button">
|
|
<a
|
|
href="/samples/ir_setting_sample.zip"
|
|
download="침입수 분석 업로드 파일 양식.zip"
|
|
>
|
|
양식다운로드
|
|
</a>
|
|
</Button>
|
|
)}
|
|
{/* RDII 분석 */}
|
|
{isRdii && (
|
|
<Button type="button">
|
|
<a
|
|
href="/samples/rdii_sample.zip"
|
|
download="RDII 분석 업로드 파일 양식.zip"
|
|
>
|
|
양식다운로드
|
|
</a>
|
|
</Button>
|
|
)}
|
|
{/* 분석 버튼 */}
|
|
{analysisBtn && (
|
|
<Button type="button" onClick={handleConfirm}>
|
|
{analysisBtn}
|
|
</Button>
|
|
)}
|
|
{resultViewBtn && (
|
|
<Button
|
|
type="button"
|
|
color="white"
|
|
onClick={onResultView}
|
|
>
|
|
{resultViewBtn}
|
|
</Button>
|
|
)}
|
|
{graphBtn && (
|
|
<Button
|
|
type="button"
|
|
color="red"
|
|
onClick={isDeleteFile}
|
|
>
|
|
삭제
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<Form {...form}>
|
|
<form
|
|
onSubmit={form.handleSubmit(onSubmitEvent)}
|
|
className="flex flex-grow flex-col"
|
|
>
|
|
<div className="min-w-0 flex-grow p-[1.2rem]">
|
|
{/* 폼 정보 입력 칸 */}
|
|
{children?.[0]}
|
|
</div>
|
|
</form>
|
|
</Form>
|
|
</section>
|
|
{children?.[1] != null && (
|
|
<section className="card card-border">
|
|
<div className="card-header flex items-center">
|
|
<h3 className="text-2xl font-medium text-label">
|
|
{titleName?.secondCardTitle}
|
|
</h3>
|
|
<div className="ml-auto flex items-center gap-2">
|
|
{graphBtn && (
|
|
<Button type="button">
|
|
엑셀다운로드
|
|
</Button>
|
|
)}
|
|
{/* 그래프 있을 시 */}
|
|
{graphBtn && (
|
|
<Button type="button" color="red">
|
|
삭제
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="p-[1.2rem]">{children?.[1]}</div>
|
|
</section>
|
|
)}
|
|
</Form>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default BaseLayout;
|