Skip to main content

List Organizations Block

ListOrganizations is an organization management table built on BaseTable, with optional tabs, search chips, row actions, and pagination.

Installation

npm install @nodeblocks/frontend-list-organization-block

What You Need

ItemWhy it matters
labelsCopy for header cells, status text, row actions, search field, and empty state
dataTable rows (ListOrganizationsRowData[])
listOrganizationsTitleHeader title
searchValue + search chips + handlers (optional)Controlled search draft, chip list, and submit/delete flow
tabs + currentTab + onTabChange (optional)Optional workflow/status filtering UI
statusMatch (optional)Maps raw row status values to active/inactive text in the status column
pagination (optional)Controlled page state
row action + navigation handlers (optional)labels.rowActions + resolveRowAction + action handlers, plus row click navigation
Controlled component

ListOrganizations does not own table state. Keep tab selection, search draft/chips, and pagination in your app, then pass handlers (onTabChange, onSearchFieldChange, onSearchSubmit, pagination.onPageChange) to update state.

Row shape and status labels

Rows use the ListOrganizationsRowData shape (id, createdAt, name, joinDate, status). By default, the generated status column treats raw values 'active' and 'inactive' as active/inactive labels; pass statusMatch when your API uses different status values. statusMatch does not filter tabs or choose row actions; keep those checks aligned with your raw row.status values.

Code Examples

Live Editor
function Example() {
  const allOrganizationData = Array.from({length: 20}, (_, i) => ({
    id: String(i + 1),
    createdAt: new Date(2024, 0, i + 1).toISOString(),
    name: `Organization ${i + 1}`,
    joinDate: new Date(2024, 0, i + 5).toISOString(),
    status: i % 2 === 0 ? 'active' : 'inactive',
  }));

  const tabs = [
    {key: 'all', label: 'All Organizations'},
    {key: 'active', label: 'Active'},
    {key: 'inactive', label: 'Inactive'},
  ];

  const labels = {
    emptyStateMessage: 'No organizations found',
    searchFieldPlaceholder: 'Search organizations...',
    rowActions: {
      activate: 'Activate Organization',
      deactivate: 'Deactivate Organization',
    },
    headerRow: {
      createdAt: 'Created At',
      name: 'Organization Name',
      joinDate: 'Join Date',
      status: 'Status',
    },
    cellData: {
      statusActive: 'Active',
      statusInactive: 'Inactive',
    },
  };

  const [currentTab, setCurrentTab] = React.useState('all');
  const [searchValue, setSearchValue] = React.useState('');
  const [currentPage, setCurrentPage] = React.useState(1);
  const [lastAction, setLastAction] = React.useState('Search, activation, and navigation feedback will appear here.');
  const itemsPerPage = 5;

  const filteredByTab = allOrganizationData.filter(row => {
    if (currentTab === 'active') return row.status === 'active';
    if (currentTab === 'inactive') return row.status === 'inactive';
    return true;
  });

  const filtered = searchValue.trim()
    ? filteredByTab.filter(row => {
        const keyword = searchValue.toLowerCase();
        return row.name.toLowerCase().includes(keyword) || row.status.toLowerCase().includes(keyword);
      })
    : filteredByTab;

  const totalPages = Math.max(1, Math.ceil(filtered.length / itemsPerPage));
  const start = (currentPage - 1) * itemsPerPage;
  const data = filtered.slice(start, start + itemsPerPage);

  return (
    <>
      <ListOrganizations
        listOrganizationsTitle="Organizations Management"
        labels={labels}
        data={data}
        tabs={tabs}
        currentTab={currentTab}
        onTabChange={tab => {
          setCurrentTab(tab);
          setCurrentPage(1);
        }}
        searchValue={searchValue}
        onSearchFieldChange={setSearchValue}
        onSearchSubmit={() => setLastAction(`Search submitted: ${searchValue}`)}
        statusMatch={{active: 'active', inactive: 'inactive'}}
        resolveRowAction={row => (row.status === 'active' ? ['deactivate'] : ['activate'])}
        onItemActivate={rowId => setLastAction(`Activate organization: ${rowId}`)}
        onItemDeactivate={rowId => setLastAction(`Deactivate organization: ${rowId}`)}
        pagination={{
          currentPage,
          totalPages,
          onPageChange: setCurrentPage,
        }}
        rowHref={row => `/organizations/${row.id}`}
        onNavigate={to => setLastAction(`Navigate: ${to}`)}
      />
      <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div>
    </>
  );
}
Result
Loading...

Important Props

Core Props

PropTypeRequiredDefaultDescription
labels{ emptyStateMessage: string; searchFieldPlaceholder: string; rowActions?: { activate: string; deactivate: string }; headerRow: { createdAt: string; name: string; joinDate: string; status: string }; cellData: { statusActive: string; statusInactive: string } }Yes-UI copy for search, table cells, row actions, and empty state
dataListOrganizationsRowData[]Yes-Table rows
listOrganizationsTitleReactNodeYes-Header title
isLoadingbooleanNoundefinedLoading state for content/pagination
searchValuestringNoundefinedControlled search input value
onSearchFieldChange(value: string) => voidNoundefinedSearch input change handler
onSearchSubmit() => voidNoundefinedTriggered by search icon click or Enter key
searchChipsTitleReactNodeNoundefinedLabel above active search chips
searchChipsBaseTableSearchChip[]NoundefinedActive search chips
onSearchChipDelete(chip: BaseTableSearchChip, index: number, event: SyntheticEvent) => voidNoundefinedRemove-chip handler
tabs{ key: string; label: string; isDisabled?: boolean; subtitle?: string }[]NoundefinedOptional tab definitions
currentTabstringNoundefinedActive tab key
onTabChange(tab: string) => voidNoundefinedTab change handler
statusMatch{ active: string; inactive: string }No{ active: 'active', inactive: 'inactive' }Maps raw row.status values to active/inactive text in the generated status column
pagination{ currentPage: number; totalPages: number; onPageChange: (page: number) => void; className?: string }NoundefinedPage controls (pages are counted from 1)
rowHref(row: ListOrganizationsRowData) => stringNoundefinedBuild row link; requires onNavigate
onNavigate(to: string) => voidNoundefinedCalled when row click resolves rowHref
shouldShowDropdownMenu(row: ListOrganizationsRowData) => booleanNoundefinedHide/show row menu per row
resolveRowAction(row: ListOrganizationsRowData) => ('activate' | 'deactivate')[] | undefinedNoundefinedRow action types to render
onItemActivate(rowId: string) => voidNoundefinedHandler for activate action
onItemDeactivate(rowId: string) => voidNoundefinedHandler for deactivate action

ListOrganizationsRowData shape:

PropTypeRequiredDefaultDescription
idstringYes-Unique row id
createdAtstringYes-ISO datetime string used in created-at column
namestringYes-Organization name
joinDatestringYes-ISO datetime string used in join-date column
statusstringYes-Raw status value; statusMatch only controls status-column display, while tabs and row actions use your own comparisons

Content Props

labels keys:

PropTypeRequiredDefaultDescription
labels.emptyStateMessagestringYes-Empty table message
labels.searchFieldPlaceholderstringYes-Search input placeholder
labels.rowActions.activatestringNoundefinedActivate action label
labels.rowActions.deactivatestringNoundefinedDeactivate action label
labels.headerRow.createdAtstringYes-Created-at header
labels.headerRow.namestringYes-Name header
labels.headerRow.joinDatestringYes-Join-date header
labels.headerRow.statusstringYes-Status header
labels.cellData.statusActivestringYes-Cell label for active rows
labels.cellData.statusInactivestringYes-Cell label for inactive rows

Subcomponents:

ComponentPropTypeRequiredDefaultDescription
TitlelistOrganizationsTitleReactNodeNoRoot listOrganizationsTitle via BaseTable headerTitleTitle text (children wins)
TitlechildrenReactNodeNoRoot titleCustom title markup
Actionlabels{ emptyStateMessage: string; searchFieldPlaceholder: string; rowActions?: { activate: string; deactivate: string }; headerRow: { createdAt: string; name: string; joinDate: string; status: string }; cellData: { statusActive: string; statusInactive: string } }NoRoot labelsSearch input copy
ActionsearchValuestringNoRoot searchValueControlled search input
ActiononSearchFieldChange(value: string) => voidNoRoot handlerSearch input change
ActiononSearchSubmit() => voidNoRoot handlerSearch submit
ActionchildrenReactNodeNoBuilt search controlsCustom action content
SearchChipssearchChipsTitleReactNodeNoRoot searchChipsTitleChips group title
SearchChipssearchChipsBaseTableSearchChip[]NoRoot searchChipsChips data
SearchChipsonSearchChipDelete(chip: BaseTableSearchChip, index: number, event: SyntheticEvent) => voidNoRoot handlerDelete-chip callback
TabsvaluestringNoRoot currentTabOverride active tab value
TabsonChangeTabsProps['onChange']NoRoot onTabChangeMUI tab change callback
TabstabPropsMUI Tab props excluding label, value, and disabledNoundefinedProps forwarded to each MUI tab
Tablelabelssame shape as root labelsNoRoot labelsGenerates columns/no-rows overlay/row action labels
TablestatusMatch{ active: string; inactive: string }No{ active: 'active', inactive: 'inactive' }Status-column display mapping for raw row.status values
TablecolumnsBaseTableColumn<ListOrganizationsRowData>[]NoGenerated from labels + statusMatchOverride grid columns
TablerowActions(row: ListOrganizationsRowData) => BaseTableRowAction<ListOrganizationsRowData>[]NoGenerated from row-action propsOverride row actions
TableactionColumnBaseTableActionColumnNo{ pin: 'right' } when row actions existAction column config
TablerowMenuReactNode or functionNoDefault menuCustom row menu UI
TableonRowClick(row: ListOrganizationsRowData) => voidNoDerived from rowHref + onNavigateRow click callback
LoaderchildrenReactNodeNoDefault loaderLoading content
Paginationpagination{ currentPage: number; totalPages: number; onPageChange: (page: number) => void; className?: string }NoRoot paginationPagination controls
PaginationdataListOrganizationsRowData[]NoRoot dataRow count source
PaginationisLoadingbooleanNoRoot isLoadingHides pagination while loading or when there are no rows

Title, Action, Header, Loader, SearchChips, Tabs, Content, Table, and Pagination are ListOrganizations.Title, etc. ListOrganizations.Tabs reads the tab definitions from the root tabs prop.

Layout and Composition Props

PropTypeRequiredDefaultDescription
childrenBlocksOverride | ReactNodeNoundefinedCompound JSX children or function override returning blocks and blockOrder
classNamestringNoundefinedClass on root container (nbb-list-organizations-container)
sxSxPropsNoundefinedMUI system styles for root

ListOrganizations renders a BaseTable root with headerTitle, search actions, optional tabs, rows, row actions, no-rows overlay, and optional pagination. It inherits StackProps passthrough (except children). defaultBlockOrder is header, searchChips, tabs, content, pagination.

Default UI Blocks

BlockBuilt onNotes
ListOrganizations (root)BaseTableRoot table wrapper
ListOrganizations.TitleBaseTable.Header.TitleTitle from listOrganizationsTitle
ListOrganizations.ActionBaseTable.Header.Actions + TextFieldSearch field with search icon submit
ListOrganizations.HeaderBaseTable.HeaderWraps title/actions
ListOrganizations.SearchChipsBaseTable.SearchChipsActive keyword chips block
ListOrganizations.TabsBaseTable.TabsOptional tabs from tabs
ListOrganizations.LoaderBaseTable.Content.LoaderLoading state
ListOrganizations.ContentBaseTable.ContentContent wrapper
ListOrganizations.TableBaseTable.Content.GridData grid with generated columns/actions
ListOrganizations.PaginationBaseTable.PaginationPage controls
no-rows overlay iconPersonOutlinedUsed when data is empty

TypeScript

import {
ListOrganizations,
ListOrganizationsRowData,
BaseTableSearchChip,
} from '@nodeblocks/frontend-list-organization-block';

const rows: ListOrganizationsRowData[] = [
{
id: '1',
createdAt: new Date().toISOString(),
name: 'Organization 1',
joinDate: new Date().toISOString(),
status: 'active',
},
];

const chips: BaseTableSearchChip[] = [{key: 'keyword-1', label: 'Org 1'}];

<ListOrganizations
listOrganizationsTitle="Organizations Management"
labels={{
emptyStateMessage: 'No organizations found',
searchFieldPlaceholder: 'Search organizations...',
rowActions: {activate: 'Activate Organization', deactivate: 'Deactivate Organization'},
headerRow: {
createdAt: 'Created At',
name: 'Organization Name',
joinDate: 'Join Date',
status: 'Status',
},
cellData: {statusActive: 'Active', statusInactive: 'Inactive'},
}}
data={rows}
searchChipsTitle="Search Keywords"
searchChips={chips}
statusMatch={{active: 'active', inactive: 'inactive'}}
/>;