Multi-step approval workflow

Select rows from an approval queue and act on them in bulk. The toolbar buttons change based on which rows are selected — already-resolved requests (approved or rejected) are automatically excluded from the action, so reviewers can't accidentally re-process them.

Request
Requester
Dept
Amount
Status
Submitted
New MacBook Pro
Sam Rivera
Engineering
$2,499
Pending
2024-05-14
Figma Teams plan
Priya Kapoor
Design
$720
Pending
2024-05-14
AWS reserved instance
Aria Chen
Engineering
$8,400
Needs info
2024-05-13
Office chairs (x4)
Marcus Webb
Product
$1,200
Pending
2024-05-12
Tableau license
Jordan Ellis
Analytics
$1,800
Approved
2024-05-10
Conference travel
Taylor Brooks
Sales
$950
Rejected
2024-05-09
Slack Enterprise
Casey Morgan
Engineering
$3,600
Pending
2024-05-08

Disabling already-resolved rows

Pass selectableRowDisabled to prevent selecting rows that can't be acted on. The toolbar still shows how many are excluded so nothing is silently skipped:

const ACTIONABLE: Status[] = ['pending', 'needs-info'];

<DataTable
  selectableRows
  selectableRowDisabled={r => !ACTIONABLE.includes(r.status)}
  onSelectedRowsChange={({ selectedRows }) => setSelected(selectedRows)}
/>

Context-aware bulk toolbar

Filter the selection down to actionable rows before rendering buttons, and include a count so reviewers know when some rows were skipped:

const actionable = selected.filter(r => ACTIONABLE.includes(r.status));

{selected.length > 0 && (
  <div>
    <span>{selected.length} selected</span>
    {actionable.length < selected.length && (
      <span>({selected.length - actionable.length} already resolved, excluded)</span>
    )}
    {actionable.length > 0 && (
      <>
        <button onClick={() => applyStatus(actionable.map(r => r.id), 'approved')}>
          Approve ({actionable.length})
        </button>
        <button onClick={() => applyStatus(actionable.map(r => r.id), 'rejected')}>
          Reject
        </button>
      </>
    )}
  </div>
)}

Clearing selection after an action

Use the imperative ref to clear the checkboxes after each bulk action commits:

const ref = useRef<DataTableHandle>(null);

function applyStatus(ids: number[], next: Status) {
  setData(prev => prev.map(r => ids.includes(r.id) ? { ...r, status: next } : r));
  ref.current?.clearSelectedRows();
}

<DataTable ref={ref} ... />