ERP-node/frontend/lib/registry/components/entity-search-input/useEntitySearch.ts

125 lines
3.6 KiB
TypeScript

import { useState, useCallback, useEffect, useRef } from "react";
import { apiClient } from "@/lib/api/client";
import { EntitySearchResult, EntitySearchResponse } from "./types";
interface UseEntitySearchProps {
tableName: string;
searchFields?: string[];
filterCondition?: Record<string, any>;
}
export function useEntitySearch({
tableName,
searchFields = [],
filterCondition = {},
}: UseEntitySearchProps) {
const [searchText, setSearchText] = useState("");
const [results, setResults] = useState<EntitySearchResult[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [pagination, setPagination] = useState({
total: 0,
page: 1,
limit: 20,
});
// searchFields와 filterCondition을 ref로 관리하여 useCallback 의존성 문제 해결
const searchFieldsRef = useRef(searchFields);
const filterConditionRef = useRef(filterCondition);
useEffect(() => {
searchFieldsRef.current = searchFields;
filterConditionRef.current = filterCondition;
}, [searchFields, filterCondition]);
const search = useCallback(
async (text: string, page: number = 1) => {
// tableName 유효성 검증
if (!tableName || tableName === "undefined" || tableName === "null") {
console.warn("엔티티 검색 건너뜀: tableName이 없음", { tableName });
setError("테이블명이 설정되지 않았습니다. 컴포넌트 설정을 확인해주세요.");
return;
}
try {
setLoading(true);
setError(null);
const params = new URLSearchParams({
searchText: text,
searchFields: searchFieldsRef.current.join(","),
filterCondition: JSON.stringify(filterConditionRef.current),
page: page.toString(),
limit: pagination.limit.toString(),
});
console.log("[useEntitySearch] 검색 실행:", {
tableName,
filterCondition: filterConditionRef.current,
searchText: text,
});
const response = await apiClient.get<EntitySearchResponse>(
`/entity-search/${tableName}?${params.toString()}`
);
if (response.data.success) {
setResults(response.data.data);
if (response.data.pagination) {
setPagination(response.data.pagination);
}
} else {
setError(response.data.error || "검색에 실패했습니다");
}
} catch (err: any) {
console.error("Entity search error:", err);
const errorMessage = err.response?.data?.message || "검색 중 오류가 발생했습니다";
setError(errorMessage);
} finally {
setLoading(false);
}
},
[tableName, pagination.limit]
);
// 디바운스된 검색
useEffect(() => {
// searchText가 명시적으로 설정되지 않은 경우(null/undefined)만 건너뛰기
if (searchText === null || searchText === undefined) {
return;
}
const timer = setTimeout(() => {
// 빈 문자열("")도 검색 (전체 목록 조회)
search(searchText.trim(), 1);
}, 300); // 300ms 디바운스
return () => clearTimeout(timer);
}, [searchText, search]);
const clearSearch = useCallback(() => {
setSearchText("");
setResults([]);
setError(null);
}, []);
const loadMore = useCallback(() => {
if (pagination.page * pagination.limit < pagination.total) {
search(searchText, pagination.page + 1);
}
}, [search, searchText, pagination]);
return {
searchText,
setSearchText,
results,
loading,
error,
pagination,
search,
clearSearch,
loadMore,
};
}