'use client'; import { useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPlus } from '@fortawesome/free-solid-svg-icons'; import { Checkbox } from '@/components/ui/checkbox'; import { fetchApi } from '@/lib/utils/client'; import { useStudioContext } from '@/app/studio/context'; import { useRankConfigContext } from '../context'; import type { RankConfigItem } from '@/types/response/donation/rankConfig'; import { PER_PAGE_OPTIONS } from '@/constants/donation'; import { RANK_PERIODS, RANK_THEMES } from '../constants'; type Props = { items: RankConfigItem[]; loading: boolean; saving: boolean; checkedIDs: Set; setCheckedIDs: React.Dispatch>>; page: number; setPage: React.Dispatch>; perPage: number; setPerPage: React.Dispatch>; onNew: () => void; onEdit: (item: RankConfigItem) => void; onBatchDelete: () => void; }; export default function RankListPanel({ items, loading, saving, checkedIDs, setCheckedIDs, page, setPage, perPage, setPerPage, onNew, onEdit, onBatchDelete }: Props) { const { channelID } = useStudioContext(); const { setItems } = useRankConfigContext(); const [togglingIDs, setTogglingIDs] = useState>(new Set()); // ── 활성/비활성 토글 ───────────────────────────── const handleToggleActive = async (item: RankConfigItem) => { if (!channelID || togglingIDs.has(item.id)) { return; } const next = !item.isActive; setTogglingIDs(prev => { const s = new Set(prev); s.add(item.id); return s; }); setItems(prev => prev.map(x => x.id === item.id ? { ...x, isActive: next } : x)); try { const res = await fetchApi<{ id: number; isActive: boolean }>( `/api/studio/donation/rank/config/${item.id}/active`, { method: 'PATCH', body: { channelID, isActive: next }, silent: true } ); if (!res.success) { setItems(prev => prev.map(x => x.id === item.id ? { ...x, isActive: item.isActive } : x)); alert(res.message || '활성 상태 변경에 실패했습니다.'); } else if (res.data && res.data.isActive !== next) { setItems(prev => prev.map(x => x.id === item.id ? { ...x, isActive: res.data!.isActive } : x)); } } catch (err) { setItems(prev => prev.map(x => x.id === item.id ? { ...x, isActive: item.isActive } : x)); alert(err instanceof Error ? err.message : '활성 상태 변경에 실패했습니다.'); } finally { setTogglingIDs(prev => { const s = new Set(prev); s.delete(item.id); return s; }); } }; // ── 페이징 ─────────────────────────────────────── const totalPages = Math.max(1, Math.ceil(items.length / perPage)); const pagedItems = items.slice((page - 1) * perPage, page * perPage); const handlePerPageChange = (value: number) => { setPerPage(value); setPage(1); }; // ── 전체선택 ───────────────────────────────────── const visibleIDs = pagedItems.map(i => i.id); const checkedCount = visibleIDs.filter(id => checkedIDs.has(id)).length; const allChecked = pagedItems.length > 0 && checkedCount === visibleIDs.length; const isIndeterminate = checkedCount > 0 && checkedCount < visibleIDs.length; const handleSelectAll = () => { setCheckedIDs(prev => { const next = new Set(prev); if (allChecked) { visibleIDs.forEach(id => next.delete(id)); } else { visibleIDs.forEach(id => next.add(id)); } return next; }); }; const handleToggleCheck = (id: number) => { setCheckedIDs(prev => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); }; return (
총 {items.length}개 {checkedIDs.size > 0 && ( ({checkedIDs.size}개 선택) )}
{loading ? (
준비 중...
) : items.length === 0 ? (
등록된 순위 설정이 없습니다.
) : ( {pagedItems.map(item => { const isChecked = checkedIDs.has(item.id); return ( ); })}
제목 기간 테마 최대인원 금액표시 활성 비고
handleToggleCheck(item.id)} aria-label={`${item.id} 선택`} /> {item.title} {RANK_PERIODS.find(p => p.value === item.period)?.label ?? item.period} {RANK_THEMES.find(t => t.value === item.theme)?.label ?? item.theme} {item.maxRankCount}명 {item.isShowAmount ? '표시' : '숨김'}
)}
{totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1).map(p => ( ))}
)}
); }