Files
invyone/frontend/lib/registry/components/table/useTableData.ts
T

151 lines
3.9 KiB
TypeScript

"use client";
import { useState, useEffect, useCallback, useRef } from "react";
/**
* useTableData — 통합 table 컴포넌트 데이터 fetch 훅
*
* entityJoinApi.getTableDataWithJoins() 호출.
* 페이지네이션, 정렬, 검색 상태를 관리.
*/
export interface UseTableDataParams {
tableName?: string;
page?: number;
pageSize?: number;
sortBy?: string;
sortOrder?: "asc" | "desc";
search?: Record<string, any>;
enabled?: boolean; // false면 fetch 안 함 (디자인 모드)
}
export interface UseTableDataResult {
data: Record<string, any>[];
total: number;
totalPages: number;
page: number;
pageSize: number;
sortBy: string;
sortOrder: "asc" | "desc";
loading: boolean;
error: string | null;
// 액션
setPage: (p: number) => void;
setPageSize: (s: number) => void;
setSortBy: (col: string) => void;
toggleSort: (col: string) => void;
setSearch: (s: Record<string, any>) => void;
refresh: () => void;
}
export function useTableData(params: UseTableDataParams): UseTableDataResult {
const {
tableName,
page: initialPage = 1,
pageSize: initialPageSize = 20,
sortBy: initialSortBy = "",
sortOrder: initialSortOrder = "desc",
search: externalSearch,
enabled = true,
} = params;
const [data, setData] = useState<Record<string, any>[]>([]);
const [total, setTotal] = useState(0);
const [totalPages, setTotalPages] = useState(1);
const [page, setPage] = useState(initialPage);
const [pageSize, setPageSize] = useState(initialPageSize);
const [sortBy, setSortBy] = useState(initialSortBy);
const [sortOrder, setSortOrder] = useState<"asc" | "desc">(initialSortOrder);
const [search, setSearch] = useState<Record<string, any>>(externalSearch || {});
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const refreshKey = useRef(0);
// 외부 검색 조건 동기화
useEffect(() => {
if (externalSearch) {
setSearch(externalSearch);
setPage(1);
}
}, [externalSearch]);
// 데이터 fetch
const fetchData = useCallback(async () => {
if (!tableName || !enabled) return;
setLoading(true);
setError(null);
try {
// 동적 import — 번들 사이즈 최적화
const { entityJoinApi } = await import("@/lib/api/entityJoin");
const response = await entityJoinApi.getTableDataWithJoins(tableName, {
page,
size: pageSize,
sortBy: sortBy || undefined,
sortOrder,
search: Object.keys(search).length > 0 ? search : undefined,
enableEntityJoin: true,
});
setData(response.data || []);
setTotal(response.total || 0);
setTotalPages(response.totalPages || 1);
} catch (err: any) {
console.error("[useTableData] fetch 실패:", err);
setError(err?.message || "데이터 로드 실패");
setData([]);
setTotal(0);
setTotalPages(1);
} finally {
setLoading(false);
}
}, [tableName, page, pageSize, sortBy, sortOrder, search, enabled, refreshKey.current]);
useEffect(() => {
fetchData();
}, [fetchData]);
// 테이블 변경 시 페이지 리셋
useEffect(() => {
setPage(1);
setSortBy("");
setSearch({});
}, [tableName]);
const toggleSort = useCallback((col: string) => {
setSortBy((prev) => {
if (prev === col) {
setSortOrder((o) => (o === "asc" ? "desc" : "asc"));
return col;
}
setSortOrder("asc");
return col;
});
}, []);
const refresh = useCallback(() => {
refreshKey.current += 1;
fetchData();
}, [fetchData]);
return {
data,
total,
totalPages,
page,
pageSize,
sortBy,
sortOrder,
loading,
error,
setPage,
setPageSize: (s: number) => { setPageSize(s); setPage(1); },
setSortBy,
toggleSort,
setSearch: (s: Record<string, any>) => { setSearch(s); setPage(1); },
refresh,
};
}