Component Reference
Lofty Golden Oil ERP — Reusable UI Component API Reference
Table of Contents
- DataTable
- SearchBar
- Pagination
- AlertBadge
- BulkActions
- UndoButton
- ConfirmDialog
- PasswordInput
- Toast System
1. DataTable
File: client/src/components/DataTable.jsx
Universal data table with sorting, searching, pagination, row selection, and CSV export. The primary component used across all list views in the application.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
data | Array<Object> | [] | Array of row objects to display |
columns | Array<ColumnConfig> | [] | Column definitions (see below) |
searchable | boolean | false | Enable client-side search input |
searchPlaceholder | string | "Search..." | Placeholder text for search input |
selectable | boolean | false | Enable row selection via checkboxes |
onSelectionChange | Function(selectedRows) | undefined | Callback when selection changes |
onBulkDelete | Function(selectedRows) | undefined | Callback for bulk delete action |
actions | Function(row) | undefined | Render function for per-row action buttons |
onAdd | Function | undefined | Callback when "Add" button is clicked |
addLabel | string | "Add New" | Label for the add button |
exportable | boolean | false | Show CSV export button |
onExport | Function(data) | undefined | Custom export handler (defaults to CSV download) |
pagination | PaginationConfig | undefined | Pagination configuration object |
loading | boolean | false | Show loading state |
emptyMessage | string | "No data found" | Message when data array is empty |
PaginationConfig
| Property | Type | Description |
|---|---|---|
enabled | boolean | Enable pagination controls |
pageSize | number | Items per page (default: 20) |
currentPage | number | Current page number (1-indexed) |
totalItems | number | Total items across all pages |
onPageChange | Function(page) | Callback when page changes |
onPageSizeChange | Function(size) | Callback when page size changes |
ColumnConfig
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Object key to display from data row |
label | string | Yes | Column header text |
sortable | boolean | No | Enable sort on this column (default: false) |
render | Function(value, row) | No | Custom render function for cell content |
Features
- Column Sorting: Click any sortable column header to toggle ascending/descending sort. A second click reverses order; a third click removes sort.
- Client-Side Search: Filters all visible columns. Debounced at 300ms for performance. Search is case-insensitive.
- Pagination: When
paginationprop is provided, renders page navigation controls at the bottom. - Row Selection: When
selectableis true, renders checkboxes in the first column. Header checkbox selects/deselects all visible rows. - CSV Export: When
exportableis true, renders an export button. Downloads filtered data as a CSV file. - Bulk Actions Bar: Appears above the table when rows are selected. Shows selected count and bulk action buttons.
- Empty State: Displays
emptyMessagewith a muted icon when data is empty. - Custom Render: Use the
renderfunction in column config to format cell content (currency, dates, badges, etc.).
Usage Examples
Basic table:
<DataTable
data={products}
columns={[
{ key: 'id', label: 'ID', sortable: true },
{ key: 'name', label: 'Product Name', sortable: true },
{ key: 'stock', label: 'Stock', sortable: true },
{ key: 'price', label: 'Price', sortable: true, render: (v) => `₦${v.toLocaleString()}` },
]}
searchable
searchPlaceholder="Search products..."
/>
With pagination, selection, and export:
const [selected, setSelected] = useState([]);
const [page, setPage] = useState(1);
<DataTable
data={products}
columns={columns}
searchable
selectable
onSelectionChange={setSelected}
onBulkDelete={handleBulkDelete}
exportable
pagination={{
enabled: true,
pageSize: 20,
currentPage: page,
totalItems: totalCount,
onPageChange: setPage,
}}
/>
With row actions:
<DataTable
data={products}
columns={columns}
actions={(row) => (
<button onClick={() => handleEdit(row)} className="text-primary hover:underline">
Edit
</button>
)}
onAdd={() => setShowModal(true)}
addLabel="Add Product"
/>
Accessibility
- Column headers use
<th>withscope="col" - Sortable headers include
aria-sortattribute (ascending,descending, ornone) - Checkboxes include
aria-labelfor row identification - Empty state uses
role="status"witharia-live="polite" - Table is wrapped in a scrollable container for keyboard navigation
2. SearchBar
File: client/src/components/SearchBar.jsx
Global search input with keyboard shortcut support. Used in the application header for cross-module search.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | "" | Controlled input value |
onChange | Function(event) | undefined | Callback on input change |
placeholder | string | "Search..." | Input placeholder text |
onKeyDown | Function(event) | undefined | Additional keydown handler |
Features
- Keyboard Shortcut: Press
Cmd+K(macOS) orCtrl+K(Windows/Linux) to focus the search input from anywhere in the application. - Escape to Blur: Press
Escapewhile focused to blur the input. - Search Icon: Renders a magnifying glass icon inside the input field.
- Keyboard Shortcut Badge: Displays the
⌘KorCtrl+Khint inside the input when not focused. - Responsive: Adapts width for mobile and desktop layouts.
Usage Example
const [searchQuery, setSearchQuery] = useState('');
<SearchBar
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search products, customers, orders..."
/>
Accessibility
- Input uses
type="search"for native clear button aria-label="Global search"identifies the input purpose- Keyboard shortcut is not required — the input is fully mouse/touch accessible
- Focus ring is visible on keyboard focus
3. Pagination
File: client/src/components/Pagination.jsx
Page navigation controls for DataTable and standalone use.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
currentPage | number | 1 | Active page number (1-indexed) |
totalPages | number | 1 | Total number of pages |
onPageChange | Function(page) | undefined | Callback when page changes |
totalItems | number | 0 | Total items across all pages |
pageSize | number | 20 | Items per page |
onPageSizeChange | Function(size) | undefined | Callback when page size changes |
Features
- Prev/Next Buttons: Navigate to previous or next page. Disabled at boundaries.
- Page Numbers: Shows page numbers with ellipsis for large page counts. Current page is highlighted.
- Items Per Page Dropdown: Select from predefined page sizes (10, 20, 50, 100).
- Range Display: Shows "Showing X–Y of Z items" text.
- Keyboard Navigation: Arrow keys navigate pages when pagination controls are focused.
Usage Example
<Pagination
currentPage={page}
totalPages={Math.ceil(totalItems / pageSize)}
onPageChange={setPage}
totalItems={totalItems}
pageSize={pageSize}
onPageSizeChange={setPageSize}
/>
Accessibility
- Navigation uses
<nav aria-label="Pagination"> - Current page button has
aria-current="page" - Prev/Next buttons include
aria-label="Previous page"andaria-label="Next page" - Page size dropdown is a native
<select>for maximum accessibility
4. AlertBadge
File: client/src/components/AlertBadge.jsx
Status indicator badge with color-coded types. Used in inventory dashboards, production status displays, and requirement approval workflows.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | string | "info" | Badge type (see types below) |
count | number | undefined | Optional numeric count to display |
label | string | undefined | Optional label text |
Types
| Type | Color | Use Case |
|---|---|---|
expired | Red (bg-red-100 text-red-800) | Expired products, overdue items |
low | Yellow (bg-yellow-100 text-yellow-800) | Low stock warnings |
expiringSoon | Orange (bg-orange-100 text-orange-800) | Products expiring within threshold |
active | Green (bg-green-100 text-green-800) | Active records, completed status |
inactive | Gray (bg-gray-100 text-gray-800) | Inactive records, disabled items |
info | Blue (bg-blue-100 text-blue-800) | Informational badges |
Features
- Color Coding: Automatically applies the correct background and text color based on
type. - Count Display: When
countis provided, shows a numeric badge alongside the label (e.g., "Low Stock (5)"). - Dark Mode: Colors adapt to the current theme with appropriate dark mode variants.
- Compact Design: Uses
rounded-fullpill shape for minimal visual footprint.
Usage Example
<AlertBadge type="low" count={5} label="Low Stock" />
<AlertBadge type="expired" count={2} label="Expired" />
<AlertBadge type="active" label="Active" />
<AlertBadge type="info" label="New" />
Accessibility
- Badge uses
role="status"to announce status to screen readers - Color is supplemented by text content (not color alone)
- Count is included in the accessible label
5. BulkActions
File: client/src/components/BulkActions.jsx
Actions bar that appears when rows are selected in a DataTable. Provides bulk operations on selected items.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
selectedCount | number | 0 | Number of currently selected rows |
onDelete | Function | undefined | Callback for bulk delete action |
customActions | Array<{ label, onClick, icon? }> | [] | Additional action buttons |
Features
- Selection Count: Displays "X items selected" text.
- Delete Button: Red-styled button that triggers the
onDeletecallback. Typically opens a ConfirmDialog. - Custom Actions: Render additional buttons from the
customActionsarray. Each action can have a label, onClick handler, and optional icon. - Animated Appearance: Slides in from above when selection occurs, slides out when cleared.
- Clear Selection: Includes a button to deselect all rows.
Usage Example
<BulkActions
selectedCount={selectedItems.length}
onDelete={() => setShowConfirmDialog(true)}
customActions={[
{ label: 'Export Selected', onClick: handleExportSelected },
{ label: 'Mark Active', onClick: handleMarkActive },
]}
/>
Accessibility
- Bar uses
role="toolbar"witharia-label="Bulk actions" - Delete button includes confirmation pattern
- Keyboard navigation follows toolbar conventions (Tab between buttons)
6. UndoButton
File: client/src/components/UndoButton.jsx
Provides an undo mechanism with an auto-dismiss countdown timer. Used after destructive actions (delete, bulk operations).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
onUndo | Function | undefined | Callback to execute when undo is triggered |
duration | number | 5000 | Time in milliseconds before auto-dismiss |
Features
- Auto-Dismiss Countdown: Shows a progress indicator that depletes over
durationmilliseconds. - Click to Undo: Clicking the button calls
onUndoand dismisses immediately. - Visual Timer: Displays remaining time or a progress bar.
- Single Use: After clicking or expiring, the button disappears.
Usage Example
const handleDelete = async (id) => {
// Store the item for potential undo
const deletedItem = await api.delete(`/items/${id}`);
setItems(prev => prev.filter(item => item.id !== id));
// Show undo button
setUndoAction(() => async () => {
await api.post('/items/restore', { id });
fetchItems();
toast.success('Item restored');
});
};
// In the JSX:
{undoAction && (
<UndoButton
onUndo={() => { undoAction(); setUndoAction(null); }}
duration={5000}
/>
)}
Accessibility
- Button uses
role="alert"to announce its presence aria-live="polite"announces countdown updates- Keyboard accessible — can be activated with Enter or Space
7. ConfirmDialog
File: client/src/components/ConfirmDialog.jsx
Modal confirmation dialog for destructive or important actions. Supports danger, warning, and info variants.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | false | Controls modal visibility |
onClose | Function | undefined | Callback when modal is dismissed |
onConfirm | Function | undefined | Callback when confirm button is clicked |
title | string | "Confirm Action" | Modal heading |
message | string | "" | Modal body text |
confirmText | string | "Confirm" | Confirm button label |
type | string | "danger" | Modal variant: danger, warning, or info |
Features
- Overlay: Semi-transparent backdrop that blocks interaction with the page behind.
- Type Variants:
danger— Red confirm button, used for delete/destructive actionswarning— Yellow/amber confirm button, used for caution-required actionsinfo— Blue confirm button, used for informational confirmations
- Keyboard Escape: Pressing
Escapecloses the modal. - Focus Trap: Tab cycles through the confirm and cancel buttons only.
- Click Outside: Clicking the overlay backdrop closes the modal.
- Animation: Fade-in/scale animation on open, reverse on close.
Usage Example
const [showConfirm, setShowConfirm] = useState(false);
<ConfirmDialog
isOpen={showConfirm}
onClose={() => setShowConfirm(false)}
onConfirm={handleDelete}
title="Delete Product"
message="Are you sure you want to delete this product? This action cannot be undone."
confirmText="Delete"
type="danger"
/>
// Trigger:
<button onClick={() => setShowConfirm(true)} className="text-red-600">
Delete
</button>
Accessibility
- Modal uses
role="dialog"witharia-modal="true" aria-labelledbypoints to the title element- Focus is trapped within the dialog
- Focus returns to the trigger element on close
Escapekey closes the dialog
8. PasswordInput
File: client/src/components/PasswordInput.jsx
Password input field with visibility toggle. Shows/hides the password text with an eye icon.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | "" | Controlled input value |
onChange | Function(event) | undefined | Callback on input change |
placeholder | string | "Enter password" | Input placeholder text |
name | string | "password" | Input name attribute |
required | boolean | false | Whether the field is required |
minLength | number | 0 | Minimum password length for validation |
Features
- Visibility Toggle: Eye icon button toggles between
type="password"andtype="text". - Eye Icons: Uses
EyeIcon(hidden) andEyeOffIcon(visible) from the icon set. - Validation: Displays an error message when
minLengthis not met and the field is touched. - Responsive: Full-width by default, adapts to container.
Usage Example
const [password, setPassword] = useState('');
<PasswordInput
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
name="password"
required
minLength={8}
/>
Accessibility
- Toggle button has
aria-label="Show password"/aria-label="Hide password" - Input uses
type="password"/type="text"(native browser support) - Error messages use
aria-describedbyto link to the input - Required field indicated by
aria-required="true"
9. Toast System
Location: Integrated throughout the application (typically via react-hot-toast or custom implementation)
Notification system for success, error, warning, and info messages. Toasts appear automatically and dismiss after a timeout.
Types
| Type | Color | Use Case |
|---|---|---|
success | Green | Successful operations (save, update, delete) |
error | Red | Failed operations, validation errors, network errors |
warning | Yellow | Low stock alerts, approaching limits |
info | Blue | Informational messages, status updates |
Features
- Auto-Dismiss: Toasts disappear after 3 seconds (configurable).
- Stacking: Multiple toasts stack vertically in the top-right corner.
- Positioning: Fixed position in the top-right of the viewport.
- Manual Dismiss: Click the X button to dismiss early.
- Queue Management: Maximum 5 visible toasts at once; older ones are dismissed.
Usage Examples
import { toast } from 'react-hot-toast'; // or project's toast utility
// Success
toast.success('Product saved successfully');
toast.success('Sale created — Invoice #1234');
// Error
toast.error('Failed to save product');
toast.error('Network error — please try again');
// Warning
toast.warning('Stock is running low for Palm Oil 5L');
toast.warning('Credit limit exceeded for this customer');
// Info
toast.info('Report generation started');
toast.info('Session expires in 5 minutes');
Custom Duration
toast.success('Saved', { duration: 5000 }); // 5 seconds
toast.error('Error', { duration: 10000 }); // 10 seconds
Accessibility
- Toast container uses
role="status"witharia-live="polite" - Error toasts use
aria-live="assertive"for immediate announcement - Toast content is fully readable by screen readers
- Dismiss button is keyboard accessible