TableManager Organism - Usage Examples
A fully generic, reusable table management component that handles common CRUD operations, search, sorting, pagination, and more.
Features
- ✅ Pagination - Built-in paginator with customizable rows per page
- ✅ Search - Optional search functionality with input field
- ✅ Sorting - Column sorting with reset on reorder mode
- ✅ Row Selection - Multi-select with checkboxes
- ✅ Row Reordering - Drag-and-drop reordering
- ✅ Delete Mode - Bulk delete with selection
- ✅ Publish/Toggle Mode - Bulk publish/unpublish
- ✅ Custom Actions - Add your own action buttons
- ✅ Internationalization - Customizable labels
- ✅ Feature Flags - Enable/disable features as needed
- ✅ Type Safe - Full TypeScript support with generics
Important Note
All data items must have an id field (string or number). This is required for row selection, reordering, and internal tracking.
// ✅ Good - has id field
const data = [
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
];
// ❌ Bad - missing id field
const data = [
{ name: "Item 1" },
{ name: "Item 2" },
];
Basic Usage
Minimal Example (Read-only table)
import TableManager from "~/components/organisms/tableManager.organism";
<TableManager
data={items}
columns={[
{ field: "name", header: "Name", sortable: true },
{ field: "email", header: "Email", sortable: true },
]}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
totalRecords={totalRecords}
onPageChange={handlePageChange}
features={{
enableSearch: false,
enableReorder: false,
enablePublish: false,
enableDelete: false,
}}
/>
Full-Featured Example (News Module)
<TableManager
data={transformedData}
columns={[
{ field: "title", header: "Titre de l'actualité", sortable: true },
{ field: "categories", header: "Catégorie(s)", sortable: true },
{ field: "active", header: "Publication", sortable: true },
{ field: "publicationDate", header: "Date de publication", sortable: true },
{ field: "actions", header: "" },
]}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
totalRecords={data?.total || 0}
onPageChange={handlePageChange}
selectedItems={selectedNews}
onSelectionChange={setSelectedNews}
primaryAction={{
id: "add-news",
icon: "add",
label: "Ajouter une actualité",
onClick: () => navigate(`/modules/${moduleSlug}/créer`),
variant: "primary",
}}
deleteAction={{
icon: "delete",
label: "Supprimer",
onClick: handleDelete,
variant: "danger",
showCount: true,
}}
publishAction={{
icon: "publish",
label: "Publier/Masquer",
onClick: handlePublish,
variant: "danger",
}}
reorderAction={{
onReorder: handleReorder,
onSortOrderChange: setSortOrder,
defaultSortOrder: ["createdAt:asc"],
}}
searchAction={{
onSearchSubmit: handleSearchSubmit,
}}
onCancel={handleCancel}
labels={{
emptyMessage: "Aucune actualité trouvée",
}}
/>
Use Cases
1. Simple Product List (with search and delete only)
<TableManager
data={products}
columns={[
{ field: "name", header: "Product Name", sortable: true },
{ field: "price", header: "Price", sortable: true },
{ field: "stock", header: "Stock", sortable: true },
]}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
totalRecords={totalRecords}
onPageChange={handlePageChange}
selectedItems={selectedProducts}
onSelectionChange={setSelectedProducts}
primaryAction={{
id: "add-product",
icon: "add",
label: "Add Product",
onClick: () => navigate("/products/new"),
}}
deleteAction={{
icon: "delete",
label: "Delete",
onClick: handledeleteMany,
showCount: true,
}}
searchAction={{
onSearchSubmit: handleSearch,
}}
features={{
enableReorder: false,
enablePublish: false,
}}
labels={{
searchPlaceholder: "Search products...",
emptyMessage: "No products found",
}}
/>
2. User Management (with custom actions)
<TableManager
data={users}
columns={[
{ field: "name", header: "Name", sortable: true },
{ field: "email", header: "Email", sortable: true },
{ field: "role", header: "Role", sortable: true },
{ field: "status", header: "Status", sortable: true },
]}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
totalRecords={totalRecords}
onPageChange={handlePageChange}
selectedItems={selectedUsers}
onSelectionChange={setSelectedUsers}
primaryAction={{
id: "invite-user",
icon: "person_add",
label: "Invite User",
onClick: handleInviteUser,
}}
customActions={[
{
id: "export",
icon: "download",
label: "Export",
onClick: handleExport,
},
{
id: "import",
icon: "upload",
label: "Import",
onClick: handleImport,
},
]}
deleteAction={{
icon: "delete",
label: "Delete Users",
onClick: handledeleteMany,
showCount: true,
}}
searchAction={{
onSearchSubmit: handleSearch,
}}
features={{
enableReorder: false,
enablePublish: false,
}}
/>
3. Blog Posts (with reordering and publish)
<TableManager
data={posts}
columns={[
{ field: "title", header: "Title", sortable: true },
{ field: "author", header: "Author", sortable: true },
{ field: "status", header: "Status", sortable: true },
{ field: "date", header: "Date", sortable: true },
]}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
totalRecords={totalRecords}
onPageChange={handlePageChange}
selectedItems={selectedPosts}
onSelectionChange={setSelectedPosts}
primaryAction={{
id: "new-post",
icon: "add",
label: "New Post",
onClick: () => navigate("/posts/new"),
}}
deleteAction={{
icon: "delete",
label: "Delete",
onClick: handleDelete,
showCount: true,
}}
publishAction={{
icon: "publish",
label: "Publish/Unpublish",
onClick: handleTogglePublish,
}}
reorderAction={{
onReorder: handleReorder,
onSortOrderChange: setSortOrder,
defaultSortOrder: ["order:asc"],
}}
searchAction={{
onSearchSubmit: handleSearch,
}}
/>
4. FAQ Management (with custom header/footer content)
<TableManager
data={faqs}
columns={[
{ field: "question", header: "Question", sortable: true },
{ field: "category", header: "Category", sortable: true },
{ field: "active", header: "Active", sortable: true },
]}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
totalRecords={totalRecords}
onPageChange={handlePageChange}
primaryAction={{
id: "add-faq",
icon: "add",
label: "Add FAQ",
onClick: () => navigate("/faqs/new"),
}}
reorderAction={{
onReorder: handleReorder,
onSortOrderChange: setSortOrder,
}}
headerContent={
<div className="flex gap-2">
<select onChange={handleCategoryFilter}>
<option value="">All Categories</option>
<option value="general">General</option>
<option value="technical">Technical</option>
</select>
</div>
}
footerContent={
<div className="text-sm text-gray-600">
Total FAQs: {totalRecords}
</div>
}
/>
Props Reference
Required Props
| Prop | Type | Description |
|---|---|---|
data | T[] | Array of data items to display |
columns | ColumnConfig[] | Column definitions |
currentPage | number | Current page (1-indexed) |
itemsPerPage | number | Items per page |
totalRecords | number | Total number of records |
onPageChange | function | Page change handler |
Optional Props
| Prop | Type | Default | Description |
|---|---|---|---|
selectedItems | Record<string, boolean> | {} | Selected items |
onSelectionChange | function | - | Selection change handler |
primaryAction | ActionButton | - | Primary action button config |
customActions | ActionButton[] | [] | Custom action buttons |
deleteAction | ModeAction | - | Delete action config |
publishAction | ModeAction | - | Publish action config |
reorderAction | ReorderConfig | - | Reorder action config |
searchAction | SearchConfig | - | Search action config |
onCancel | function | - | Cancel handler |
features | FeatureFlags | All enabled | Feature toggles |
labels | Labels | French defaults | Custom labels |
stripedRows | boolean | true | Striped row styling |
rowsPerPageOptions | number[] | [10,20,50,100] | Rows per page options |
headerContent | ReactNode | - | Custom header content |
footerContent | ReactNode | - | Custom footer content |
Internationalization
<TableManager
// ... other props
labels={{
searchPlaceholder: "Search...",
searchLabel: "Search",
reorderLabel: "Reorder",
publishLabel: "Publish/Unpublish",
deleteLabel: "Delete",
cancelLabel: "Cancel",
emptyMessage: "No data found",
}}
/>
Feature Flags
<TableManager
// ... other props
features={{
enableSearch: true, // Show search button
enableReorder: true, // Show reorder button
enablePublish: true, // Show publish button
enableDelete: true, // Show delete button
enableCustomActions: true, // Show custom actions
}}
/>
Best Practices
- Always provide meaningful labels for better UX
- Use feature flags to disable unused functionality
- Keep data transformation in parent component
- Handle errors in action callbacks
- Provide confirmation dialogs for destructive actions
- Use TypeScript generics for type safety:
<TableManager<MyDataType>>