224 lines
7.0 KiB
TypeScript
224 lines
7.0 KiB
TypeScript
/**
|
|
* 📄 useReportTab Hook
|
|
* 검색 모달 보고서 탭 훅
|
|
*
|
|
* - 계층적 폴더 구조 평탄화 및 데이터 변환
|
|
* - 파일 목록 조회 및 페이징 처리
|
|
* - 파일 다운로드 기능
|
|
* - 셀 클릭 이벤트 처리
|
|
* - Zustand를 통한 상태 관리 및 로딩 처리
|
|
* - 데이터 매핑 및 코드 변환 (시도/시군구, 보고서 유형 등)
|
|
*
|
|
* 🔗 사용 API
|
|
* - 보고서 파일 조회: GET /api/v1/file (dc-service)
|
|
* - 파일 다운로드: GET /api/v1/fileDownload (dc-service)
|
|
*/
|
|
import { getDcData } from '@/components/lib/api';
|
|
import { fileDownload } from '@/utils';
|
|
import { useReportTabStore } from '@/store/useReportTabStore';
|
|
import useSearchModalStore from '@/store/useSearchModalStore';
|
|
import { useAlertStore } from '@/shared/lib';
|
|
|
|
export type Folder = {
|
|
clsfSystmSn: string;
|
|
upClsfSystmSn: string;
|
|
clsfSystmNm: string;
|
|
children: Folder[];
|
|
};
|
|
export type FlattenedFolder = {
|
|
[key: string]: string | number | undefined;
|
|
};
|
|
export type SelectDataType = {
|
|
text: string;
|
|
val: string;
|
|
sido?: string; // 시도 코드 (선택 사항)
|
|
}
|
|
export type ReportDataFile = {
|
|
no: number;
|
|
fileTitle: string;
|
|
regDt: string;
|
|
fileSn: string;
|
|
filePath: string;
|
|
}
|
|
|
|
const getStringValue = (value: string | number | undefined) => String(value ?? '');
|
|
|
|
const buildDisplayFileName = (files: ReportDataFile[]) => {
|
|
if (files.length === 0) return '-';
|
|
if (files.length === 1) return files[0]?.fileTitle ?? '-';
|
|
|
|
return `${files[0]?.fileTitle ?? '대표 파일'} 외 ${files.length - 1}건`;
|
|
};
|
|
|
|
export default function useReportTab() {
|
|
const alert = useAlertStore((state) => state.setMessage);
|
|
const REPORT_TYPES = [
|
|
{ val: '0', text: '전체' },
|
|
{ val: '1', text: '보고서' },
|
|
{ val: '2', text: '요약보고서' },
|
|
{ val: '3', text: '부록' },
|
|
{ val: '4', text: '기타' }
|
|
];
|
|
|
|
const { curPage, setIsLoading } = useSearchModalStore();
|
|
const { dcFiles: filesOrigin, files, setFiles } = useReportTabStore();
|
|
|
|
/**
|
|
* Flatten a hierarchical folder structure into a flat array.
|
|
* @param data - The hierarchical folder data to flatten.
|
|
* @returns An array of flattened folders with their paths and levels.
|
|
*/
|
|
const flattenFolders = (data: Folder[]): FlattenedFolder[] => {
|
|
const result: FlattenedFolder[] = [];
|
|
const exceptNodeNm = ['부분', '통합', '전체'];
|
|
const addNodeNm = ['보고서', '요약보고서', '부록', '기타']; // level5
|
|
const columnNms: { [key: number]: string } = {
|
|
0: 'root', 1: 'sido', 2: 'sigungu', 3: 'approvalYear',
|
|
4: 'plantZoneGubun', 5: 'reportGubun', 7: 'fileName', 8: 'regDate'
|
|
};
|
|
|
|
const traverse = (
|
|
node: Folder,
|
|
path: { [key: string]: string | number },
|
|
level: number
|
|
) => {
|
|
const isYear = (value: string): boolean => {
|
|
return /^[0-9]{4}년$/.test(value);
|
|
};
|
|
|
|
if (level === 2 && isYear(node.clsfSystmNm)) {
|
|
level++;
|
|
}
|
|
if (level === 4 && addNodeNm.includes(node.clsfSystmNm)) {
|
|
level++; // 레벨을 증가시켜 하위값에 추가되도록 함.
|
|
}
|
|
|
|
const newPath = {
|
|
...path,
|
|
[`${columnNms[level]}`]: node.clsfSystmNm,
|
|
};
|
|
|
|
if (node.children.length === 0) {
|
|
newPath.no = result.length + 1;
|
|
newPath.clsfSystmSn = node.clsfSystmSn; // 파일 조회를 위한 고유번호
|
|
result.push(newPath);
|
|
} else {
|
|
if (level === 4 && exceptNodeNm.includes(node.clsfSystmNm)) {
|
|
level--; // 제외할 노드 이름인 경우, 현재의 레벨에 하위값이 써지도록 함.
|
|
}
|
|
node.children.forEach((child) => {
|
|
traverse(child, newPath, level + 1);
|
|
});
|
|
}
|
|
};
|
|
|
|
if (data && data.length > 0 && data[0]?.clsfSystmNm == '기본계획') {
|
|
data.forEach((root) => traverse(root, {}, 0));
|
|
} else {
|
|
data.forEach((root) => traverse(root, {}, 1));
|
|
}
|
|
return result;
|
|
};
|
|
|
|
const getDcFiles = async (clsfSystmSn: string): Promise<ReportDataFile[]> => {
|
|
if (!clsfSystmSn) {
|
|
console.error('clsfSystmSn is required to get files.');
|
|
return [];
|
|
}
|
|
const queryString = 'clsfSystmSn=' + clsfSystmSn;
|
|
const result = await getDcData(`/api/v1/file${queryString ? `?${queryString}` : ''}`);
|
|
return result;
|
|
};
|
|
|
|
// 파일 이름 추가
|
|
const addFileName = async (data: FlattenedFolder[]) => {
|
|
const fileReportData = await Promise.all(
|
|
data.map(async (item) => {
|
|
const fileArr: ReportDataFile[] = await getDcFiles(String(item.clsfSystmSn));
|
|
if (fileArr.length === 0) {
|
|
return {
|
|
...item,
|
|
fileName: '-',
|
|
regDate: '',
|
|
fileSn: '',
|
|
filePath: '',
|
|
fileCount: 0,
|
|
downloadFileTitle: '',
|
|
};
|
|
}
|
|
|
|
const primaryFile = fileArr[0];
|
|
return {
|
|
...item,
|
|
fileName: buildDisplayFileName(fileArr),
|
|
regDate: primaryFile?.regDt ?? '',
|
|
fileSn: primaryFile?.fileSn ?? '',
|
|
filePath: primaryFile?.filePath ?? '',
|
|
fileCount: fileArr.length,
|
|
downloadFileTitle: primaryFile?.fileTitle ?? '',
|
|
};
|
|
})
|
|
);
|
|
return fileReportData;
|
|
};
|
|
|
|
const getDcFileDownload = async (fileSn: string, fileName: string, filePath: string) => {
|
|
if (filePath === '') {
|
|
alert(`filePath 정보가 없으므로 다운로드가 불가합니다.\nfilesn: ${fileSn}`);
|
|
return;
|
|
}
|
|
const queryString = 'filePath=' + encodeURIComponent(filePath);
|
|
const responseBlob = await getDcData(`/api/v1/fileDownload${queryString ? `?${queryString}` : ''}`, 'blob');
|
|
fileDownload(responseBlob, fileName);
|
|
};
|
|
|
|
const downloadReportFile = async (item: FlattenedFolder) => {
|
|
const fileSn = getStringValue(item.fileSn);
|
|
const filePath = getStringValue(item.filePath);
|
|
const downloadFileTitle = getStringValue(item.downloadFileTitle || item.fileName);
|
|
|
|
if (!fileSn || !filePath || !downloadFileTitle || downloadFileTitle === '-') {
|
|
return;
|
|
}
|
|
|
|
await getDcFileDownload(fileSn, downloadFileTitle, filePath);
|
|
};
|
|
|
|
// 파일 목록 조회
|
|
const getFiles = async () => {
|
|
setIsLoading(true); // 로딩 시작
|
|
try {
|
|
let data = filesOrigin;
|
|
data = data?.slice((curPage - 1) * 10, curPage * 10); // paging
|
|
data = data?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 }));
|
|
setFiles(data);
|
|
} catch (error) {
|
|
console.error('파일 목록 조회 에러:', error);
|
|
} finally {
|
|
setIsLoading(false); // 로딩 종료
|
|
}
|
|
};
|
|
|
|
// cellClick 핸들러
|
|
const handleCellClick = (index: number | undefined) => {
|
|
if (typeof index === 'number' && files[index]) {
|
|
const fileName = files[index].fileTitle;
|
|
const filePath = files[index].filePath;
|
|
if (files[index].fileSn !== '') {
|
|
getDcFileDownload(files[index].fileSn, fileName, filePath);
|
|
}
|
|
}
|
|
};
|
|
|
|
return {
|
|
REPORT_TYPES,
|
|
flattenFolders,
|
|
addFileName,
|
|
getDcFiles,
|
|
getDcFileDownload,
|
|
downloadReportFile,
|
|
getFiles,
|
|
handleCellClick
|
|
};
|
|
}
|