320 lines
12 KiB
TypeScript
320 lines
12 KiB
TypeScript
/**
|
|
* ➡️ 경로 : I/I 분석 - 침입수 분석결과
|
|
*
|
|
* 📄 Result Page
|
|
* 침입수 분석 결과 페이지 컴포넌트
|
|
*
|
|
* - 침입수 분석 결과 조회 및 테이블 표시
|
|
* - 페이징 및 검색 기능 (시도/시군구/하수처리시설/기간 기반)
|
|
* - 분석 결과 선택 및 엑셀 다운로드 기능
|
|
* - 분석 결과 파일 삭제 기능
|
|
* - 로딩 오버레이 및 에러 핸들링 지원
|
|
* - Zustand를 통한 상태 관리 및 데이터 포맷팅
|
|
*
|
|
* 🔗 사용 API
|
|
* - 침입수분석 목록 조회: GET /api/v1/inftwtAnls (sewerage-service)
|
|
* - 침입수분석결과관로 목록 조회: GET /api/v1/inftwtAnlsRslt/ppl/{inftwtAnlsSn} (sewerage-service)
|
|
* - 침입수분석결과 삭제: DELETE /api/v1/inftwtAnlsRslt/{inftwtAnlsSn} (sewerage-service)
|
|
* - 침입수분석 삭제: DELETE /api/v1/inftwtAnls/{inftwtAnlsSn} (sewerage-service)
|
|
*/
|
|
'use client'
|
|
import { deleteData, getData } from '@/components/lib/api'
|
|
import { UiTable, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { useState, useEffect } from 'react'
|
|
import { useForm } from 'react-hook-form'
|
|
import { z } from 'zod'
|
|
import BaseLayout from '../BaseLayout'
|
|
import { handleDelete } from '@/components/lib/utils'
|
|
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
|
import useStore from '../../../store/useStore';
|
|
|
|
const formSchema = z.object({
|
|
swtrmtFcltCd: z.string().default('0'), // 하수처리시설코드
|
|
swtrmtFcltNm: z.string().default(''), // 하수처리시설명
|
|
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
// const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
// return `${year}${month}`;
|
|
return `${year}01`;
|
|
}), // 분석기간 시작연월
|
|
aplcnYmTo: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
return `${year}${month}`;
|
|
}), // 분석기간 종료연월
|
|
})
|
|
|
|
type ResultFormValues = z.infer<typeof formSchema>
|
|
|
|
const tableColumn = [
|
|
{ id: 'rowNumber', title: '일련번호' },
|
|
{ id: 'ctpvNm', title: '시도' },
|
|
{ id: 'sggNm', title: '시군구' },
|
|
{ id: 'swtrmtFcltNm', title: '하수처리시설' },
|
|
{ id: 'aplcnYm', title: '적용연월' },
|
|
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
|
{ id: 'anlsInfo', title: '침입수 분석정보' },
|
|
{ id: 'regDt', title: '분석일자' },
|
|
{ id: 'rgtrNm', title: '분석자' },
|
|
{ id: 'scsYn', title: '분석성공여부' }
|
|
]
|
|
|
|
const Result = () => {
|
|
//const AUTH = useCookieAuth();
|
|
|
|
const form = useForm<ResultFormValues>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues: {
|
|
swtrmtFcltCd: '0',
|
|
swtrmtFcltNm: '',
|
|
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
// const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
// return `${year}${month}`;
|
|
return `${year}01`;
|
|
})(),
|
|
aplcnYmTo: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
return `${year}${month}`;
|
|
})(),
|
|
}
|
|
})
|
|
|
|
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
|
const [totalData, setTotalData] = useState(0) //총 데이터 수l
|
|
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
|
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
|
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
|
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
|
const [curPage, setCurPage] = useState(1); // 현재 페이지
|
|
const [ctpvCd, setCtpvCd] = useState<string>('0'); // 시도 코드 상태
|
|
const [sggCd, setSggCd] = useState<string>('0'); // 시군구 코드 상태
|
|
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
|
const { ctpv, sgg } = useStore();
|
|
|
|
useEffect(() => {
|
|
// setCurPage(1);
|
|
handleSearchButtonClick();
|
|
}, [])
|
|
|
|
// 페이지 변경 시마다 데이터 fetch
|
|
useEffect(() => {
|
|
setIsResetPage(false);
|
|
handleSearchButtonClick();
|
|
}, [curPage]);
|
|
|
|
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
|
|
const handleSearch: () => Promise<void> = async () => {
|
|
setCurPage(1); // 페이지를 1로 초기화
|
|
setIsResetPage(true); // 페이지를 초기화
|
|
await handleSearchButtonClick();
|
|
};
|
|
|
|
const handleSearchButtonClick = async () => {
|
|
setIsLoadingResult(true);
|
|
setResetSelection(true);
|
|
const formValues = form.getValues(); // react-hook-form에서 현재 값 가져오기
|
|
|
|
let inftwtAnlsSearchDto = {
|
|
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
|
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
|
"ctpvCd": ctpvCd === '0' ? null : ctpvCd.trim(),
|
|
"sggCd": sggCd === '0' ? null : sggCd,
|
|
"swtrmtFcltNm": formValues.swtrmtFcltNm?.trim() ? formValues.swtrmtFcltNm : null,
|
|
//"auth": AUTH,
|
|
};
|
|
|
|
const queryParams = new URLSearchParams({
|
|
page: `${curPage - 1}`,
|
|
size: '10',
|
|
sort: 'inftwtAnlsSn,desc'
|
|
});
|
|
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== null && String(value).trim() !== '') { // undefined, null, 빈 문자열이 아닌 경우에만 추가
|
|
queryParams.append(key, String(value));
|
|
}
|
|
});
|
|
const queryString = queryParams.toString();
|
|
try {
|
|
const data = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
|
|
|
// 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가
|
|
const dataWithRowNumber = (data.content || []).map((item: any, index: number) => {
|
|
// 🎯 먼저 각 아이템을 포맷팅
|
|
const formattedItem = formatDisplayData(item);
|
|
const ctpvOne = ctpv.find(code => code.val.trim() === item?.ctpvCd?.trim());
|
|
const sggOne = sgg.find(code => code.val === item.sggCd);
|
|
// 추가 처리 및 일련번호 추가
|
|
return {
|
|
...formattedItem,
|
|
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
|
|
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경
|
|
ctpvNm: ctpvOne ? ctpvOne.text : item.ctpvCd,
|
|
sggNm: sggOne ? sggOne.text : item.sggCd,
|
|
};
|
|
})
|
|
.filter((item: any) => item.scsYn === 'Y');
|
|
|
|
// 선택된 행 초기화
|
|
setSelectedRowData(null);
|
|
|
|
setTableData(dataWithRowNumber);
|
|
setTotalData(data?.totalElements || 0); // totalElements가 없을 경우 0으로 설정
|
|
setIsLoadingResult(false);
|
|
} catch (error) {
|
|
setIsLoadingResult(false);
|
|
console.error("Error fetching analysis result data:", error);
|
|
alert("분석결과 조회 중 오류가 발생했습니다.");
|
|
setTableData([]);
|
|
setTotalData(0);
|
|
setSelectedRowData(null);
|
|
}
|
|
setResetSelection(false);
|
|
};
|
|
|
|
// 테이블 셀 클릭 핸들러 추가
|
|
const handleCellClick = (index: number | undefined, headId: string) => {
|
|
if (index !== undefined && tableData[index]) {
|
|
setSelectedRowData(tableData[index]);
|
|
}
|
|
};
|
|
|
|
// Excel 다운로드 핸들러 추가
|
|
const handleExcelDownload = async () => {
|
|
setIsLoadingResult(true);
|
|
if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) {
|
|
setIsLoadingResult(false);
|
|
alert('다운로드할 데이터를 선택해주세요.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const data = await getData(`v1/inftwtAnlsRslt/ppl/${selectedRowData.inftwtAnlsSn}`);
|
|
|
|
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
|
const excelData = data.content; // 배열로 감싸서 전달
|
|
|
|
// 헤더 매핑 정의
|
|
const headerMapping: { [key: string]: string } = {
|
|
'inftwtAnlsSn': '침입수분석\n일련번호',
|
|
'pplMngNo': '관로\n관리번호',
|
|
'anlsTm': '분석시각',
|
|
'predcOilq': '예측유량\n(m³/h)'
|
|
};
|
|
|
|
// 파일명 생성 (분석번호 기반)
|
|
const fileName = `침입수분석결과_${selectedRowData.swtrmtFcltNm}_${selectedRowData.aplcnYm}_${selectedRowData.inftwtAnlsSn}`;
|
|
|
|
await downloadExcelWithKoreanHeaders({
|
|
data: excelData as JSON[],
|
|
fileName: fileName,
|
|
sheetNameArray: ["침입수 분석결과"],
|
|
headerRange: "A1:D1",
|
|
headerMapping: headerMapping
|
|
});
|
|
setIsLoadingResult(false);
|
|
} catch (error) {
|
|
setIsLoadingResult(false);
|
|
console.error("Excel 다운로드 중 오류 발생:", error);
|
|
alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요.");
|
|
}
|
|
};
|
|
|
|
// 파일 삭제 핸들러 추가
|
|
const handleFileDelete = async () => {
|
|
setIsLoadingResult(true);
|
|
if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) {
|
|
setIsLoadingResult(false);
|
|
alert('삭제할 파일을 선택해주세요.');
|
|
return;
|
|
}
|
|
// 삭제 확인 대화상자
|
|
const confirmDelete = window.confirm('선택된 분석 결과를 삭제하시겠습니까?');
|
|
if (!confirmDelete) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// // 삭제 API 호출
|
|
await deleteData(`v1/inftwtAnlsRslt/${selectedRowData.inftwtAnlsSn}`);
|
|
await deleteData(`v1/inftwtAnls/${selectedRowData.inftwtAnlsSn}`);
|
|
// 첨부파일(inp파일) 삭제 API 호출
|
|
handleDelete(selectedRowData.atflCd, {
|
|
handleDeleteStart: () => console.log("파일 삭제 시작"),
|
|
handleDeleteSuccess: (response) => console.log("파일 삭제 성공:", response),
|
|
handleDeleteError: (error) => console.error("파일 삭제 실패:", error),
|
|
});
|
|
|
|
// 테이블 선택 상태 초기화 트리거
|
|
setResetSelection(true);
|
|
|
|
// 검색 버튼 클릭 이벤트 트리거
|
|
await handleSearchButtonClick();
|
|
|
|
setIsLoadingResult(false);
|
|
|
|
alert('삭제가 완료되었습니다.');
|
|
|
|
setResetSelection(false);
|
|
} catch (error) {
|
|
setIsLoadingResult(false);
|
|
console.error("분석결과 파일 삭제 중 오류 발생:", error);
|
|
alert("분석결과 파일 삭제 중 오류가 발생했습니다. 다시 시도해주세요.");
|
|
}
|
|
};
|
|
|
|
|
|
return (
|
|
<BaseLayout
|
|
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearch }}
|
|
titleName={{ mainTitle: '분석결과', firstCardTitle: '분석이력' }}
|
|
form={form} // form 인스턴스 전달
|
|
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
|
graphBtn={true}
|
|
showDatePicker={true}
|
|
ctpvCd={ctpvCd}
|
|
sggCd={sggCd}
|
|
sggName={sggName}
|
|
selectedSwagePrcsFclt={form.watch("swtrmtFcltCd")}
|
|
onSwagePrcsFcltChange={(val) => form.setValue("swtrmtFcltCd", String(val))}
|
|
aplcnYmFrom={form.watch("aplcnYmFrom")}
|
|
setAplcnYmFrom={(val: string) => { form.setValue("aplcnYmFrom", val) }}
|
|
aplcnYmTo={form.watch("aplcnYmTo")}
|
|
setAplcnYmTo={(val: string) => { form.setValue("aplcnYmTo", val) }}
|
|
setCtpvCd={setCtpvCd}
|
|
setSggCd={setSggCd}
|
|
setSggName={setSggName}
|
|
setSwtrmtFcltNm={(val: string) => { form.setValue("swtrmtFcltNm", val) }}
|
|
setIsLoading={setIsLoadingResult}
|
|
onExcelDownload={handleExcelDownload}
|
|
onDeleteFile={handleFileDelete}
|
|
selectedRowData={selectedRowData}
|
|
>
|
|
{[<div key='table1' className="flex-1 flex flex-col border rounded-lg bg-white shadow-md min-h-0">
|
|
{/* <div className="mb-[1.2rem] text-xl font-medium">○ 침입수 산정결과 현황</div> */}
|
|
<div className="flex-1 min-h-0">
|
|
<UiTable
|
|
headName=''
|
|
headlist={tableColumn}
|
|
tableData={tableData}
|
|
total={totalData}
|
|
pageSize={10}
|
|
cellClick={handleCellClick}
|
|
onPageChange={setCurPage}
|
|
resetSelection={resetSelection}
|
|
resetPageNo={isResetPage}
|
|
/>
|
|
</div>
|
|
</div>
|
|
]}
|
|
</BaseLayout>
|
|
)
|
|
}
|
|
|
|
export default Result |