import { useEffect, useRef, useState } from 'react'; // 서버사이드 리스트(검색·필터·페이지네이션)의 UI 상태 단일 출처. // 실제 데이터 패칭은 각 도메인 훅(useProducts/usePartners 등)이 이 상태로 // - 검색/필터가 바뀌면 page 를 1 로 리셋한다(다른 결과셋의 동일 페이지로 점프 방지). export type ServerListControls = { page: number; setPage: (p: number) => void; pageSize: number; search: string; // input value (controlled) setSearch: (v: string) => void; submitSearch: () => void; // 엔터/즉시 검색용 (디바운스·최소길이 무시하고 바로 발사) debouncedSearch: string; // 쿼리 파라미터용 (디바운스 적용) filters: Record; setFilter: (key: string, value: string) => void; totalPages: (total: number) => number; // 서버 total → 페이지 수 }; export function useServerList(opts?: { pageSize?: number; initialFilters?: Record; debounceMs?: number; minSearchLength?: number; }): ServerListControls { const pageSize = opts?.pageSize ?? 10; const debounceMs = opts?.debounceMs ?? 500; const minSearchLength = opts?.minSearchLength ?? 2; const [page, setPage] = useState(1); const [search, setSearchInput] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); const [filters, setFilters] = useState>(() => opts?.initialFilters ?? {}); const timerRef = useRef | undefined>(undefined); // 입력 디바운스 → 쿼리용 검색어. // 최소 길이 미만은 빈 검색(전체)으로 둬서 1글자 스캔 요청이 서버로 나가지 않게 막는다. useEffect(() => { timerRef.current = setTimeout(() => { const q = search.trim(); setDebouncedSearch(q.length >= minSearchLength ? q : ''); }, debounceMs); return () => clearTimeout(timerRef.current); }, [search, debounceMs, minSearchLength]); const setSearch = (v: string) => { setSearchInput(v); setPage(1); }; // 엔터: 대기 중인 디바운스 타이머를 버리고 최소길이 무시하고 즉시 1회 발사(의도적 검색). const submitSearch = () => { clearTimeout(timerRef.current); setDebouncedSearch(search.trim()); setPage(1); }; const setFilter = (key: string, value: string) => { setFilters((f) => ({ ...f, [key]: value })); setPage(1); }; const totalPages = (total: number) => Math.max(1, Math.ceil(total / pageSize)); return { page, setPage, pageSize, search, setSearch, submitSearch, debouncedSearch, filters, setFilter, totalPages }; }