mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-19 17:02:59 +00:00
feat(live-reporting): select page vs all pages in bulk download
The header tick now selects the current page (its intuitive meaning). When the whole page is ticked and more filtered rows exist, a Gmail-style banner offers "Select all N across pages" (and "Clear selection" once everything is selected) — so users can grab a page-sized chunk without selecting the entire project. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
34dd3fe7ae
commit
7a7f7e157e
2 changed files with 69 additions and 7 deletions
|
|
@ -178,7 +178,8 @@ export default function DocumentBulkDownload({
|
|||
{selectedIds.size} selected
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
Tick the properties to include, or use the header box to select all.
|
||||
Tick properties, or use the header box to select the page (then “select
|
||||
all across pages”).
|
||||
</span>
|
||||
{overCap && (
|
||||
<span className="text-xs font-medium text-red-600">
|
||||
|
|
|
|||
|
|
@ -119,21 +119,23 @@ export default function DocumentTable({ data, onOpenDrawer, docStatusMap, portfo
|
|||
enableSorting: false,
|
||||
enableGlobalFilter: false,
|
||||
header: ({ table }) => {
|
||||
const ids = table
|
||||
.getFilteredRowModel()
|
||||
// Header ticks the current page; the banner below offers all pages.
|
||||
const pageIds = table
|
||||
.getRowModel()
|
||||
.rows.map((r) => r.original.landlordPropertyId)
|
||||
.filter((id): id is string => !!id);
|
||||
const allSelected = ids.length > 0 && ids.every((id) => selection.selectedIds.has(id));
|
||||
const someSelected = ids.some((id) => selection.selectedIds.has(id));
|
||||
const allSelected =
|
||||
pageIds.length > 0 && pageIds.every((id) => selection.selectedIds.has(id));
|
||||
const someSelected = pageIds.some((id) => selection.selectedIds.has(id));
|
||||
return (
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-label="Select all filtered properties"
|
||||
aria-label="Select all properties on this page"
|
||||
checked={allSelected}
|
||||
ref={(el) => {
|
||||
if (el) el.indeterminate = someSelected && !allSelected;
|
||||
}}
|
||||
onChange={() => selection.onSetMany(ids, !allSelected)}
|
||||
onChange={() => selection.onSetMany(pageIds, !allSelected)}
|
||||
className="h-4 w-4 accent-brandblue align-middle"
|
||||
/>
|
||||
);
|
||||
|
|
@ -217,6 +219,31 @@ export default function DocumentTable({ data, onOpenDrawer, docStatusMap, portfo
|
|||
const currentPage = table.getState().pagination.pageIndex + 1;
|
||||
const totalFiltered = table.getFilteredRowModel().rows.length;
|
||||
|
||||
// "Select all across pages" banner: shown when the whole current page is
|
||||
// ticked and there are more filtered rows than fit on it (Gmail-style).
|
||||
const pageSelectableIds = selection
|
||||
? table
|
||||
.getRowModel()
|
||||
.rows.map((r) => r.original.landlordPropertyId)
|
||||
.filter((id): id is string => !!id)
|
||||
: [];
|
||||
const filteredSelectableIds = selection
|
||||
? table
|
||||
.getFilteredRowModel()
|
||||
.rows.map((r) => r.original.landlordPropertyId)
|
||||
.filter((id): id is string => !!id)
|
||||
: [];
|
||||
const pageFullySelected =
|
||||
pageSelectableIds.length > 0 &&
|
||||
pageSelectableIds.every((id) => selection!.selectedIds.has(id));
|
||||
const allFilteredSelected =
|
||||
filteredSelectableIds.length > 0 &&
|
||||
filteredSelectableIds.every((id) => selection!.selectedIds.has(id));
|
||||
const showSelectAllBanner =
|
||||
!!selection &&
|
||||
pageFullySelected &&
|
||||
filteredSelectableIds.length > pageSelectableIds.length;
|
||||
|
||||
const retroAssessmentLabel: Record<RetroAssessmentFilter, string> = {
|
||||
all: "All retrofit statuses",
|
||||
none: "No Retrofit Docs",
|
||||
|
|
@ -314,6 +341,40 @@ export default function DocumentTable({ data, onOpenDrawer, docStatusMap, portfo
|
|||
propert{totalFiltered === 1 ? "y" : "ies"}
|
||||
</p>
|
||||
|
||||
{/* Select-all-across-pages banner */}
|
||||
{showSelectAllBanner && (
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-lg border border-brandblue/20 bg-brandlightblue/20 px-3.5 py-2 text-[13px] text-brandblue">
|
||||
{allFilteredSelected ? (
|
||||
<>
|
||||
<span>
|
||||
All <span className="font-semibold">{filteredSelectableIds.length}</span>{" "}
|
||||
matching propert{filteredSelectableIds.length === 1 ? "y is" : "ies are"}{" "}
|
||||
selected.
|
||||
</span>
|
||||
<button
|
||||
onClick={() => selection!.onSetMany(filteredSelectableIds, false)}
|
||||
className="font-semibold underline hover:text-brandblue/80"
|
||||
>
|
||||
Clear selection
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>
|
||||
All <span className="font-semibold">{pageSelectableIds.length}</span> on this
|
||||
page selected.
|
||||
</span>
|
||||
<button
|
||||
onClick={() => selection!.onSetMany(filteredSelectableIds, true)}
|
||||
className="font-semibold underline hover:text-brandblue/80"
|
||||
>
|
||||
Select all {filteredSelectableIds.length} across pages
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Table */}
|
||||
<div className="rounded-xl border border-gray-200 overflow-hidden shadow-sm">
|
||||
<div className="overflow-x-auto">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue