'use client'; /** * 검사 목록 - UniversalListPage 마이그레이션 * * 기존 IntegratedListTemplateV2 → UniversalListPage config 기반으로 변환 * - 서버 사이드 페이지네이션 (getInspections API) * - 통계 카드 (getInspectionStats API) * - 상태별 탭 필터 (전체/대기/진행중/완료) */ import { useState, useEffect, useMemo, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { Plus, ClipboardCheck, Clock, PlayCircle, CheckCircle2, AlertTriangle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { TableCell, TableRow } from '@/components/ui/table'; import { Checkbox } from '@/components/ui/checkbox'; import { UniversalListPage, type UniversalListConfig, type SelectionHandlers, type RowClickHandlers, type TabOption, type StatCard, type ListParams, } from '@/components/templates/UniversalListPage'; import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard'; import { getInspections, getInspectionStats } from './actions'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { statusColorMap, inspectionTypeLabels } from './mockData'; import type { Inspection, InspectionStatus, InspectionStats } from './types'; // 탭 필터 정의 type TabFilter = '전체' | InspectionStatus; // 페이지당 항목 수 const ITEMS_PER_PAGE = 20; export function InspectionList() { const router = useRouter(); // ===== 통계 데이터 (외부 관리) ===== const [statsData, setStatsData] = useState({ waitingCount: 0, inProgressCount: 0, completedCount: 0, defectRate: 0, }); const [totalItems, setTotalItems] = useState(0); // 초기 통계 로드 useEffect(() => { const loadStats = async () => { try { const result = await getInspectionStats(); if (result.success && result.data) { setStatsData(result.data); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[InspectionList] loadStats error:', error); } }; loadStats(); }, []); // ===== 행 클릭 핸들러 ===== const handleRowClick = useCallback( (item: Inspection) => { router.push(`/quality/inspections/${item.id}?mode=view`); }, [router] ); // ===== 등록 핸들러 ===== const handleCreate = useCallback(() => { router.push('/quality/inspections?mode=new'); }, [router]); // ===== 탭 옵션 (통계 기반) ===== const tabs: TabOption[] = useMemo( () => [ { value: '전체', label: '전체', count: totalItems }, { value: '대기', label: '대기', count: statsData.waitingCount, color: 'gray' }, { value: '진행중', label: '진행중', count: statsData.inProgressCount, color: 'blue' }, { value: '완료', label: '완료', count: statsData.completedCount, color: 'green' }, ], [totalItems, statsData] ); // ===== 통계 카드 ===== const stats: StatCard[] = useMemo( () => [ { label: '금일 대기 건수', value: `${statsData.waitingCount}건`, icon: Clock, iconColor: 'text-gray-600', }, { label: '진행 중 검사', value: `${statsData.inProgressCount}건`, icon: PlayCircle, iconColor: 'text-blue-600', }, { label: '금일 완료 건수', value: `${statsData.completedCount}건`, icon: CheckCircle2, iconColor: 'text-green-600', }, { label: '불량 발생률', value: `${statsData.defectRate.toFixed(1)}%`, icon: AlertTriangle, iconColor: statsData.defectRate > 0 ? 'text-red-600' : 'text-gray-400', }, ], [statsData] ); // ===== UniversalListPage Config ===== const config: UniversalListConfig = useMemo( () => ({ // 페이지 기본 정보 title: '검사 목록', description: '품질검사 관리', icon: ClipboardCheck, basePath: '/quality/inspections', // ID 추출 idField: 'id', // API 액션 (서버 사이드 페이지네이션) actions: { getList: async (params?: ListParams) => { try { const result = await getInspections({ page: params?.page || 1, size: params?.pageSize || ITEMS_PER_PAGE, q: params?.search || undefined, status: params?.tab !== '전체' ? (params?.tab as InspectionStatus) : undefined, }); if (result.success) { // 통계 다시 로드 const statsResult = await getInspectionStats(); if (statsResult.success && statsResult.data) { setStatsData(statsResult.data); } // totalItems 업데이트 (탭용) setTotalItems(result.pagination.total); return { success: true, data: result.data, totalCount: result.pagination.total, totalPages: result.pagination.lastPage, }; } return { success: false, error: result.error }; } catch (error) { if (isNextRedirectError(error)) throw error; return { success: false, error: '데이터 로드 중 오류가 발생했습니다.' }; } }, }, // 테이블 컬럼 columns: [ { key: 'no', label: '번호', className: 'w-[60px] text-center' }, { key: 'inspectionType', label: '검사유형', className: 'w-[80px]' }, { key: 'requestDate', label: '요청일', className: 'w-[100px]' }, { key: 'itemName', label: '품목명', className: 'min-w-[150px]' }, { key: 'lotNo', label: 'LOT NO', className: 'min-w-[130px]' }, { key: 'status', label: '상태', className: 'w-[80px]' }, { key: 'inspector', label: '담당자', className: 'w-[80px]' }, ], // 서버 사이드 페이지네이션 clientSideFiltering: false, itemsPerPage: ITEMS_PER_PAGE, // 검색 searchPlaceholder: 'LOT번호, 품목명, 공정명 검색...', // 탭 설정 tabs, defaultTab: '전체', // 통계 카드 stats, // 헤더 액션 (등록 버튼) headerActions: () => ( ), // 테이블 행 렌더링 renderTableRow: ( item: Inspection, index: number, globalIndex: number, handlers: SelectionHandlers & RowClickHandlers ) => { return ( handleRowClick(item)} > e.stopPropagation()}> {globalIndex} {item.inspectionType} {item.requestDate} {item.itemName} {item.lotNo} {item.status} {item.inspector || '-'} ); }, // 모바일 카드 렌더링 renderMobileCard: ( item: Inspection, index: number, globalIndex: number, handlers: SelectionHandlers & RowClickHandlers ) => { return ( handleRowClick(item)} headerBadges={ <> #{globalIndex} {item.inspectionType} } title={item.itemName} statusBadge={ {item.status} } infoGrid={
} /> ); }, }), [tabs, stats, handleRowClick, handleCreate] ); return ; }