Files
frontend/apps/sn/app/html/(map-layout)/map/searchModal/useBasicTab.ts
T
2026-03-25 13:03:39 +09:00

349 lines
16 KiB
TypeScript

/**
* 📄 useBasicTab Hook
* 검색 모달 기본정보 탭 훅
*
* - 필터링된 시설물 데이터 조회 및 포맷팅
* - 테이블 셀 클릭 시 지도 피처 선택 및 줌 기능
* - 엑셀 다운로드 기능 (한글 헤더 지원)
* - CQL 필터 생성을 통한 쿼리 조건 설정
* - Zustand를 통한 상태 관리 및 로딩 처리
* - 데이터 매핑 및 코드 변환 (시도/시군구, 용도 등)
*
* 🔗 사용 API
* - 관망도 시설물 목록 조회: GET /api/v1/fclts (sewerage-service)
*/
import { downloadExcelWithKoreanHeaders } from '@common/business_components/ui';
import {
MS_SWAGE_PPL_USE_CODES, MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_SHAPE, MS_SWAGE_MHL_CODES, MS_SWAGE_MHL_USE_CODES,
MsSwagePplExcelCol, SwtrmtFcltExcelCol, SwtrmtPrcsZoneExcelCol, MsSwageMhlExcelCol, ExclnSewagePumpExcelCol, stofcbrExcelCol
} from '../components/common/table';
import { getData } from "@/components/lib/api";
import { BASIC_TYPES } from './basicTab';
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
import useStore from '@/store/useStore';
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import { confirmToast } from '@/components/ui/toast'
import { toast } from "react-hot-toast";
import useSearchModalStore from '@/store/useSearchModalStore';
import { swagePplCqlFilters } from './swagePpl';
import { swagePrcsFcltCqlFilters } from './swagePrcsFclt';
import { swagePrcsZoneCqlFilters } from './swagePrcsZone';
import { swageMhlCqlFilters } from './swageMhl';
import { swagePmstnCqlFilters } from './swagePmstn';
import { stofcbrCqlFilters } from './stofcbr';
export default function useBasicTab() {
const {
basicType,
tableData,
curPage,
todayYear, todayMonth, todayDate,
setIsSearchModalOpen,
setTableData,
setTableCount,
setIsLoading,
} = useSearchModalStore();
const { ctpv, sgg } = useStore();
const fetchBasicFilteredData = async () => {
let cqlFilters: string[] = [];
const queryParams = new URLSearchParams({
lyrTypeNm: basicType,
page: `${curPage - 1}`,
size: '10',
sort: 'string',
});
if (basicType === 'tb_ms_swage_ppl') cqlFilters = swagePplCqlFilters(); // 하수관로
else if (basicType === 'tb_il_swage_prcs_fclt') cqlFilters = swagePrcsFcltCqlFilters(); // 하수처리시설
else if (basicType === 'tb_ms_swage_prcs_zone') cqlFilters = swagePrcsZoneCqlFilters(); // 하수처리구역
else if (basicType === 'tb_ms_swage_mhl') cqlFilters = swageMhlCqlFilters(); // 하수맨홀
else if (basicType === 'tb_ms_swage_pmstn') cqlFilters = swagePmstnCqlFilters(); // 하수펌프장
else if (basicType === 'tb_ms_stofcbr') cqlFilters = stofcbrCqlFilters(); // 우수토실
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
const res = await getData(`v1/fclts?${queryParams.toString()}`);
// 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가
const dataWithRowNumber = (res.data || []).map((item: any, index: number) => {
// 🎯 먼저 각 아이템을 포맷팅
const formattedItem = formatDisplayData(item);
let ctpvCd = item?.ctpvNm ?? formattedItem?.ctpvNm ?? '';
let sggCd = item?.sggNm ?? formattedItem?.sggNm ?? '';
if (!ctpvCd || !sggCd) {
const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '')?.trim();
if (!ctpvCd && code) ctpvCd = code.slice(0, 2);
if (!sggCd && code) sggCd = code.slice(0, 5);
}
const matchedMqltCode = basicType === 'tb_ms_swage_ppl'
? MS_SWAGE_PPL_CODES.find(code => code.val === item?.mqltCd?.trim()) // 하수관로 재질
: basicType === 'tb_ms_swage_mhl'
? MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim()) // 맨홀 재질
: undefined;
const matchedMhlUsgCode = MS_SWAGE_MHL_USE_CODES.find(code => code.val === item?.usgCd); // 맨홀 용도
const matchedPplShpCode = MS_SWAGE_PPL_SHAPE.find(code => code.val === item?.fcltyShpCd); // 하수관로 시설물형태코드
const ctpvNm = ctpv.find(code => code.val.trim() === ctpvCd)?.text || item?.ctpvNm;
const sggNm = sgg.find(code => code.val.trim() === sggCd)?.text || item?.sggNm;
const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item?.usgCd?.trim());
// 추가 처리 및 일련번호 추가
return {
...formattedItem,
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item?.scsYn, // scsYn이 null이면 'N'으로 변경
mqltCd: matchedMqltCode ? matchedMqltCode.text : item?.mqltCd,
usgCd: matchedMhlUsgCode ? matchedMhlUsgCode.text : (usgCdObj ? usgCdObj.text : item?.usgCd),
ctpvNm: ctpvNm ?? '-',
sggNm: sggNm ?? '-',
swtrmt_zone_nm: item.swtrmtZoneNm || item.swtrmt_zone_nm || '-',
fcltyShpCd: matchedPplShpCode? matchedPplShpCode.text : item?.fcltyShpCd,
};
});
setTableData(dataWithRowNumber);
// console.log(tableData);
setTableCount(res.count || []);
};
// 기본정보 테이블 cellClick 핸들러
const handleBasicCellClick = async (index?: number | undefined, headId?: String) => {
if (typeof index === "number") {
setIsLoading(true);
const row = tableData[index];
const layerNm = row.lyrTypeNm;
const fid = row.fid;
const core = GisApp.Module.layer as any;
await core.showSearchResultsFromGeoJSON(layerNm, fid);
setIsLoading(false);
setIsSearchModalOpen(false); // SearchModal 닫기
}
};
// 기본정보 Excel 다운로드 핸들러
const basicExcelDownload = async () => {
if (tableData.length === 0) {
alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요.");
return;
}
toast.dismiss("global-confirm-toast");
confirmToast(
"파일 다운로드에는 2분 내외의 시간이 소요될 수 있습니다.\n파일 다운로드를 진행하시겠습니까?",
onDownload ?? (() => { }),
"text-xl font-medium text-gray-800"
);
async function onDownload() {
setIsLoading(true);
try {
const queryParams = new URLSearchParams({
lyrTypeNm: basicType,
size: '10000',
});
let cqlFilters: string[] = [];
let headerMapping: { [key: string]: string } = {};
let headerRange: string = "A1:F1";
switch (basicType) {
case 'tb_ms_swage_ppl': {
// 하수관로
cqlFilters = swagePplCqlFilters()
headerMapping = MsSwagePplExcelCol;
headerRange = "A1:Q1";
break;
}
case 'tb_il_swage_prcs_fclt': {
// 하수처리시설
cqlFilters = swagePrcsFcltCqlFilters();
headerMapping = SwtrmtFcltExcelCol;
headerRange = "A1:J1";
break;
}
case 'tb_ms_swage_prcs_zone': {
// 하수처리구역
cqlFilters = swagePrcsZoneCqlFilters();
headerMapping = SwtrmtPrcsZoneExcelCol;
headerRange = "A1:D1";
break;
}
case 'tb_ms_swage_mhl': {
// 하수맨홀
cqlFilters = swageMhlCqlFilters();
headerMapping = MsSwageMhlExcelCol;
headerRange = "A1:J1";
break;
}
case 'tb_ms_swage_pmstn': {
// 하수펌프장
cqlFilters = swagePmstnCqlFilters();
headerMapping = ExclnSewagePumpExcelCol;
headerRange = "A1:J1";
break;
}
case 'tb_ms_stofcbr': {
// 우수토실
cqlFilters = stofcbrCqlFilters();
headerMapping = stofcbrExcelCol;
headerRange = "A1:H1";
break;
}
}
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
const res = await getData(`v1/fclts?${queryParams.toString()}`);
const mhlUsgCdMap = new Map(MS_SWAGE_MHL_USE_CODES.map(code => [code.val?.trim(), code.text]));
const pplUsgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val?.trim(), code.text]));
const pplMqltCdMap = new Map(MS_SWAGE_PPL_CODES.map(code => [code.val?.trim(), code.text]));
const pplShpCdMap = new Map(MS_SWAGE_PPL_SHAPE.map(code => [code.val?.trim(), code.text])); // 관로 시설물형태코드
const ctpvMap = new Map(ctpv.map(code => [code.val?.trim(), code.text]));
const sggMap = new Map(sgg.map(code => [code.val?.trim(), code.text]));
const mappedContent = (res.data || []).map((item: any) => {
const formattedItem = formatDisplayData(item);
let ctpvCd = item?.ctpvCd ?? formattedItem?.ctpvCd ?? '';
let sggCd = item?.sggCd ?? formattedItem?.sggCd ?? '';
if (!ctpvCd || !sggCd) {
const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '')?.trim();
if (!ctpvCd && code) ctpvCd = code.slice(0, 2);
if (!sggCd && code) sggCd = code.slice(0, 5);
}
// trim 한 번만 수행
const trimmedUsgCd = item?.usgCd?.trim();
// const trimmedMqltCd = item?.mqltCd?.trim();
switch (basicType) {
case 'tb_ms_swage_ppl': {
// 하수관로
return {
fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-',
instMngNo: formattedItem.instMngNo ?? item.instMngNo ?? '-',
ctpvNm: ctpvMap.get(ctpvCd) ?? item.ctpvNm ?? '-',
sggNm: sggMap.get(sggCd) ?? item.sggNm ?? '-',
swtrmt_zone_nm: formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? '-',
instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-',
usgCd: pplUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-',
pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '-',
prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '-',
tmptDpth: formattedItem.tmptDpth ?? item.tmptDpth ?? '-',
tmptMhlMngNo: formattedItem.tmptMhlMngNo ?? item.tmptMhlMngNo ?? '-',
scendDpth: formattedItem.scendDpth ?? item.scendDpth ?? '-',
scendMhlMngNo: formattedItem.scendMhlMngNo ?? item.scendMhlMngNo ?? '-',
vrtcLen: formattedItem.vrtcLen ?? item.vrtcLen ?? '-',
wdthLen: formattedItem.wdthLen ?? item.wdthLen ?? '-',
fcltyShpCd: pplShpCdMap.get(item?.fcltyShpCd?.trim()) || item.fcltyShpCd || formattedItem.fcltyShpCd || '-',
mqltCd: pplMqltCdMap.get(item?.mqltCd?.trim()) || item.mqltCd || formattedItem.mqltCd || '-',
};
}
case 'tb_il_swage_prcs_fclt': {
// 하수처리시설
return {
swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-',
fcltCpct: formattedItem.fcltCpct ?? item.fcltCpct ?? '-',
rgnExpln: formattedItem.rgnExpln ?? item.rgnExpln ?? '-',
oprtngStrtYmd: formattedItem.oprtngStrtYmd ?? item.oprtngStrtYmd ?? '-',
laeNm: formattedItem.laeNm ?? item.laeNm ?? '-',
sareaNm: formattedItem.sareaNm ?? item.sareaNm ?? '-',
rvrNm: formattedItem.rvrNm ?? item.rvrNm ?? '-',
dschgWtstNm: formattedItem.dschgWtstNm ?? item.dschgWtstNm ?? '-',
swtrmtPpltnCnt: formattedItem.swtrmtPpltnCnt ?? item.swtrmtPpltnCnt ?? '-',
rprsMsrcCn: formattedItem.rprsMsrcCn ?? item.rprsMsrcCn ?? '-'
};
}
case 'tb_ms_swage_prcs_zone': {
// 하수처리구역
return {
swtrmtZoneNm: formattedItem.swtrmtZoneNm ?? item.swtrmtZoneNm ?? '-',
ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-',
sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-',
swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-'
};
}
case 'tb_ms_swage_mhl': {
// 하수맨홀
return {
fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-',
ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-',
sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-',
instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-',
usgCd: mhlUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-',
swageMhlDiamVl: formattedItem.swageMhlDiamVl ?? item.swageMhlDiamVl ?? '-',
wdthLen: formattedItem.wdthLen ?? item.wdthLen ?? '-',
vrtcLen: formattedItem.vrtcLen ?? item.vrtcLen ?? '-',
altdVl: formattedItem.altdVl ?? item.altdVl ?? '-',
dpthlVl: formattedItem.dpthlVl ?? item.dpthlVl ?? '-'
};
}
case 'tb_ms_swage_pmstn': {
// 하수펌프장
return {
fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-',
ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-',
sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-',
swagePmstnNm: formattedItem.swagePmstnNm ?? item.swagePmstnNm ?? '-',
instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-',
dailyPrcsCpct: formattedItem.dailyPrcsCpct ?? item.dailyPrcsCpct ?? '-',
maxImpndCpct: formattedItem.maxImpndCpct ?? item.maxImpndCpct ?? '-',
altdVl: formattedItem.altdVl ?? item.altdVl ?? '-',
wtlvVl: formattedItem.wtlvVl ?? item.wtlvVl ?? '-',
exclnWpmpAbltVl: formattedItem.exclnWpmpAbltVl ?? item.exclnWpmpAbltVl ?? '-'
};
}
case 'tb_ms_stofcbr': {
// 우수토실
return {
fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-',
mapshtNo: formattedItem.mapshtNo ?? item.mapshtNo ?? '-',
ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-',
sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-',
instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-',
worflwWallHgt: formattedItem.worflwWallHgt ?? item.worflwWallHgt ?? '-',
swageMhlMngNo: formattedItem.swageMhlMngNo ?? item.swageMhlMngNo ?? '-',
drtnAngl: formattedItem.drtnAngl ?? item.drtnAngl ?? '-'
};
}
}
});
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
const excelData = mappedContent; // 배열로 감싸서 전달
// 파일명 생성 (분석번호 기반)
const label = BASIC_TYPES.find((type) => type.val === basicType)?.text
const fileName = `${label} 조회 결과_${todayYear}${todayMonth}${todayDate}`;
await downloadExcelWithKoreanHeaders({
data: excelData as JSON[],
fileName: fileName,
sheetNameArray: [fileName],
headerRange: headerRange,
headerMapping: headerMapping
});
setIsLoading(false);
} catch (error) {
setIsLoading(false);
console.error("Excel 다운로드 중 오류 발생:", error);
alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요.");
}
}
};
return { fetchBasicFilteredData, handleBasicCellClick, basicExcelDownload };
};