"use client"; import useSWR, { mutate } from "swr"; import { getDocumentList, getDocumentDetail, cancelStandbyFile, downloadDocumentFile, type DocumentListItem, type DocumentDetail, type DownloadFileResult, } from "@/lib/swp/document-service"; // ============================================================================ // SWR Hooks // ============================================================================ /** * 문서 목록 조회 Hook * @param projNo 프로젝트 번호 * @param vndrCd 벤더 코드 (선택) */ export function useDocumentList(projNo: string | null, vndrCd?: string) { const key = projNo ? ["swp-documents", projNo, vndrCd] : null; return useSWR( key, async () => { if (!projNo) return []; return getDocumentList(projNo, vndrCd); }, { revalidateOnFocus: false, // 포커스시 재검증 안함 revalidateOnReconnect: true, // 재연결시 재검증 dedupingInterval: 5000, // 5초간 중복 요청 방지 } ); } /** * 문서 상세 조회 Hook (Rev-Activity-File 트리) * @param projNo 프로젝트 번호 * @param docNo 문서 번호 */ export function useDocumentDetail( projNo: string | null, docNo: string | null ) { const key = projNo && docNo ? ["swp-document-detail", projNo, docNo] : null; return useSWR( key, async () => { if (!projNo || !docNo) throw new Error("projNo and docNo required"); return getDocumentDetail(projNo, docNo); }, { revalidateOnFocus: false, revalidateOnReconnect: true, dedupingInterval: 2000, // 2초간 중복 요청 방지 shouldRetryOnError: false, } ); } // ============================================================================ // Mutation Helpers // ============================================================================ /** * 파일 취소 */ export async function useCancelFile( boxSeq: string, actvSeq: string, userId: string, options?: { onSuccess?: () => void; onError?: (error: Error) => void; } ) { try { await cancelStandbyFile(boxSeq, actvSeq, userId); // 문서 상세 캐시 무효화 (재조회) await mutate( (key: unknown) => Array.isArray(key) && key[0] === "swp-document-detail", undefined, { revalidate: true } ); // 문서 목록 캐시도 무효화 await mutate( (key: unknown) => Array.isArray(key) && key[0] === "swp-documents", undefined, { revalidate: true } ); options?.onSuccess?.(); } catch (error) { options?.onError?.( error instanceof Error ? error : new Error("파일 취소 실패") ); throw error; } } /** * 파일 다운로드 */ export async function useDownloadFile( projNo: string, ownDocNo: string, fileName: string, options?: { onSuccess?: () => void; onError?: (error: string) => void; } ) { try { const result: DownloadFileResult = await downloadDocumentFile( projNo, ownDocNo, fileName ); if (!result.success || !result.data) { const errorMsg = result.error || "파일 다운로드 실패"; options?.onError?.(errorMsg); throw new Error(errorMsg); } // Blob을 다운로드 const blob = new Blob([Buffer.from(result.data)], { type: result.mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = result.fileName || fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); options?.onSuccess?.(); } catch (error) { const errorMsg = error instanceof Error ? error.message : "파일 다운로드 실패"; options?.onError?.(errorMsg); throw error; } } /** * 수동 새로고침 헬퍼 */ export function refreshDocumentList(projNo: string, vndrCd?: string) { return mutate(["swp-documents", projNo, vndrCd]); } export function refreshDocumentDetail(projNo: string, docNo: string) { return mutate(["swp-document-detail", projNo, docNo]); }