o2o-negosium-original/negodata/front/src/features/cards/components/CardTable.tsx

112 lines
4.4 KiB
TypeScript

import type { ReactNode } from 'react';
import { DataTable } from '@/components/ui/data-table';
import { renderEmphasis } from '@/lib/emphasis';
import type { NegotiationCard } from '../types';
type CardTableProps = {
data: NegotiationCard[];
onEdit: (card: NegotiationCard) => void;
footer?: ReactNode;
};
export function CardTable({ data, onEdit, footer }: CardTableProps) {
return (
<DataTable
data={data}
rowKey={(card) => card.id}
onRowClick={onEdit}
empty="데이터가 없습니다."
footer={footer}
columns={[
{
header: '구분',
headClassName: 'w-24',
cell: (card) => (
<div className="flex flex-col items-start gap-1">
{card.isWildcard ? (
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-bold bg-rose-50 text-rose-700 dark:bg-rose-950/20 dark:text-rose-400 border border-rose-200/40">
와일드카드
</span>
) : (
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-bold bg-blue-50 text-blue-700 dark:bg-blue-950/20 dark:text-blue-400 border border-blue-200/40">
협상카드
</span>
)}
{card.isShared && (
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-bold bg-violet-50 text-violet-700 dark:bg-violet-950/20 dark:text-violet-400 border border-violet-200/40">
전체
</span>
)}
</div>
),
},
{
header: '카드번호',
headClassName: 'w-28',
cellClassName: 'font-mono font-semibold text-foreground',
cell: (card) => card.code,
},
{
header: '카드이름',
headClassName: 'w-52',
mobileHeader: true, // 모바일 카드뷰 제목(첫 컬럼 '구분' 배지 대신)
cell: (card) => (
<>
<div className="font-semibold text-foreground text-sm">{card.title}</div>
{card.memo && (
<div className="text-[10px] text-muted-foreground font-mono mt-0.5 max-w-[200px] truncate">
{card.memo}
</div>
)}
</>
),
},
{
header: '스크립트',
headClassName: 'w-[22rem]', // 컬럼 폭 고정 → 긴 스크립트가 표를 늘리지 않게
cellClassName: 'font-mono text-muted-foreground',
mobileBlock: true, // 긴 미리보기 블록 → 모바일 카드뷰에서 라벨 아래 풀폭
cell: (card) => (
<div className="max-w-[22rem] truncate bg-muted/20 px-2 py-1 rounded border border-border/30 text-[11px] leading-snug">
{/* 마커(**굵게**·{{색}})를 스타일로 렌더 — 표 미리보기에 원시 마커가 보이지 않게 */}
{renderEmphasis(card.scriptPreview)}
</div>
),
},
{
header: '사용 조건 (와일드 전용)',
headClassName: 'w-40',
cellClassName: 'font-mono text-xs font-semibold text-rose-600 dark:text-rose-400',
cell: (card) => (card.isWildcard ? card.triggerCondition || '조건 없음' : '-'),
},
{
header: '상태',
align: 'center',
headClassName: 'w-24',
cell: (card) =>
card.isWildcard ? (
<span
className={`inline-flex items-center px-2 py-0.5 text-[10px] font-semibold rounded-full border ${
card.status === 'ACTIVE'
? 'bg-emerald-50 text-emerald-700 dark:bg-emerald-950/20 dark:text-emerald-400 border-emerald-300/40'
: 'bg-amber-50 text-amber-700 dark:bg-amber-950/20 dark:text-amber-400 border-amber-300/45'
}`}
>
{card.status === 'ACTIVE' ? '협상적용 가능' : '적용 대기(수동)'}
</span>
) : (
<span className="text-muted-foreground font-mono font-medium opacity-65">-</span>
),
},
{
header: '작성자',
align: 'center',
headClassName: 'w-24',
cellClassName: 'text-muted-foreground whitespace-nowrap',
cell: (card) => (card.isShared ? '공용' : card.creatorName ?? '-'),
},
]}
/>
);
}