"use client"; import { ReactNode, Fragment, useState, RefObject } from "react"; import { LucideIcon, Trash2 } from "lucide-react"; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent } from "@/components/ui/tabs"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { PageLayout } from "@/components/organisms/PageLayout"; import { PageHeader } from "@/components/organisms/PageHeader"; import { StatCards } from "@/components/organisms/StatCards"; import { SearchFilter } from "@/components/organisms/SearchFilter"; import { ScreenVersionHistory } from "@/components/organisms/ScreenVersionHistory"; import { TabChip } from "@/components/atoms/TabChip"; /** * 기본 통합 목록_버젼2 * * 품목관리 스타일의 완전한 목록 템플릿 * - PageHeader, StatCards, SearchFilter, ScreenVersionHistory * - 탭 기반 필터 (데스크톱: TabsList, 모바일: 커스텀 버튼) * - 체크박스 포함 DataTable (Desktop) * - 체크박스 포함 모바일 카드 (Mobile) * - 페이지네이션 */ export interface TabOption { value: string; label: string; count: number; color?: string; // 모바일 탭 색상 } export interface TableColumn { key: string; label: string; className?: string; hideOnMobile?: boolean; hideOnTablet?: boolean; } export interface PaginationConfig { currentPage: number; totalPages: number; totalItems: number; itemsPerPage: number; onPageChange: (page: number) => void; } export interface StatCard { label: string; value: string | number; icon: LucideIcon; iconColor: string; } export interface VersionHistoryItem { version: string; description: string; modifiedBy: string; modifiedAt: string; } export interface DevMetadata { componentName: string; pagePath: string; description: string; apis?: any[]; dataStructures?: any[]; dbSchema?: any[]; businessLogic?: any[]; } export interface IntegratedListTemplateV2Props { // 페이지 헤더 title: string; description?: string; icon?: LucideIcon; headerActions?: ReactNode; // 통계 카드 stats?: StatCard[]; // 버전 이력 versionHistory?: VersionHistoryItem[]; versionHistoryTitle?: string; // 검색 및 필터 searchValue: string; onSearchChange: (value: string) => void; searchPlaceholder?: string; extraFilters?: ReactNode; // Select, DatePicker 등 추가 필터 // 탭 (품목 유형, 상태 등) tabs: TabOption[]; activeTab: string; onTabChange: (value: string) => void; // 테이블 컬럼 tableColumns: TableColumn[]; tableTitle?: string; // "전체 목록 (100개)" 같은 타이틀 // 데이터 data: T[]; // 데스크톱용 페이지네이션된 데이터 totalCount?: number; // 전체 데이터 개수 (역순 번호 계산용) allData?: T[]; // 모바일 인피니티 스크롤용 전체 필터된 데이터 mobileDisplayCount?: number; // 모바일에서 표시할 개수 onLoadMore?: () => void; // 더 불러오기 콜백 infinityScrollSentinelRef?: RefObject; // 인피니티 스크롤용 sentinel ref // 체크박스 선택 selectedItems: Set; onToggleSelection: (id: string) => void; onToggleSelectAll: () => void; getItemId: (item: T) => string; // 아이템에서 ID 추출 onBulkDelete?: () => void; // 일괄 삭제 핸들러 // 렌더링 함수 renderTableRow: (item: T, index: number, globalIndex: number) => ReactNode; renderMobileCard: (item: T, index: number, globalIndex: number, isSelected: boolean, onToggle: () => void) => ReactNode; // 페이지네이션 pagination: PaginationConfig; // 개발자 메타데이터 devMetadata?: DevMetadata; } export function IntegratedListTemplateV2({ title, description, icon, headerActions, stats, versionHistory, versionHistoryTitle = "수정 이력", searchValue, onSearchChange, searchPlaceholder = "검색...", extraFilters, tabs, activeTab, onTabChange, tableColumns, tableTitle, data, totalCount, allData, mobileDisplayCount, onLoadMore, infinityScrollSentinelRef, selectedItems, onToggleSelection, onToggleSelectAll, getItemId, onBulkDelete, renderTableRow, renderMobileCard, pagination, devMetadata, }: IntegratedListTemplateV2Props) { const [showDeleteDialog, setShowDeleteDialog] = useState(false); const startIndex = (pagination.currentPage - 1) * pagination.itemsPerPage; const allSelected = selectedItems.size === data.length && data.length > 0; // 일괄삭제 확인 핸들러 const handleBulkDeleteClick = () => { setShowDeleteDialog(true); }; // 일괄삭제 실행 const handleConfirmDelete = () => { if (onBulkDelete) { onBulkDelete(); } setShowDeleteDialog(false); }; return ( {/* 페이지 헤더 */} {/* 통계 카드 - 태블릿/데스크톱 */} {stats && stats.length > 0 && (
)} {/* 버전 이력 */} {versionHistory && versionHistory.length > 0 && ( )} {/* 검색 및 필터 */} {/* 목록 카드 */} {/* 데스크톱 (1280px+) - TabChip 탭 */}
{tabs.map((tab) => ( onTabChange(tab.value)} color={tab.color as any} /> ))}
{selectedItems.size >= 2 && onBulkDelete && ( )}
{/* 탭 컨텐츠 */} {tabs.map((tab) => ( {/* 모바일/태블릿/소형 노트북 (~1279px) - 선택 삭제 버튼 */} {selectedItems.size >= 2 && onBulkDelete && (
)} {/* 모바일/태블릿/소형 노트북 (~1279px) 카드 뷰 */}
{(allData && allData.length > 0 ? allData : data).length === 0 ? (
검색 결과가 없습니다.
) : ( // 백엔드가 created_at ASC로 정렬해서 보내줌 (오래된 순) (allData || data).map((item, index) => { const itemId = getItemId(item); const isSelected = selectedItems.has(itemId); // 순차 번호: 1번부터 시작 const globalIndex = index + 1; return (
{renderMobileCard( item, index, globalIndex, isSelected, () => onToggleSelection(itemId) )}
); }) )} {/* 인피니티 스크롤 Sentinel */} {infinityScrollSentinelRef && ( {/* 데스크톱 (1280px+) 테이블 뷰 */}
{tableColumns.map((column) => { // "actions" 컬럼은 항상 렌더링하되, 선택된 항목이 없을 때는 빈 헤더로 표시 return ( {column.key === "actions" && selectedItems.size === 0 ? "" : column.label} ); })} {data.length === 0 ? ( 검색 결과가 없습니다. ) : ( // 백엔드가 created_at ASC로 정렬해서 보내줌 (오래된 순) data.map((item, index) => { const itemId = getItemId(item); // 순차 번호: startIndex 기준으로 1부터 시작 const globalIndex = startIndex + index + 1; return ( {renderTableRow(item, index, globalIndex)} ); }) )}
))} {/* 페이지네이션 - 데스크톱에서만 표시 */} {pagination.totalPages > 1 && (
전체 {pagination.totalItems}개 중 {startIndex + 1}-{Math.min(startIndex + pagination.itemsPerPage, pagination.totalItems)}개 표시
{Array.from({ length: pagination.totalPages }, (_, i) => i + 1).map((page) => { // 현재 페이지 근처만 표시 if ( page === 1 || page === pagination.totalPages || (page >= pagination.currentPage - 2 && page <= pagination.currentPage + 2) ) { return ( ); } else if ( page === pagination.currentPage - 3 || page === pagination.currentPage + 3 ) { return ...; } return null; })}
)} {/* 일괄 삭제 확인 다이얼로그 */} 일괄 삭제 확인 선택된 항목을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다. ); }