AssociatesTable
Pure presentation of the company's members. It never fetches;
the caller passes the resolved associates (via useBusinessUnit) and owns
the row markup via renderItem. The signed-in user (from
OrganizationProvider) is flagged to the renderer.
import { AssociatesTable } from "@evolve-storefront/feature-organization/associates-table";Renders inside OrganizationProvider.
Preview
Usage
const { data: unit } = useBusinessUnit();
// No children + a renderItem = the standard table; the row is yours.<AssociatesTable associates={unit?.associates.items} renderItem={(associate) => <AssociateRow associate={associate} actions={<RowActions associate={associate} />} />}/>;Props
| Prop | Type | Description |
|---|---|---|
associates? | BusinessUnitAssociate[] | The resolved associates. The caller owns the query; the view renders. |
renderItem? | AssociatesTableRowRenderer<BusinessUnitAssociate> | Renders each associate's row — required for the default composition (no
children). Receives |
emptyAction? | ReactNode | Shown in the empty state (e.g. an invite button). |
children? | ReactNode | Compose a bespoke layout, or omit for the standard table. |
Any other props go to the root element, like on a plain HTML element.
Change the layout
Pass children to AssociatesTable to arrange its parts yourself. Every part is exported from @evolve-storefront/feature-organization/associates-table.
<AssociatesTable> <table className="w-full"> <thead><tr><th>{t("member")}</th><th>{t("roles")}</th><th>{t("actions")}</th></tr></thead> <AssociatesTableRows associates={unit.associates.items} renderItem={(associate) => <AssociateRow associate={associate} />} /> </table></AssociatesTable>;AssociateRowOne row in the associates table.
| Prop | Type | Description |
|---|---|---|
associate | BusinessUnitAssociate | |
isCurrentUser? | boolean | Marks this row as the signed-in user. |
actions? | ReactNode | Row actions (edit roles / remove) — composed by the caller. |
Any other props go to the root element, like on a plain HTML element.
AssociatesTableRowsThe associates <tbody>.
The key goes on a Fragment, not on a wrapper. Every sibling list part wraps
renderItem's output in <li key={…}> and gets the key for free; a <tbody>
has no valid wrapper, so the key was simply dropped and React warned on every
render. A keyed Fragment adds no DOM — which is the only option between
<tbody> and the consumer's <tr>.
| Prop | Type | Description |
|---|---|---|
associates | BusinessUnitAssociate[] | The associates to render. Kept an explicit prop so |
renderItem | AssociatesTableRowRenderer<BusinessUnitAssociate> | Renders each associate's row. Required — the feature owns no row markup. |
Any other props go to the root element, like on a plain HTML element.
Loading and empty states
Render these while the data loads or when there is nothing to show, so the page does not jump when it arrives.
| Empty | AssociatesEmpty | Empty state for the associates table. |
| Loading | AssociatesTableSkeleton | Loading state for AssociatesTable. |
Examples
Empty
// An empty company renders the empty state, with your action in it.<AssociatesTable associates={unit?.associates.items} renderItem={renderItem} emptyAction={<Button onClick={() => setInviting(true)}>{t("invite")}</Button>}/>;LoadingSkeleton and real view adjacent — the only cheap check that the placeholder mirrors the DOM it replaces.
const { data: unit, isPending } = useBusinessUnit();
// Gate on isPending: the query waits for the session rather than settling empty.if (isPending) return <AssociatesTableSkeleton />;
<AssociatesTable associates={unit?.associates.items} renderItem={renderItem} />;Remove From A Row ActionA per-record destructive action from a row's menu.
// The menu item sets state; one confirmation sits outside the table.<> <AssociatesTable associates={associates} renderItem={(associate) => ( <AssociateRow associate={associate} actions={<ActionsMenu label={t("actions")} actions={[{ id: "remove", label: t("remove"), destructive: true, onSelect: () => confirm(associate) }]} />} /> )} /> <RemoveAssociateDialog associate={target} open={open} onOpenChange={setOpen} /></>;
