"use client"; import { ReactNode } from "react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { ChevronLeft, ChevronRight, LucideIcon } from "lucide-react"; import { StatusBadge, StatusType } from "@/components/molecules/StatusBadge"; import { IconWithBadge } from "@/components/molecules/IconWithBadge"; import { TableActions, TableAction } from "@/components/molecules/TableActions"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; // 셀 타입 정의 export type CellType = | "text" // 일반 텍스트 | "number" // 숫자 (우측 정렬) | "currency" // 통화 (₩ 표시) | "date" // 날짜 | "datetime" // 날짜 + 시간 | "status" // 상태 배지 | "badge" // 일반 배지 | "icon" // 아이콘 | "iconBadge" // 아이콘 + 배지 | "actions" // 액션 버튼들 | "custom"; // 커스텀 렌더링 export interface Column { key: keyof T | string; label: string; type?: CellType; width?: string; align?: "left" | "center" | "right"; sortable?: boolean; className?: string; // 타입별 설정 statusConfig?: { showDot?: boolean; }; badgeConfig?: { variant?: "default" | "secondary" | "destructive" | "outline"; className?: string; }; iconConfig?: { iconColor?: string; iconBgColor?: string; size?: "sm" | "md" | "lg"; }; currencyConfig?: { locale?: string; currency?: string; }; dateConfig?: { format?: "short" | "long" | "relative"; }; // 커스텀 렌더링 render?: (value: any, row: T, index?: number) => ReactNode; // 값 변환 format?: (value: any) => string | number; } interface DataTableProps { columns: Column[]; data: T[]; keyField: keyof T; onRowClick?: (row: T) => void; loading?: boolean; emptyMessage?: string; pagination?: { currentPage: number; totalPages: number; onPageChange: (page: number) => void; showInfo?: boolean; }; striped?: boolean; hoverable?: boolean; compact?: boolean; } // 셀 렌더러 function renderCell(column: Column, value: any, row: T, index?: number): ReactNode { // 값 포맷팅 또는 render 호출 let formattedValue: any; if (column.render) { formattedValue = column.render(value, row, index); } else if (column.format) { formattedValue = column.format(value); } else { formattedValue = value; } // 타입별 렌더링 switch (column.type) { case "number": return {formattedValue?.toLocaleString()}; case "currency": const locale = column.currencyConfig?.locale || "ko-KR"; const currency = column.currencyConfig?.currency || "KRW"; const currencyValue = typeof formattedValue === "number" ? formattedValue.toLocaleString(locale, { style: "currency", currency }) : formattedValue; return {currencyValue}; case "date": if (!formattedValue) return "-"; const dateValue = new Date(formattedValue); return {dateValue.toLocaleDateString("ko-KR")}; case "datetime": if (!formattedValue) return "-"; const datetimeValue = new Date(formattedValue); return ( {datetimeValue.toLocaleDateString("ko-KR")} {datetimeValue.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit" })} ); case "status": return ( ); case "badge": return ( {formattedValue} ); case "icon": const IconComponent = formattedValue as LucideIcon; return IconComponent ? ( ) : null; case "iconBadge": // value should be { icon, label, badge } if (typeof formattedValue === "object" && formattedValue.icon) { return ( ); } return formattedValue; case "actions": // render 함수가 TableAction[] 배열을 반환해야 함 const actions = Array.isArray(formattedValue) ? formattedValue : []; return actions.length > 0 ? : null; case "custom": // 커스텀 타입은 이미 render 함수에서 처리됨 return formattedValue; case "text": default: return formattedValue ?? "-"; } } // 정렬 함수 function getAlignClass(column: Column): string { if (column.align) { return column.align === "center" ? "text-center" : column.align === "right" ? "text-right" : "text-left"; } // 타입별 기본 정렬 switch (column.type) { case "number": case "currency": case "actions": return "text-right"; case "status": case "badge": case "icon": case "iconBadge": return "text-center"; default: return "text-left"; } } export function DataTable({ columns, data, keyField, onRowClick, loading, emptyMessage = "데이터가 없습니다.", pagination, striped = false, hoverable = true, compact = false }: DataTableProps) { return (
{columns.map((column) => ( {column.label} ))} {loading ? ( 로딩 중... ) : data.length === 0 ? ( {emptyMessage} ) : ( data.map((row, rowIndex) => ( onRowClick?.(row)} className={cn( onRowClick && "cursor-pointer", hoverable && "hover:bg-muted/50", striped && rowIndex % 2 === 1 && "bg-muted/20", compact && "h-10" )} > {columns.map((column) => { const value = column.key in row ? row[column.key as keyof T] : null; return ( {renderCell(column, value, row, rowIndex)} ); })} )) )}
{pagination && pagination.totalPages > 1 && (
{pagination.showInfo !== false && (

페이지 {pagination.currentPage} / {pagination.totalPages}

)}
)}
); }