feat(ui): actualizar lógica de selección en PublicationsTable para manejar solo la página actual

Se modifica el comportamiento del checkbox maestro para que solo seleccione las filas visibles en la página actual, en lugar de toda la colección filtrada. Además, se actualizan las estadísticas de selección para reflejar esta nueva lógica y se renombra la función correspondiente para mayor claridad.
This commit is contained in:
Alexis
2026-06-01 13:42:49 +02:00
parent 552254d4a8
commit c176c91e89
@@ -71,11 +71,9 @@ function TriStateCheckbox({ checked, indeterminate = false, onChange, ariaLabel
* retries and toasts can be handled in one place. * retries and toasts can be handled in one place.
* *
* Selection semantics: * Selection semantics:
* - The master checkbox toggles the WHOLE currently-filtered set (not * - The master checkbox toggles only the rows on the current page.
* just the visible page). This matches the user mental model of * - Selection is stored by ID in the parent and persists across pages,
* "filtrar por 2024 → marcar todas de 2024". * filters and sorts so the user can select page by page.
* - Selection survives filter changes: the stored IDs remain even if
* those rows are no longer visible.
*/ */
export function PublicationsTable({ export function PublicationsTable({
publications, publications,
@@ -152,20 +150,19 @@ export function PublicationsTable({
return filtered.slice(start, start + PAGE_SIZE); return filtered.slice(start, start + PAGE_SIZE);
}, [filtered, currentPage]); }, [filtered, currentPage]);
const selectionStats = useMemo(() => { const pageSelectionStats = useMemo(() => {
if (filtered.length === 0) { if (pageRows.length === 0) {
return { allChecked: false, anyChecked: false, selectedInFiltered: 0 }; return { allChecked: false, anyChecked: false };
} }
let count = 0; let count = 0;
for (const pub of filtered) { for (const pub of pageRows) {
if (selectedIds.has(pub.id)) count += 1; if (selectedIds.has(pub.id)) count += 1;
} }
return { return {
allChecked: count === filtered.length, allChecked: count === pageRows.length,
anyChecked: count > 0, anyChecked: count > 0,
selectedInFiltered: count,
}; };
}, [filtered, selectedIds]); }, [pageRows, selectedIds]);
function toggleSort(key) { function toggleSort(key) {
if (sortKey === key) { if (sortKey === key) {
@@ -189,12 +186,12 @@ export function PublicationsTable({
emit(next); emit(next);
} }
function toggleAllFiltered() { function toggleCurrentPage() {
const next = new Set(selectedIds); const next = new Set(selectedIds);
if (selectionStats.allChecked) { if (pageSelectionStats.allChecked) {
for (const pub of filtered) next.delete(pub.id); for (const pub of pageRows) next.delete(pub.id);
} else { } else {
for (const pub of filtered) next.add(pub.id); for (const pub of pageRows) next.add(pub.id);
} }
emit(next); emit(next);
} }
@@ -378,10 +375,10 @@ export function PublicationsTable({
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<TriStateCheckbox <TriStateCheckbox
checked={selectionStats.allChecked} checked={pageSelectionStats.allChecked}
indeterminate={selectionStats.anyChecked} indeterminate={pageSelectionStats.anyChecked}
onChange={toggleAllFiltered} onChange={toggleCurrentPage}
ariaLabel="Seleccionar todas las publicaciones del filtro actual" ariaLabel="Seleccionar todas las publicaciones de esta página"
/> />
</th> </th>
{COLUMNS.map((col) => ( {COLUMNS.map((col) => (