feat: DynamicTable adapter for Untitled UI Table + import preview upgrade

- DynamicTable component: wraps Table for dynamic/unknown columns with headerRenderer support
- Import wizard preview now uses DynamicTable instead of plain HTML table
- Fixed modal height (80vh) to prevent jitter between wizard steps
- Campaign card shows actual linked lead count, not marketing metric

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 13:53:46 +05:30
parent f0ed4ad32b
commit b8ae561d0f
2 changed files with 106 additions and 58 deletions

View File

@@ -0,0 +1,61 @@
import type { ReactNode } from 'react';
import { TableBody as AriaTableBody } from 'react-aria-components';
import { Table } from './table';
export type DynamicColumn = {
id: string;
label: string;
headerRenderer?: () => ReactNode;
width?: string;
};
export type DynamicRow = {
id: string;
[key: string]: any;
};
interface DynamicTableProps<T extends DynamicRow> {
columns: DynamicColumn[];
rows: T[];
renderCell: (row: T, columnId: string) => ReactNode;
rowClassName?: (row: T) => string;
size?: 'sm' | 'md';
maxRows?: number;
}
export const DynamicTable = <T extends DynamicRow>({
columns,
rows,
renderCell,
rowClassName,
size = 'sm',
maxRows,
}: DynamicTableProps<T>) => {
const displayRows = maxRows ? rows.slice(0, maxRows) : rows;
return (
<Table size={size} aria-label="Dynamic table">
<Table.Header>
{columns.map(col => (
<Table.Head key={col.id} id={col.id} label={col.headerRenderer ? '' : col.label}>
{col.headerRenderer?.()}
</Table.Head>
))}
</Table.Header>
<AriaTableBody items={displayRows}>
{(row) => (
<Table.Row
id={row.id}
className={rowClassName?.(row)}
>
{columns.map(col => (
<Table.Cell key={col.id}>
{renderCell(row, col.id)}
</Table.Cell>
))}
</Table.Row>
)}
</AriaTableBody>
</Table>
);
};