DataTableRecetas
Acciones masivas
Operar sobre la selección de la página o sobre todo el resultado filtrado.
Sobre la selección
const bulkActions = useCallback((rows: UserRow[]) => (
<>
<Button size="sm" variant="outline" onClick={() => blockMany(rows.map(r => r.id))}>
Bloquear
</Button>
<Button size="sm" variant="destructive" onClick={() => deleteMany(rows.map(r => r.id))}>
Eliminar
</Button>
</>
), [blockMany, deleteMany]);
<SmartDataTable
selection={{ enabled: true, showCheckbox: true }}
bulkActions={bulkActions}
/* … */
/>
// En la mutación:
const blockMany = useCallback((ids: string[]) => {
startTransition(async () => {
const result = await bulkBlockUsersAction(ids);
if (result.error) return toast.error(result.error);
toast.success(result.success);
tableRef.current?.clearSelection();
void tableRef.current?.silentRefetch();
});
}, []);La barra integrada pone el contador y el botón de limpiar; tú solo aportas los botones.
Sobre TODO el filtro (estilo bandeja de correo)
Cuando el usuario marca la página completa y hay más resultados, la barra ofrece "Seleccionar los N que coinciden". Tu callback recibe el contexto:
const bulkActions = useCallback((rows: UserRow[], context: BulkActionsContext) => (
<Button size="sm" variant="outline" onClick={() => {
if (context.allMatchingSelected) {
// Operar en servidor sobre el FILTRO, no sobre ids
void blockAllMatchingAction({ q: searchTextRef.current, ...filterValuesRef.current });
} else {
void bulkBlockUsersAction(rows.map(r => r.id));
}
}}>
Bloquear {context.allMatchingSelected ? `los ${context.totalRows}` : rows.length}
</Button>
), []);La parte importante
Con allMatchingSelected los ids de la página no bastan: la action del servidor debe
reconstruir el where con los mismos filtros y operar ahí. Por eso una búsqueda nueva limpia
la selección — evita operar sobre filas que ya no coinciden.