'use client'; /** * 1:1 문의 목록 - UniversalListPage 마이그레이션 * * 기존 IntegratedListTemplateV2 → UniversalListPage config 기반으로 변환 * - 날짜 범위 선택 + 문의 등록 버튼 * - 필터 3개: 상담분류, 상태, 정렬 (PC: 인라인, 모바일: 바텀시트) */ import { useState, useMemo, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { MessageSquare } from 'lucide-react'; import { format } from 'date-fns'; import { TableCell, TableRow } from '@/components/ui/table'; import { Card, CardContent } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { Badge } from '@/components/ui/badge'; import { UniversalListPage, type UniversalListConfig, type SelectionHandlers, type RowClickHandlers, } from '@/components/templates/UniversalListPage'; import { type Inquiry, type InquiryCategory, type InquiryStatus, type SortOption, CATEGORY_FILTER_OPTIONS, INQUIRY_STATUSES, SORT_OPTIONS, INQUIRY_CATEGORY_LABELS, INQUIRY_STATUS_LABELS, transformPostToInquiry, } from './types'; import { getPosts, deletePost } from '../shared/actions'; export function InquiryList() { const router = useRouter(); // ===== 날짜 범위 상태 (외부 관리) ===== const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); // ===== 행 클릭 핸들러 ===== const handleRowClick = useCallback( (item: Inquiry) => { router.push(`/ko/customer-center/qna/${item.id}`); }, [router] ); const handleCreate = useCallback(() => { router.push('/ko/customer-center/qna/create'); }, [router]); // ===== 상태 Badge 색상 ===== const getStatusBadge = useCallback((status: InquiryStatus) => { if (status === 'waiting') { return ( {INQUIRY_STATUS_LABELS[status]} ); } return ( {INQUIRY_STATUS_LABELS[status]} ); }, []); // ===== UniversalListPage Config ===== const config: UniversalListConfig = useMemo( () => ({ // 페이지 기본 정보 title: '1:1 문의', description: '1:1 문의를 등록하고 답변을 확인합니다.', icon: MessageSquare, basePath: '/customer-center/qna', // ID 추출 idField: 'id', // API 액션 actions: { getList: async () => { const result = await getPosts('qna', { per_page: 100 }); if (result.success && result.data) { const transformed = result.data.data.map(transformPostToInquiry); return { success: true, data: transformed, totalCount: transformed.length }; } return { success: false, error: result.error }; }, deleteItem: async (id: string) => { const result = await deletePost('qna', id); return { success: result.success, error: result.error }; }, }, // 테이블 컬럼 columns: [ { key: 'no', label: 'No.', className: 'w-[60px] text-center' }, { key: 'category', label: '상담분류', className: 'w-[120px]' }, { key: 'title', label: '제목', className: 'min-w-[200px]' }, { key: 'status', label: '상태', className: 'w-[100px] text-center' }, { key: 'createdAt', label: '등록일', className: 'w-[120px] text-center' }, ], // 클라이언트 사이드 필터링 clientSideFiltering: true, itemsPerPage: 10, // 검색 필터 searchPlaceholder: '제목, 작성자로 검색...', searchFilter: (item, searchValue) => { const searchLower = searchValue.toLowerCase(); return ( item.title.toLowerCase().includes(searchLower) || item.authorName.toLowerCase().includes(searchLower) ); }, // 커스텀 필터 (날짜 + 카테고리 + 상태) customFilterFn: (items, filterValues) => { let result = [...items]; // 날짜 필터 if (startDate && endDate) { result = result.filter((item) => { const itemDate = format(new Date(item.createdAt), 'yyyy-MM-dd'); return itemDate >= startDate && itemDate <= endDate; }); } // 상담분류 필터 const categoryFilter = filterValues.category as string; if (categoryFilter && categoryFilter !== 'all') { result = result.filter((item) => item.category === categoryFilter); } // 상태 필터 const statusFilter = filterValues.status as string; if (statusFilter && statusFilter !== 'all') { result = result.filter((item) => item.status === statusFilter); } return result; }, // 커스텀 정렬 customSortFn: (items, filterValues) => { const sorted = [...items]; const sortOption = (filterValues.sort as SortOption) || 'latest'; if (sortOption === 'latest') { sorted.sort( (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() ); } else { sorted.sort( (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() ); } return sorted; }, // 필터 설정 (PC: 인라인, 모바일: 바텀시트) // IntegratedListTemplateV2에서 "전체" 옵션을 자동 추가하므로 중복 방지를 위해 "all" 제외 filterConfig: [ { key: 'category', label: '상담분류', type: 'single', options: CATEGORY_FILTER_OPTIONS.filter((opt) => opt.value !== 'all').map((opt) => ({ value: opt.value, label: opt.label, })), }, { key: 'status', label: '상태', type: 'single', options: INQUIRY_STATUSES.filter((opt) => opt.value !== 'all').map((opt) => ({ value: opt.value, label: opt.label })), }, { key: 'sort', label: '정렬', type: 'single', options: SORT_OPTIONS.map((opt) => ({ value: opt.value, label: opt.label })), }, ], initialFilters: { category: 'all', status: 'all', sort: 'latest' }, // 공통 헤더 옵션: 날짜 선택기 (왼쪽) dateRangeSelector: { enabled: true, startDate, endDate, onStartDateChange: setStartDate, onEndDateChange: setEndDate, }, // 공통 헤더 옵션: 등록 버튼 (오른쪽 끝) createButton: { label: '문의 등록', onClick: handleCreate, }, // 테이블 헤더 액션 (총 건수만 표시) tableHeaderActions: ({ totalCount }) => ( 총 {totalCount}건 ), // 테이블 행 렌더링 renderTableRow: ( item: Inquiry, index: number, globalIndex: number, handlers: SelectionHandlers & RowClickHandlers ) => { return ( handleRowClick(item)} > e.stopPropagation()}> {globalIndex} {INQUIRY_CATEGORY_LABELS[item.category]} {item.title} {getStatusBadge(item.status)} {format(new Date(item.createdAt), 'yyyy-MM-dd')} ); }, // 모바일 카드 렌더링 renderMobileCard: ( item: Inquiry, index: number, globalIndex: number, handlers: SelectionHandlers & RowClickHandlers ) => { return ( handleRowClick(item)} > e.stopPropagation()}> #{globalIndex} {INQUIRY_CATEGORY_LABELS[item.category]} {getStatusBadge(item.status)} {item.title} {format(new Date(item.createdAt), 'yyyy-MM-dd')} ); }, }), [startDate, endDate, handleRowClick, handleCreate, getStatusBadge] ); return ; } export default InquiryList;