Skip to content
Nana UI

Rental

Bulk Progress

Progress for a long run over many items — posting readings, generating invoices, sending reminders — where individual items can fail without stopping the rest.

View .md Download

Month-end in a rental system is a batch: 120 invoices, 90 reminders, a meter reading posted for every unit. Some of them will fail — a unit with no reading, a tenant with no email — and the rest should carry on regardless.

A bar that only says “84 of 120” leaves the reader to find out later which three were skipped. This keeps the failed items and their reasons in view while the run goes, and offers them back for a retry when it stops.

npx @nana-tec/ui add bulk-progress

Lands as src/components/ui/bulk-progress/, with progress.ts beside the component — the counting and the wording live there, so a worker or a job summary can reuse them without importing the view.

import { BulkProgress } from "@/components/ui/bulk-progress";
export function InvoiceRun({ run, retry }) {
return (
<BulkProgress
label="Generating invoices"
unit="invoice"
status={run.status}
total={run.total}
done={run.done}
failures={run.failures}
onRetry={(failures) => retry(failures.map((f) => f.id))}
/>
);
}

failures is an array of { label, reason?, id? }. onRetry hands the whole array back, id included, so the caller knows exactly which items to run again.

status="running" counts up. The line under the bar reports what has come back — successes and failures together, so it matches the bar — and names the failures separately. cancellable adds a stop button that fires nana-cancel.

Retry is deliberately absent until the run stops: retrying halfway would race the run itself.

Leave total at 0 and the bar runs indeterminate, counting what has come back so far. Useful when the work is streamed rather than counted up front.

units covers the plurals an s will not fix.

StatusReads asTone
idle120 invoices to processNeutral
running87 of 120 invoices · 3 failedPrimary
complete, nothing failedAll 120 invoices doneSuccess
complete, some failed117 of 120 invoices done · 3 failedWarning
cancelledStopped after 41 of 120 invoices · 1 failedWarning

A run that ends with failures opens its failure list once, unasked — the reader has come back to a finished job and the failures are the only thing left to do. Once they close it themselves, it stays as they left it.

The generated list is a label and a reason. Anything richer — a link to the unit, a per-item retry, a status chip — goes in the failures slot on the Web Component, or the renderFailures prop on the framework builds.

Unit 4BNo meter readingOpen unit
Unit 9ANo email addressOpen unit

The bar carries role="progressbar" with its current value, and the failure list is a proper disclosure — a button with aria-expanded and aria-controls over the region it opens.

The spoken summary is deliberately coarse. A live region that changed on every one of 120 items would talk over the page for the length of the run, so the announcement only moves at the milestones: the run starts, the run ends, the run is stopped. The count itself stays readable on screen and through the progress bar’s own value.

The React and Svelte props, read from the components’ own types:

BulkProgress

PropTypeDefault
label
What the run is called.
string''
total
How many items the run covers; `0` while the count is still unknown.
number0
done
How many items have come back successfully.
number0
failures
The items that failed, newest last.
BulkFailure[][]
statusBulkProgressStatus'idle'
unit
What the items are called, singular.
string'item'
units
Plural of `unit`, for the words an `s` will not fix.
string
renderFailures
Replaces the generated failure list.
ReactNode
onRetry(failures: BulkFailure[]) => void
onCancel() => void
retryLabelstring'Retry failed'
cancelLabelstring'Cancel'
actionsReactNode
childrenReactNode

Also accepts Omit<HTMLAttributes<HTMLDivElement>, 'children'>.

The attribute names, events and CSS hooks below are the Web Components reference.

Properties

PropertyAttributeTypeDefaultDescription
cancelLabelcancel-labelstring'Cancel'
cancellablecancellablebooleanfalseOffer to stop the run while it is going
donedonenumber0How many items have come back successfully
failuresfailuresBulkFailure[][]The items that failed, newest last. Set the property to an array, or the attribute to the same array as JSON.
labellabelstring''What the run is called
noRetryno-retrybooleanfalseHide the retry button
openopenbooleanfalseShow the failure list expanded
retryLabelretry-labelstring'Retry failed'
statusstatusBulkProgressStatus'idle'Where the run has got to
totaltotalnumber0How many items the run covers; 0 while the count is still unknown
unitunitstring'item'What the items are called, singular
unitsunitsstring''Plural of unit, for the words an s will not fix

Events

EventDescription
nana-retryThe reader asked to run the failed items again (detail.failures)
nana-cancelThe reader asked to stop the run

Slots

SlotDescription
(default)Extra detail under the summary
failuresReplaces the generated failure list
actionsExtra buttons beside the retry control

CSS parts

PartDescription
::part(base)The container
::part(header)The title row
::part(title)The run's name
::part(status)The status chip
::part(track)The progress bar's track, forwarded from nana-progress-bar
::part(fill)The progress bar's fill, forwarded from nana-progress-bar
::part(summary)The count line under the bar
::part(failures)The failure region
::part(failure)One failed item
::part(toggle)The disclosure button for the failure list
::part(actions)The actions row
::part(retry)The retry button
::part(cancel)The cancel button

CSS custom properties

PropertyDescription
--nana-bulk-progress-bgContainer background
--nana-bulk-progress-borderContainer border colour
--nana-bulk-progress-list-heightHeight the failure list scrolls after
--nana-press-scaleHow far the buttons depress on press, shared with the other pressable controls
EventTypeDescription
nana-retryCustomEventThe reader asked to run the failed items again (detail.failures)
nana-cancelCustomEventThe reader asked to stop the run
SlotDescription
(default)Extra detail under the summary
failuresReplaces the generated failure list
actionsExtra buttons beside the retry control
PartDescription
baseThe container
headerThe title row
titleThe run’s name
statusThe status chip
summaryThe count line under the bar
failuresThe failure region
failureOne failed item
toggleThe disclosure button for the failure list
actionsThe actions row
retryThe retry button
cancelThe cancel button
Explore Bulk Progress interactively Every state, prop, and edge case — with live controls — in Storybook.
View in Storybook