feat(sn): CS 기준 UI 통일 및 지도 팝업 표준화
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* ➡️ 경로 : I/I 분석 모든 페이지
|
||||
*
|
||||
*
|
||||
* 📄 Base Layout
|
||||
* 분석 페이지 기본 레이아웃 컴포넌트
|
||||
*
|
||||
@@ -11,58 +11,57 @@
|
||||
* - 양식 파일 다운로드 링크 제공
|
||||
* - 폼 제출 및 이벤트 핸들링
|
||||
*/
|
||||
'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";
|
||||
"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
|
||||
}
|
||||
mainTitle: string;
|
||||
firstCardTitle: string;
|
||||
secondCardTitle?: string;
|
||||
};
|
||||
type SelectBox = {
|
||||
labelNm?: Label
|
||||
searchChk?: boolean
|
||||
labelNm?: Label;
|
||||
searchChk?: boolean;
|
||||
title?: string;
|
||||
onSearchClick?: () => Promise<void>;
|
||||
}
|
||||
onResetClick?: () => void | Promise<void>;
|
||||
};
|
||||
type Label = {
|
||||
first: string
|
||||
second: string
|
||||
third: string
|
||||
fourth: string
|
||||
}
|
||||
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
|
||||
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
|
||||
sigungu?: ApiSelectOption;
|
||||
selectedSwagePrcsFclt?: string;
|
||||
onSwagePrcsFcltChange?: (value: string) => void;
|
||||
aplcnYmFrom?: string
|
||||
aplcnYmFrom?: string;
|
||||
setAplcnYmFrom?: (aplcnYmFrom: string) => void;
|
||||
aplcnYmTo?: string
|
||||
aplcnYmTo?: string;
|
||||
setAplcnYmTo?: (aplcnYmTo: string) => void;
|
||||
setCtpvCd: (ctpv: string) => void;
|
||||
setSggCd: (sgg: string) => void;
|
||||
@@ -72,12 +71,12 @@ type BaseLayout = {
|
||||
setIsLoading?: (isLoading: boolean) => void;
|
||||
onExcelDownload?: () => void;
|
||||
onDeleteFile?: () => void;
|
||||
onAnalysis?: () => void;
|
||||
onAnalysis?: () => void | Promise<void>;
|
||||
onResultView?: () => void;
|
||||
selectedRowData?: any;
|
||||
isIr?: boolean;
|
||||
isRdii?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
const BaseLayout = ({
|
||||
children,
|
||||
@@ -113,14 +112,8 @@ const BaseLayout = ({
|
||||
isIr = false,
|
||||
isRdii = false,
|
||||
}: BaseLayout) => {
|
||||
|
||||
// 컴포넌트 언마운트 시 confirmToast 정리
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// 컴포넌트가 언마운트될 때 global-confirm-toast ID를 가진 Toast 제거
|
||||
toast.dismiss("global-confirm-toast");
|
||||
};
|
||||
}, []);
|
||||
// 분석 실행 전 CS와 동일한 전역 확인 대화상자를 호출한다.
|
||||
const { confirm } = useConfirm();
|
||||
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
@@ -129,8 +122,8 @@ const BaseLayout = ({
|
||||
onExcelDownload();
|
||||
return;
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
||||
@@ -138,64 +131,75 @@ const BaseLayout = ({
|
||||
onDeleteFile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
// 기존 confirmToast가 있다면 먼저 제거
|
||||
toast.dismiss("global-confirm-toast");
|
||||
|
||||
confirmToast(
|
||||
const handleConfirm = async () => {
|
||||
const shouldRunAnalysis = await confirm(
|
||||
"분석에는 2분 내외의 시간이 소요될 수 있습니다.\n분석을 진행하시겠습니까?",
|
||||
onAnalysis ?? (() => { }),
|
||||
"text-xl font-medium text-gray-800"
|
||||
);
|
||||
if (!shouldRunAnalysis) return;
|
||||
|
||||
await onAnalysis?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form} className="min-h-screen flex flex-col">
|
||||
<ListDiv>
|
||||
<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">{selectBox.title}</h3>
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
{selectBox.title}
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
<div className="ml-2 mt-2 flex items-center w-full">
|
||||
<div className="flex w-full flex-wrap items-center gap-3 p-[1.2rem]">
|
||||
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
||||
<>
|
||||
<Label className="mr-4 w-20 text-xl font-medium text-gray-600 whitespace-nowrap">상수사용기간</Label>
|
||||
<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 형식
|
||||
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} // 시작 연도 설정
|
||||
selectClass="w-[10rem]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
<span className="font-semibold text-xl mx-4">~</span>
|
||||
<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 형식
|
||||
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} // 시작 연도 설정
|
||||
selectClass="w-[10rem]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -207,7 +211,7 @@ const BaseLayout = ({
|
||||
sggName={sggName}
|
||||
onSidoChange={(sidoId: string) => {
|
||||
setCtpvCd(sidoId);
|
||||
setSggCd('0');
|
||||
setSggCd("0");
|
||||
}}
|
||||
onSigunguChange={setSggCd}
|
||||
setSggName={setSggName}
|
||||
@@ -219,86 +223,123 @@ const BaseLayout = ({
|
||||
/>
|
||||
)}
|
||||
{selectBox?.searchChk && (
|
||||
<div className='flex flex-1 justify-end mr-6'>
|
||||
<Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button>
|
||||
<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>
|
||||
</ListDiv>
|
||||
<ListDiv>
|
||||
</section>
|
||||
<section className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">{titleName.firstCardTitle}</h3>
|
||||
<div>
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
{titleName.firstCardTitle}
|
||||
</h3>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{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>
|
||||
<Button type="button" onClick={isChckFile}>
|
||||
엑셀다운로드
|
||||
</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 type="button">
|
||||
<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 type="button">
|
||||
<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>
|
||||
<Button type="button" onClick={handleConfirm}>
|
||||
{analysisBtn}
|
||||
</Button>
|
||||
)}
|
||||
{resultViewBtn && (
|
||||
<Button
|
||||
className="px-4 py-2 bg-slate-600 text-white rounded-sm text-sm font-medium hover:bg-slate-700 !mr-[1.2rem]"
|
||||
size="sm"
|
||||
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-grow flex flex-col">
|
||||
<div className="card !m-[1.2rem] flex-grow flex flex-col">
|
||||
<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>
|
||||
</ListDiv>
|
||||
</section>
|
||||
{children?.[1] != null && (
|
||||
<ListDiv>
|
||||
<section className="card card-border">
|
||||
<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'>
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
{titleName?.secondCardTitle}
|
||||
</h3>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
{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>
|
||||
<Button type="button">
|
||||
엑셀다운로드
|
||||
</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>
|
||||
<Button type="button" color="red">
|
||||
삭제
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem]">{children?.[1]}</div>
|
||||
</ListDiv>
|
||||
<div className="p-[1.2rem]">{children?.[1]}</div>
|
||||
</section>
|
||||
)}
|
||||
</Form>
|
||||
<Toaster position="top-center"></Toaster>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseLayout
|
||||
export default BaseLayout;
|
||||
|
||||
Reference in New Issue
Block a user