'use client'; import { useCallback, useState, type ChangeEvent, type FormEvent } from 'react'; import { Search } from 'lucide-react'; import { fetchApi } from '@/lib/utils/client'; import type { LiveChannelItem, LiveChannelListResponse } from '@/types/response/channel/live-list'; import LiveChannelCard from '../_components/LiveChannelCard'; import LiveChannelEmpty from '../_components/LiveChannelEmpty'; type SortKey = 'viewers'|'newest'|'name'; type Props = { initialChannels: LiveChannelItem[]; initialTotal: number; pageSize: number; }; const SORT_OPTIONS: { value: SortKey; label: string }[] = [ { value: 'viewers', label: '시청자순' }, { value: 'newest', label: '최신순' }, { value: 'name', label: '채널명순' } ]; export default function LiveExplorer({ initialChannels, initialTotal, pageSize }: Props) { const [channels, setChannels] = useState(initialChannels); const [total, setTotal] = useState(initialTotal); const [keyword, setKeyword] = useState(''); const [appliedKeyword, setAppliedKeyword] = useState(''); const [sort, setSort] = useState('viewers'); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const hasMore = channels.length < total; const fetchList = useCallback(async (kw: string, sortKey: SortKey, offset: number) => { setLoading(true); setError(null); try { const params = new URLSearchParams({ limit: String(pageSize), offset: String(offset), sort: sortKey }); if (kw) { params.set('keyword', kw); } const res = await fetchApi(`/api/channel/live-list?${params.toString()}`, { silent: true }); if (res.success && res.data) { const data = res.data; setChannels(prev => offset === 0 ? data.channels : [...prev, ...data.channels]); setTotal(data.total); } else { setError('목록을 불러오지 못했습니다.'); } } catch { setError('목록을 불러오지 못했습니다.'); } finally { setLoading(false); } }, [pageSize]); const handleSearch = useCallback((e: FormEvent) => { e.preventDefault(); setAppliedKeyword(keyword); fetchList(keyword, sort, 0); }, [keyword, sort, fetchList]); const handleSortChange = useCallback((e: ChangeEvent) => { const next = e.target.value as SortKey; setSort(next); fetchList(appliedKeyword, next, 0); }, [appliedKeyword, fetchList]); const handleLoadMore = useCallback(() => { if (loading || !hasMore) { return; } fetchList(appliedKeyword, sort, channels.length); }, [loading, hasMore, appliedKeyword, sort, channels.length, fetchList]); return (

생방송 {total.toLocaleString()}

setKeyword(e.target.value)} placeholder="채널명 또는 핸들 검색" aria-label="검색" />
{channels.length === 0 ? ( ) : (
{channels.map((ch, idx) => (
))}
)} {hasMore && (
)} {error &&
{error}
}
); }