All templates

Table dialog

Modal - Table

A modal that previews customers ready to be imported in a selectable table, with Cancel and Import in the footer. Modal, dialog, table, data table, import, upload, CSV, select rows, multi select, bulk action.

npx @adaptavant/edscli templates table-dialog

Code

'use client';

import {
	Button,
	Heading,
	Modal,
	ModalContent,
	ModalFooter,
	ModalHeader,
	Table,
	type TableProps,
	Text,
} from '@adaptavant/eds-core';
import * as React from 'react';

type Customer = {
	id: string;
	name: string;
	email: string;
	phone: string;
	lastVisit: string;
};

const CUSTOMERS: Customer[] = [
	{
		id: '1',
		name: 'Aaron Blake',
		email: 'aaron.blake@example.com',
		phone: '+1 415 555 0101',
		lastVisit: '02 Oct 2026',
	},
	{
		id: '2',
		name: 'Abigail Chen',
		email: 'abigail.chen@example.com',
		phone: '+1 415 555 0132',
		lastVisit: '28 Sep 2026',
	},
	{
		id: '3',
		name: 'Adam Foster',
		email: 'adam.foster@example.com',
		phone: '+1 212 555 0144',
		lastVisit: '21 Sep 2026',
	},
	{
		id: '4',
		name: 'Aisha Patel',
		email: 'aisha.patel@example.com',
		phone: '+1 212 555 0178',
		lastVisit: '15 Sep 2026',
	},
	{
		id: '5',
		name: 'Alice Moreno',
		email: 'alice.moreno@example.com',
		phone: '+1 646 555 0190',
		lastVisit: '09 Sep 2026',
	},
	{
		id: '6',
		name: 'Andre Silva',
		email: 'andre.silva@example.com',
		phone: '+44 20 7946 0123',
		lastVisit: '30 Aug 2026',
	},
	{
		id: '7',
		name: 'Arjun Mehta',
		email: 'arjun.mehta@example.com',
		phone: '+44 20 7946 0958',
		lastVisit: '22 Aug 2026',
	},
	{
		id: '8',
		name: 'Ava Thompson',
		email: 'ava.thompson@example.com',
		phone: '+61 2 5550 1234',
		lastVisit: '14 Aug 2026',
	},
	{
		id: '9',
		name: 'Benjamin Clark',
		email: 'ben.clark@example.com',
		phone: '+1 312 555 0167',
		lastVisit: '03 Aug 2026',
	},
	{
		id: '10',
		name: 'Caleb Turner',
		email: 'caleb.turner@example.com',
		phone: '+1 503 555 0110',
		lastVisit: '27 Jul 2026',
	},
];

const COLUMNS: TableProps<Customer>['columns'] = [
	{ header: 'Name', accessorKey: 'name', size: 180 },
	{ header: 'Email', accessorKey: 'email', size: 240 },
	{ header: 'Phone', accessorKey: 'phone', size: 160 },
	{ header: 'Last visit', accessorKey: 'lastVisit', size: 120 },
];

/** Preview: the table modal, always open. */
export default function TableDialogTemplate() {
	return (
		<div className="h-screen bg-canvas">
			<TableModal onClose={() => {}} onConfirm={() => {}} open />
		</div>
	);
}

/**
 * A modal that previews the customers about to be imported. Every row starts
 * selected; unselecting a row leaves it out of the import.
 */
export function TableModal({
	open,
	onClose,
	onConfirm,
}: {
	open: boolean;
	onClose: () => void;
	onConfirm: (customers: Customer[]) => void;
}) {
	const allIds = CUSTOMERS.map((customer) => customer.id);
	const [selectedIds, setSelectedIds] = React.useState<string[]>(allIds);
	const count = selectedIds.length;

	// Closing resets the selection.
	const close = () => {
		setSelectedIds(allIds);
		onClose();
	};

	return (
		<Modal
			closeButtonProps={{ label: 'Close', onClick: close }}
			onClose={close}
			open={open}
			size={800}
			titleId="table-modal-title"
		>
			<ModalHeader>
				<Heading as="h2" className="text-heading-20" id="table-modal-title">
					Import customers
				</Heading>
			</ModalHeader>
			{/* A fixed height, so only the table scrolls under the summary. */}
			<ModalContent className="flex h-[480px] flex-col gap-4 overflow-hidden">
				<Text className="text-body-14">
					{count} {count === 1 ? 'customer is' : 'customers are'} ready to be
					imported
				</Text>
				<div className="min-h-0 flex-1 overflow-y-auto">
					<Table
						columns={COLUMNS}
						data={CUSTOMERS}
						getRowId={(customer) => customer.id}
						role="grid"
						rowSelection={{
							mode: 'multiple',
							selectedRowIds: selectedIds,
							onRowSelectionChange: setSelectedIds,
							selectAllLabel: 'Select all customers',
							getRowLabel: (customer) => `Select ${customer.name}`,
						}}
					/>
				</div>
			</ModalContent>
			<ModalFooter>
				<Button onClick={close} variant="neutralTertiary">
					Cancel
				</Button>
				<Button
					isDisabled={count === 0}
					onClick={() => {
						onConfirm(
							CUSTOMERS.filter((customer) => selectedIds.includes(customer.id))
						);
						close();
					}}
				>
					Import
				</Button>
			</ModalFooter>
		</Modal>
	);
}