react-admin-crud-manager 1.1.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1206 -210
- package/dist/index.cjs.js +20 -55
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +4092 -2529
- package/dist/index.es.js.map +1 -1
- package/dist/types/components/Details/Details.d.ts +11 -0
- package/dist/types/components/Details/components/CardGroup.d.ts +9 -1
- package/dist/types/components/Details/components/DetailRow.d.ts +9 -1
- package/dist/types/components/Details/components/GroupRow.d.ts +9 -1
- package/dist/types/components/Filter/FilterDrawer.d.ts +2 -2
- package/dist/types/components/Form/components/AudioPicker.d.ts +2 -1
- package/dist/types/components/Form/components/Checkbox.d.ts +2 -1
- package/dist/types/components/Form/components/ImagePicker.d.ts +2 -1
- package/dist/types/components/Form/components/Input.d.ts +1 -0
- package/dist/types/components/Form/components/MultiImagePicker.d.ts +21 -0
- package/dist/types/components/Form/components/PhoneInput.d.ts +2 -1
- package/dist/types/components/Form/components/Radio.d.ts +2 -1
- package/dist/types/components/Form/components/RenderFields.d.ts +3 -2
- package/dist/types/components/Form/components/Select.d.ts +5 -2
- package/dist/types/components/Form/components/Switch.d.ts +1 -0
- package/dist/types/components/Form/components/TextArea.d.ts +2 -0
- package/dist/types/components/Form/components/TinyEditor.d.ts +3 -1
- package/dist/types/components/Form/components/VideoPicker.d.ts +2 -1
- package/dist/types/components/Modal/Modal.d.ts +10 -1
- package/dist/types/lib/crudClasses.d.ts +94 -0
- package/dist/types/types/crudtypes.d.ts +51 -0
- package/package.json +10 -8
- package/dist/tailwind.css +0 -2364
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
A reusable React CRUD admin template with modular components for rapid admin dashboard development.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
|
+
|
|
6
7
|
- Plug-and-play CRUD page component
|
|
7
8
|
- Modular, customizable UI components (Table, Modal, Form, etc.)
|
|
8
9
|
- Built with React 18+ and TypeScript
|
|
@@ -11,7 +12,6 @@ A reusable React CRUD admin template with modular components for rapid admin das
|
|
|
11
12
|
- Sorting, filtering, searching, and pagination support
|
|
12
13
|
- Rich form fields including text, select, image upload, rich text editor, and more
|
|
13
14
|
|
|
14
|
-
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```sh
|
|
@@ -20,15 +20,7 @@ npm install react-admin-crud-manager
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
-
### 1.
|
|
24
|
-
|
|
25
|
-
In your main entry file (e.g., `src/main.jsx` or `src/index.js`):
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
import 'react-admin-crud-manager/dist/tailwind.css';
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### 2. Use the component
|
|
23
|
+
### 1. Use the component
|
|
32
24
|
|
|
33
25
|
```
|
|
34
26
|
import Crud from 'react-admin-crud-manager';
|
|
@@ -44,31 +36,34 @@ function App() {
|
|
|
44
36
|
```
|
|
45
37
|
|
|
46
38
|
## Components
|
|
39
|
+
|
|
47
40
|
- `Crud`: Main CRUD page component
|
|
48
|
-
- `Table`, `Modal`, `Form`, etc.: Available for advanced use
|
|
49
41
|
|
|
50
42
|
## Props
|
|
43
|
+
|
|
51
44
|
Below is a complete reference of the public props accepted by this package (types, accepted values and defaults). The library exposes a single primary component (`Crud`) that receives a single `config` prop — most configuration lives inside that object.
|
|
52
45
|
|
|
53
46
|
---
|
|
54
47
|
|
|
55
48
|
### Crud (default export)
|
|
49
|
+
|
|
56
50
|
`<Crud config={config} />`
|
|
51
|
+
|
|
57
52
|
- `config` (object) — required. Top-level configuration object used by the CRUD page.
|
|
58
53
|
|
|
59
54
|
#### Key properties of `config`
|
|
60
55
|
|
|
61
|
-
| Property
|
|
62
|
-
|
|
63
|
-
| `title`
|
|
64
|
-
| `description`
|
|
65
|
-
| `buttonText`
|
|
66
|
-
| `fetchData`
|
|
67
|
-
| `fetchRowDetails` | function | No
|
|
68
|
-
| `isStaticData`
|
|
69
|
-
| `tableConfig`
|
|
70
|
-
| `modalConfig`
|
|
71
|
-
| `filterConfig`
|
|
56
|
+
| Property | Type | Required | Description |
|
|
57
|
+
| ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `title` | string | Yes | Title of the CRUD page |
|
|
59
|
+
| `description` | string | No | Optional description text |
|
|
60
|
+
| `buttonText` | string | No | Custom text for add button |
|
|
61
|
+
| `fetchData` | function | Yes | Async function to fetch data. Signature: `async ({ search, rows_per_page, current_page, sort_by, sort_order, ...filters }) => Promise<{ data: Array, pagination?: { current_page: number, rows_per_page: number, total_pages: number, total_records: number } }>`. Component expects `resp.data` (array) and optional `resp.pagination` for server-side pagination. |
|
|
62
|
+
| `fetchRowDetails` | function | No | Async function to fetch additional details for a row. Signature: `async (item) => Promise<any>`. Used for view modal or details. |
|
|
63
|
+
| `isStaticData` | boolean | No | Default: `false`. If true, add/edit/delete apply client-side instead of re-fetching |
|
|
64
|
+
| `tableConfig` | object | Yes | Table configuration — [see tableConfig](#tableconfig-configuration) |
|
|
65
|
+
| `modalConfig` | object | No | Modal definitions — [see modalConfig](#modalconfig-definitions) |
|
|
66
|
+
| `filterConfig` | object | No | Filter drawer configuration — [see Form Field Schema](#form-field-schema) |
|
|
72
67
|
|
|
73
68
|
---
|
|
74
69
|
|
|
@@ -76,39 +71,36 @@ Below is a complete reference of the public props accepted by this package (type
|
|
|
76
71
|
|
|
77
72
|
#### Table Configuration Keys
|
|
78
73
|
|
|
79
|
-
| Key
|
|
80
|
-
|
|
81
|
-
| `table_head` | array of column objects |
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
| `setServerSidePaginationData` | function | No | Used to update server-side pagination/search state |
|
|
91
|
-
| `onFilterApply` | function | No | Callback: `(filters: object)` |
|
|
92
|
-
| `filterConfig` | object | No | Fields array rendered in the FilterDrawer |
|
|
74
|
+
| Key | Type | Description | Accepted Values / Example |
|
|
75
|
+
| ------------ | ----------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `table_head` | array of column objects | Column definitions | Array of table column objects (see [Table column object](#table-column-object-table_head)) |
|
|
77
|
+
| `search` | object | Search functionality config | `{ enabled: true, useServerSideSearch?: false, searchKeys?: ["name", "email"] }` |
|
|
78
|
+
| `filter` | object | Filter drawer config | `{ enabled: true, useServerSideFilters?: false }` |
|
|
79
|
+
| `pagination` | object | Pagination controls | `{ enabled: true, useServerSidePagination?: false }` |
|
|
80
|
+
| `sort` | object | Sorting configuration | `{ enabled: true, useServerSideSorting?: false, autoGenerate: true, onChange?: (payload) => void, options?: [...], clearLabel?: "Clear Sort" }` |
|
|
81
|
+
| `exportCSV` | object | Export data as CSV | `{ enabled: true, fileName: "users.csv", fields: [{ label: "Name", key: "name" }, ...] }` |
|
|
82
|
+
| `rowClick` | function or boolean | Callback on table row click | `(row: object, rowIndex: number) => void` or `true` (setting true will open details) |
|
|
83
|
+
|
|
84
|
+
<br>
|
|
93
85
|
|
|
94
86
|
#### Table Column Object (`table_head[]`)
|
|
95
87
|
|
|
96
|
-
| Key
|
|
97
|
-
|
|
98
|
-
| `key`
|
|
99
|
-
| `title`
|
|
100
|
-
| `type`
|
|
101
|
-
| `imageKey`
|
|
102
|
-
| `titleKey`
|
|
103
|
-
| `subtitleKey`
|
|
104
|
-
| `onClickDetails` | boolean
|
|
105
|
-
| `variant`
|
|
106
|
-
| `chipOptions`
|
|
107
|
-
| `defaultColor`
|
|
108
|
-
| `className`
|
|
109
|
-
| `format`
|
|
110
|
-
| `menuList`
|
|
111
|
-
| `render`
|
|
88
|
+
| Key | Type | Description | Accepted Values / Example |
|
|
89
|
+
| ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
90
|
+
| `key` | string | Property name in row objects | `"id"`, `"name"`, `"email"` (must exist in data objects) |
|
|
91
|
+
| `title` | string | Column header text | `"User ID"`, `"Full Name"`, `"Email Address"` |
|
|
92
|
+
| `type` | string | Column renderer type | `"plain"` (default), `"index"` (row number), `"group"` (avatar+text), `"chip"` (badge), `"date"`, `"avatar"`, `"menu_actions"` |
|
|
93
|
+
| `imageKey` | string | Image property for avatar/group types | `"profileImage"`, `"avatarUrl"` (path to image in data object) |
|
|
94
|
+
| `titleKey` | string | Title property for group/avatar types | `"name"`, `"fullName"` (property key in data object) |
|
|
95
|
+
| `subtitleKey` | string | Subtitle property for group/avatar types | `"email"`, `"department"` (property key in data object) |
|
|
96
|
+
| `onClickDetails` | boolean | Clicking cell opens view details modal | `true`, `false` (default: `false`) |
|
|
97
|
+
| `variant` | string | Chip styling variant | `"contained"`, `"outline"`, `"soft"` (used with `type: "chip"`) |
|
|
98
|
+
| `chipOptions` | array | Map values to chip labels and colors; array of `{ value: string\|number\|boolean, label: string, color?: string }` | `[{ value: "active", label: "Active", color: "green" }, { value: "inactive", label: "Inactive", color: "red" }]` |
|
|
99
|
+
| `defaultColor` | string | Default color for chips (if no match in chipOptions) | `"green"`, `"red"`, `"blue"`, `"yellow"`, `"purple"`, `"gray"`, etc. |
|
|
100
|
+
| `className` | string | Custom CSS class for cell content | Tailwind classes: `"font-bold text-sm text-gray-600"` |
|
|
101
|
+
| `format` | string | Date format pattern | `"DD MMM YYYY"`, `"YYYY-MM-DD"`, `"DD/MM/YYYY HH:mm"` (uses date-fns patterns) |
|
|
102
|
+
| `menuList` | array | Action menu items; array of `{ title: string, type: string, variant?: string, icon?: ReactNode }` | `[{ title: "Edit", type: "edit", icon: <EditIcon /> }, { title: "Delete", type: "delete", icon: <TrashIcon /> }]` |
|
|
103
|
+
| `render` | function | Custom cell renderer (overrides built-in logic) | `(row: object, rowIndex: number) => ReactNode`; e.g., `(row) => <span>{row.name.toUpperCase()}</span>` |
|
|
112
104
|
|
|
113
105
|
---
|
|
114
106
|
|
|
@@ -116,224 +108,1228 @@ Below is a complete reference of the public props accepted by this package (type
|
|
|
116
108
|
|
|
117
109
|
#### Add & Edit Modal (`addModal`, `editModal`)
|
|
118
110
|
|
|
119
|
-
| Property
|
|
120
|
-
|
|
121
|
-
| `title`
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
111
|
+
| Property | Type | Required | Description | Accepted Values / Example |
|
|
112
|
+
| --------------- | --------- | -------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| `title` | string | Yes | Modal title | `"Add New User"`, `"Edit User Profile"` |
|
|
114
|
+
| `icon` | ReactNode | No | Icon element displayed in modal header | `<PlusIcon />`, `<EditIcon />` |
|
|
115
|
+
| `size` | string | No | Modal width | `"sm"`, `"md"` (default), `"lg"`, `"xl"`, `"full"` |
|
|
116
|
+
| `formClass` | string | No | Custom CSS class for form wrapper | Tailwind classes: `"grid grid-cols-12 gap-4"` |
|
|
117
|
+
| `formFields` | array | Yes | Form field objects | Array of form field objects (see [Form Field Schema](#form-field-schema)) |
|
|
118
|
+
| `handleSubmit` | function | Yes | Async callback on form submission | `async (formData) => Promise<{ newObject, message?: string }>` (add), `async (formData, item) => Promise<{ newObject, targetObject, message?: string }>` (edit) |
|
|
119
|
+
| `actionButtons` | array | No | Custom action buttons | `[{ type: "submit", label: "Save", color: "primary", variant: "contained", onClick?: (e, item) => void, disabled?: boolean }]` |
|
|
127
120
|
|
|
128
121
|
#### Delete Modal (`deleteModal`)
|
|
129
122
|
|
|
130
|
-
| Property
|
|
131
|
-
|
|
132
|
-
| `title`
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `actionButtons` | array
|
|
123
|
+
| Property | Type | Required | Description | Accepted Values / Example |
|
|
124
|
+
| --------------- | --------- | -------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
125
|
+
| `title` | string | No | Modal title | `"Delete User"`, `"Confirm Delete"` |
|
|
126
|
+
| `icon` | ReactNode | No | Icon element displayed in modal header | `<TrashIcon />`, `<WarningIcon />` |
|
|
127
|
+
| `size` | string | No | Modal width | `"sm"` (default), `"md"`, `"lg"`, `"xl"`, `"full"` |
|
|
128
|
+
| `confirmText` | string | No | Confirmation message text | `"Are you sure you want to delete this user?"`, `"This action cannot be undone."` |
|
|
129
|
+
| `referenceKey` | string | No | Property key to display as confirmation reference | `"name"`, `"email"` (shows the value from selected item) |
|
|
130
|
+
| `actionButtons` | array | No | Custom action buttons | `[{ type: "button", label: "Delete", color: "error", variant: "contained", onClick: async (event , selectedItem) => Promise<{ targetObject }>},... ]` |
|
|
138
131
|
|
|
139
132
|
#### View Modal (`viewModal`)
|
|
140
133
|
|
|
141
|
-
| Property
|
|
142
|
-
|
|
143
|
-
| `title`
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
134
|
+
| Property | Type | Required | Description | Accepted Values / Example |
|
|
135
|
+
| ----------------- | --------------- | -------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `title` | string | Yes | Modal title | `"User Details"`, `"View Profile"` |
|
|
137
|
+
| `icon` | ReactNode | No | Icon element displayed in modal header | `<EyeIcon />`, `<InfoIcon />` |
|
|
138
|
+
| `size` | string | No | Modal width | `"sm"`, `"md"` (default), `"lg"`, `"xl"`, `"full"` |
|
|
139
|
+
| `component` | React component | No | Custom component to render (receives `data` prop) | `(props) => <CustomViewComponent data={props.data} />` |
|
|
140
|
+
| `variant` | string | No | View layout style | `"default"`, `"card"`, `"split"` |
|
|
141
|
+
| `fields` | array | No | View field objects (if not using custom component) | Array of field objects (see [View Field Schema](#view-field-schema)) |
|
|
142
|
+
| `styles` | object | No | Custom CSS classes for various view elements | `{ containerClass: "...", rowClass: "...", labelClass: "...", valueClass: "...", ... }` |
|
|
143
|
+
| `modalClassNames` | object | No | Custom classes for modal structure | `{ overlay: "...", container: "...", header: "...", body: "...", footer: "...", closeButton: "..." }` |
|
|
144
|
+
| `footer` | object | No | Footer configuration | `{ cancelButton: true, cancelText: "Close" }` |
|
|
148
145
|
|
|
149
146
|
---
|
|
150
147
|
|
|
151
148
|
### Form Field Schema
|
|
152
149
|
|
|
153
|
-
Used by `modalConfig.*.formFields` and `
|
|
154
|
-
|
|
155
|
-
#### Common Field Keys (All Types)
|
|
150
|
+
Used by `modalConfig.*.formFields`, `filterConfig.fields`, and `viewModal.fields`. All form fields follow the `FormField` shape.
|
|
156
151
|
|
|
157
|
-
|
|
158
|
-
|-----|------|----------|-------------|
|
|
159
|
-
| `key` | string | Yes | Property name for form data |
|
|
160
|
-
| `label` | string | No | Human-readable label |
|
|
161
|
-
| `type` | string | Yes | Field type: `text`, `number`, `email`, `password`, `select`, `checkbox`, `radio`, `switch`, `phone`, `textarea`, `image`, `video`, `audio`, `tinyEditor`, `group`, `cardGroup` |
|
|
162
|
-
| `required` | boolean | No | Field is required for form submission |
|
|
163
|
-
| `minLength` | number | No | Minimum character length |
|
|
164
|
-
| `parentClass` | string | No | Tailwind grid class (e.g., `col-span-6`) |
|
|
165
|
-
| `placeholder` | string | No | Placeholder text |
|
|
166
|
-
| `disabled` | boolean | No | Field is disabled |
|
|
152
|
+
#### Common Field Properties (All Types)
|
|
167
153
|
|
|
168
|
-
|
|
154
|
+
| Key | Type | Required | Description | Accepted Values / Example |
|
|
155
|
+
| ------------------ | -------- | -------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
156
|
+
| `key` | string | Yes | Property name/identifier for form data | `"username"`, `"email"`, `"birth_date"` |
|
|
157
|
+
| `label` | string | No | Human-readable label for the field | `"User Name"`, `"Email Address"`, `"Date of Birth"` |
|
|
158
|
+
| `type` | string | Yes | Field type determining the input/renderer | `"text"`, `"number"`, `"email"`, `"password"`, `"select"`, `"checkbox"`, `"radio"`, `"switch"`, `"phone"`, `"textarea"`, `"image"`, `"video"`, `"audio"`, `"tinyEditor"`, `"group"` |
|
|
159
|
+
| `required` | boolean | No | Field must have a value for form submission | `true`, `false` (default: `false`) |
|
|
160
|
+
| `minLength` | number | No | Minimum character length (for text fields) | `5`, `10`, `50` |
|
|
161
|
+
| `placeholder` | string | No | Placeholder text shown in empty field | `"Enter your name"`, `"user@example.com"` |
|
|
162
|
+
| `disabled` | boolean | No | Field is disabled and read-only | `true`, `false` (default: `false`) |
|
|
163
|
+
| `parentClass` | string | No | Custom CSS classes for field wrapper (grid) | Tailwind classes: `"col-span-6"`, `"col-span-12"` |
|
|
164
|
+
| `renderCondition` | function | No | Show field based on form data | `(formData: Record<string, any>) => boolean`; e.g., `(data) => data.userType === 'admin'` |
|
|
165
|
+
| `customValidation` | function | No | Custom validation logic | `(value: any) => boolean \| string`; return `false` for invalid, error message string, or `true` for valid |
|
|
166
|
+
| `className` | string | No | Custom CSS class for input element | Tailwind classes: `"bg-gray-100 rounded-lg"` |
|
|
169
167
|
|
|
170
|
-
|
|
168
|
+
#### Type-Specific Properties
|
|
171
169
|
|
|
172
|
-
|
|
173
|
-
|-----|------|-------------|
|
|
174
|
-
| `options` | array | `{ value: string|number|boolean, label: string }[]` |
|
|
175
|
-
| `multiple` | boolean | Allow multiple selections |
|
|
176
|
-
| `search` | boolean | Show search input inside dropdown |
|
|
177
|
-
| `dropdownMaxHeight` | string | CSS height value (e.g., `300px`) |
|
|
170
|
+
##### `"text"` Field
|
|
178
171
|
|
|
179
|
-
|
|
172
|
+
| Property | Type | Description | Example |
|
|
173
|
+
| ------------------ | ------- | ------------------------- | ---------------------------------------------------------------------------- |
|
|
174
|
+
| `pattern` | string | Regex validation pattern | `"^[a-zA-Z ]*$"` (letters and spaces only) |
|
|
175
|
+
| `mask` | string | Input mask pattern | `"(99) 99999-9999"` (format: 9=digit, A=letter, X=alphanumeric, \*=any char) |
|
|
176
|
+
| `maskApplyOnValue` | boolean | Apply mask formData value | `true`, `false` (default: `true`) |
|
|
180
177
|
|
|
181
|
-
|
|
182
|
-
|-----|------|-------------|
|
|
183
|
-
| `options` | array | `{ value, label }[]` |
|
|
184
|
-
| `multiple` | boolean | Allow selecting multiple values (component prop: `multiSelect`) |
|
|
178
|
+
##### `"number"` Field
|
|
185
179
|
|
|
186
|
-
|
|
180
|
+
| Property | Type | Description | Example |
|
|
181
|
+
| --------------------- | ------- | ---------------------- | ---------------------------------- |
|
|
182
|
+
| `negativeNumberAllow` | boolean | Allow negative numbers | `true`, `false` (default: `false`) |
|
|
187
183
|
|
|
188
|
-
|
|
189
|
-
|-----|------|-------------|
|
|
190
|
-
| `options` | array | Optional radio-like options: `[{ label, value }]` |
|
|
191
|
-
| `text` | string | Description text shown next to switch |
|
|
184
|
+
##### `"select"` Field
|
|
192
185
|
|
|
193
|
-
|
|
186
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
187
|
+
| ------------------- | ------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
188
|
+
| `options` | array | Select options; `{ value, label, color? }[]` | `[{ value: "active", label: "Active", color: "green" }, { value: "inactive", label: "Inactive", color: "red" }]` |
|
|
189
|
+
| `countriesList` | boolean | Country selector dropdown | `true`, `false` (default: `false`) |
|
|
190
|
+
| `multiple` | boolean | Allow selecting multiple options | `true`, `false` (default: `false`) |
|
|
191
|
+
| `search` | boolean | Enable searchable dropdown | `true`, `false` (default: `false`) |
|
|
192
|
+
| `dropdownMaxHeight` | number | Max height of dropdown in pixels | `300`, `400` |
|
|
193
|
+
| `defaultValue` | any | Initial value for the field | `"John"`, `25`, `true`, `["option1", "option2"]` |
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|-----|------|-------------|
|
|
197
|
-
| `countriesList` | boolean | Show country selector |
|
|
198
|
-
| `defaultCountry` | string | ISO country code (e.g., `US`) |
|
|
199
|
-
| `search` | boolean | Enable searching countries |
|
|
200
|
-
| `placeholder` | string | Placeholder text |
|
|
195
|
+
##### `"checkbox"` Field
|
|
201
196
|
|
|
202
|
-
|
|
197
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
198
|
+
| ---------- | ------- | -------------------------------------- | -------------------------------------------------------------------------------- |
|
|
199
|
+
| `options` | array | Checkbox options; `{ value, label }[]` | `[{ value: "read", label: "Can Read" }, { value: "write", label: "Can Write" }]` |
|
|
200
|
+
| `multiple` | boolean | Allow selecting multiple values | `true` (default: `true`); affects how data is stored |
|
|
203
201
|
|
|
204
|
-
|
|
205
|
-
|-----|------|-------------|
|
|
206
|
-
| `rows` | number | Number of visible rows |
|
|
202
|
+
##### `"radio"` Field
|
|
207
203
|
|
|
208
|
-
|
|
204
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
205
|
+
| --------- | ----- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
|
206
|
+
| `options` | array | Radio button options; `{ value, label }[]` | `[{ value: "male", label: "Male" }, { value: "female", label: "Female" }, { value: "other", label: "Other" }]` |
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
|-----|------|-------------|
|
|
212
|
-
| `accept` | string | MIME type filter (default: `image/*`) |
|
|
213
|
-
| `dragDrop` | boolean | Enable drag-and-drop upload |
|
|
208
|
+
##### `"switch"` Field
|
|
214
209
|
|
|
215
|
-
|
|
210
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
211
|
+
| --------- | ------ | -------------------------------------------- | ---------------------------------------------------------------- |
|
|
212
|
+
| `text` | string | Description/label shown next to switch | `"Enable notifications"`, `"Is admin"` |
|
|
213
|
+
| `options` | array | Optional radio-like options (radio fallback) | `[{ value: "yes", label: "Yes" }, { value: "no", label: "No" }]` |
|
|
216
214
|
|
|
217
|
-
|
|
218
|
-
|-----|------|-------------|
|
|
219
|
-
| `editorKey` | string | TinyMCE API key |
|
|
220
|
-
| `fontFamily` | string | Default font family |
|
|
221
|
-
| `height` | number | Editor height in pixels |
|
|
222
|
-
| `imageUploadHandler` | function | `(blobInfo) => Promise<string>`. Returns image URL |
|
|
215
|
+
##### `"phone"` Field
|
|
223
216
|
|
|
224
|
-
|
|
217
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
218
|
+
| ---------------- | ------- | -------------------------------------- | -------------------------------------- |
|
|
219
|
+
| `countriesList` | boolean | Show country selector dropdown | `true`, `false` (default: `false`) |
|
|
220
|
+
| `defaultCountry` | string | Default country ISO code | `"US"`, `"GB"`, `"IN"`, `"CA"`, `"AU"` |
|
|
221
|
+
| `search` | boolean | Enable searching countries in selector | `true`, `false` (default: `false`) |
|
|
222
|
+
| `placeholder` | string | Placeholder text | `"+1 (555) 000-0000"` |
|
|
225
223
|
|
|
226
|
-
|
|
227
|
-
|-----|------|-------------|
|
|
228
|
-
| `options` | array | `{ value, label }[]` |
|
|
224
|
+
##### `"textarea"` Field
|
|
229
225
|
|
|
230
|
-
|
|
226
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
227
|
+
| -------- | ------ | ---------------------- | ------------------------- |
|
|
228
|
+
| `rows` | number | Number of visible rows | `3`, `5`, `10` |
|
|
231
229
|
|
|
232
|
-
|
|
233
|
-
|-----|------|-------------|
|
|
234
|
-
| `accept` | string | MIME type filter (default: `video/*`) |
|
|
235
|
-
| `dragDrop` | boolean | Enable drag-and-drop upload |
|
|
236
|
-
| `maxSize` | number | Maximum file size in bytes |
|
|
230
|
+
##### `"image"` Field
|
|
237
231
|
|
|
238
|
-
|
|
232
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
233
|
+
| ------------- | ------- | -------------------------------- | ----------------------------------------------- |
|
|
234
|
+
| `accept` | string | MIME type filter | `"image/*"` (default), `"image/jpeg,image/png"` |
|
|
235
|
+
| `dragDrop` | boolean | Enable drag-and-drop upload | `true`, `false` (default: `false`) |
|
|
236
|
+
| `cropImage` | boolean | Enable image cropping modal | `true`, `false` (default: `false`) |
|
|
237
|
+
| `aspectRatio` | number | Crop aspect ratio (width:height) | `1` (1:1 square), `16/9`, `4/3`, `1.5` |
|
|
238
|
+
| `multiple` | boolean | Allow selecting images | `true`, `false` (default: `false`) |
|
|
239
|
+
| `maxImages` | number | Limit the multiple select images | `8`, `4` |
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|-----|------|-------------|
|
|
242
|
-
| `renderType` | string | Rendering type for group fields |
|
|
241
|
+
##### `"video"` Field
|
|
243
242
|
|
|
244
|
-
|
|
243
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
244
|
+
| ---------- | ------- | --------------------------- | ----------------------------------------------- |
|
|
245
|
+
| `accept` | string | MIME type filter | `"video/*"` (default), `"video/mp4,video/webm"` |
|
|
246
|
+
| `dragDrop` | boolean | Enable drag-and-drop upload | `true`, `false` (default: `false`) |
|
|
247
|
+
| `maxSize` | number | Maximum file size in bytes | `5242880` (5MB), `10485760` (10MB) |
|
|
245
248
|
|
|
246
|
-
|
|
247
|
-
|-----|------|-------------|
|
|
248
|
-
| `renderType` | string | Rendering type for card group fields |
|
|
249
|
+
##### `"audio"` Field
|
|
249
250
|
|
|
250
|
-
|
|
251
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
252
|
+
| ---------- | ------- | --------------------------- | ----------------------------------------------- |
|
|
253
|
+
| `accept` | string | MIME type filter | `"audio/*"` (default), `"audio/mpeg,audio/wav"` |
|
|
254
|
+
| `dragDrop` | boolean | Enable drag-and-drop upload | `true`, `false` (default: `false`) |
|
|
255
|
+
| `maxSize` | number | Maximum file size in bytes | `5242880` (5MB), `10485760` (10MB) |
|
|
256
|
+
|
|
257
|
+
##### `"tinyEditor"` Field
|
|
251
258
|
|
|
252
|
-
|
|
259
|
+
| Property | Type | Description | Accepted Values / Example |
|
|
260
|
+
| ------------ | ------ | ----------------------------- | ----------------------------------------------------------- |
|
|
261
|
+
| `editorKey` | string | TinyMCE API key (required) | Get from `import.meta.env.VITE_EDITOR_KEY` or pass directly |
|
|
262
|
+
| `fontFamily` | string | Default font family in editor | `"Arial"`, `"Georgia"`, `"Courier New"` |
|
|
263
|
+
| `height` | number | Editor height in pixels | `300`, `500` |
|
|
253
264
|
|
|
254
265
|
---
|
|
255
266
|
|
|
256
|
-
###
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
|
261
|
-
|
|
|
262
|
-
|
|
|
263
|
-
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
|
268
|
-
|
|
269
|
-
| `variant`
|
|
270
|
-
| `
|
|
271
|
-
| `
|
|
272
|
-
| `
|
|
273
|
-
| `
|
|
274
|
-
| `
|
|
275
|
-
| `
|
|
276
|
-
| `disabled` | boolean | `false` | Disable button interaction |
|
|
277
|
-
|
|
278
|
-
#### Chip Props
|
|
279
|
-
|
|
280
|
-
| Prop | Type | Default | Description |
|
|
281
|
-
|------|------|---------|-------------|
|
|
282
|
-
| `label` | string | Required | Badge text |
|
|
283
|
-
| `variant` | string | `contained` | Style variant: `contained`, `outline`, `soft` |
|
|
284
|
-
| `color` | string | `blue` | Color: `blue`, `teal`, `purple`, `yellow`, `green`, `red`, `gray` |
|
|
285
|
-
|
|
286
|
-
#### Modal Props (Direct Usage)
|
|
287
|
-
|
|
288
|
-
| Prop | Type | Description |
|
|
289
|
-
|------|------|-------------|
|
|
290
|
-
| `isOpen` | boolean | Show/hide modal |
|
|
291
|
-
| `onClose` | function | Callback when modal closes |
|
|
292
|
-
| `icon` | React node | Icon element displayed in modal header |
|
|
293
|
-
| `title` | string | Modal title |
|
|
294
|
-
| `size` | string | Size: `sm`, `md`, `lg`, `xl`, `full` |
|
|
295
|
-
| `actionButtons` | array | `[{ type, label, color, variant, onClick, disabled }]` |
|
|
296
|
-
| `loadingBtn` | boolean | Show loading state on action button |
|
|
297
|
-
|
|
298
|
-
#### FilterDrawer Props
|
|
299
|
-
|
|
300
|
-
| Prop | Type | Description |
|
|
301
|
-
|------|------|-------------|
|
|
302
|
-
| `isOpen` | boolean | Show/hide filter drawer |
|
|
303
|
-
| `onClose` | function | Callback when drawer closes |
|
|
304
|
-
| `config` | object | Fields array using `formFieldType` schema |
|
|
305
|
-
| `onApply` | function | Callback: `(filters: object) => void` |
|
|
267
|
+
### View Field Schema
|
|
268
|
+
|
|
269
|
+
Used by `viewModal.fields` to display data in view/details modals.
|
|
270
|
+
|
|
271
|
+
| Key | Type | Description | Accepted Values / Example |
|
|
272
|
+
| ----------------- | --------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
|
273
|
+
| `key` | string | Property name to display | `"username"`, `"email"`, `"birth_date"` |
|
|
274
|
+
| `label` | string | Display label for the field | `"User Name"`, `"Email Address"` |
|
|
275
|
+
| `type` | string | Display type (similar to form field types) | `"text"`, `"date"`, `"chip"`, `"image"`, `"avatar"`, `"group"`, `"cardGroup"` |
|
|
276
|
+
| `format` | string | Date format pattern | `"DD MMM YYYY"`, `"YYYY-MM-DD"`, `"DD/MM/YYYY HH:mm"` |
|
|
277
|
+
| `imageKey` | string | Image property for avatar types | `"profileImage"`, `"avatarUrl"` |
|
|
278
|
+
| `titleKey` | string | Title property for group/avatar types | `"name"`, `"fullName"` |
|
|
279
|
+
| `subtitleKey` | string | Subtitle property for group/avatar types | `"email"`, `"department"` |
|
|
280
|
+
| `variant` | string | Chip display variant | `"contained"`, `"outline"`, `"soft"` |
|
|
281
|
+
| `chipOptions` | array | Map values to chip labels and colors | `[{ value: "active", label: "Active", color: "green" }]` |
|
|
282
|
+
| `defaultColor` | string | Default chip color | `"green"`, `"red"`, `"blue"`, `"yellow"`, `"purple"`, `"gray"` |
|
|
283
|
+
| `className` | string | Custom CSS class for display element | Tailwind classes |
|
|
284
|
+
| `blockClass` | string | Custom CSS class for field block/wrapper | Tailwind classes |
|
|
285
|
+
| `icon` | ReactNode | Icon element to display with field | `<UserIcon />`, `<MailIcon />` |
|
|
286
|
+
| `renderCondition` | function | Show field based on data | `(data: Record<string, any>) => boolean` |
|
|
306
287
|
|
|
307
288
|
---
|
|
308
289
|
|
|
290
|
+
## Advanced Features
|
|
291
|
+
|
|
292
|
+
### 1. Export CSV
|
|
293
|
+
|
|
294
|
+
Export table data as a CSV file with custom field selection:
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
const config = {
|
|
298
|
+
// ... other config
|
|
299
|
+
tableConfig: {
|
|
300
|
+
table_head: [...],
|
|
301
|
+
data: [...]
|
|
302
|
+
exportCSV: {
|
|
303
|
+
enabled: true,
|
|
304
|
+
fileName: "users_export.csv",
|
|
305
|
+
fields: [
|
|
306
|
+
{ label: "ID", key: "id" },
|
|
307
|
+
{ label: "Name", key: "name" },
|
|
308
|
+
{ label: "Email", key: "email" },
|
|
309
|
+
{ label: "Status", key: "status" }
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 2. Conditional Field Rendering
|
|
317
|
+
|
|
318
|
+
Show/hide form fields based on other field values using `renderCondition`:
|
|
319
|
+
|
|
320
|
+
```js
|
|
321
|
+
const formFields = [
|
|
322
|
+
{
|
|
323
|
+
key: "userType",
|
|
324
|
+
label: "User Type",
|
|
325
|
+
type: "select",
|
|
326
|
+
options: [
|
|
327
|
+
{ value: "admin", label: "Administrator" },
|
|
328
|
+
{ value: "user", label: "Regular User" },
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
key: "adminLevel",
|
|
333
|
+
label: "Admin Level",
|
|
334
|
+
type: "select",
|
|
335
|
+
renderCondition: (formData) => formData.userType === "admin", // Only show if userType is admin
|
|
336
|
+
options: [
|
|
337
|
+
{ value: "superadmin", label: "Super Admin" },
|
|
338
|
+
{ value: "moderator", label: "Moderator" },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
key: "department",
|
|
343
|
+
label: "Department",
|
|
344
|
+
type: "text",
|
|
345
|
+
renderCondition: (formData) => formData.userType === "user", // Only show if userType is user
|
|
346
|
+
},
|
|
347
|
+
];
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 3. Custom Field Validation
|
|
351
|
+
|
|
352
|
+
Add custom validation logic to form fields:
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
const formFields = [
|
|
356
|
+
{
|
|
357
|
+
key: "email",
|
|
358
|
+
label: "Email",
|
|
359
|
+
type: "email",
|
|
360
|
+
customValidation: (value) => {
|
|
361
|
+
// Return true/false or custom error message
|
|
362
|
+
if (!value.includes("@company.com")) {
|
|
363
|
+
return "Email must be a company email"; // Error message
|
|
364
|
+
}
|
|
365
|
+
return true; // Valid
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
key: "age",
|
|
370
|
+
label: "Age",
|
|
371
|
+
type: "number",
|
|
372
|
+
customValidation: (value) => {
|
|
373
|
+
if (value < 18) return "Must be 18 or older";
|
|
374
|
+
if (value > 120) return "Please enter a valid age";
|
|
375
|
+
return true;
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
];
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### 4. Custom Table Cell Rendering
|
|
382
|
+
|
|
383
|
+
Use the `render` function for complex cell display:
|
|
384
|
+
|
|
385
|
+
```js
|
|
386
|
+
const tableConfig = {
|
|
387
|
+
table_head: [
|
|
388
|
+
{ key: "id", title: "ID", type: "index" },
|
|
389
|
+
{
|
|
390
|
+
key: "name",
|
|
391
|
+
title: "Name",
|
|
392
|
+
render: (row, rowIndex) => (
|
|
393
|
+
<div className="font-bold text-blue-600">{row.name.toUpperCase()}</div>
|
|
394
|
+
)
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
key: "price",
|
|
398
|
+
title: "Price",
|
|
399
|
+
render: (row) => (
|
|
400
|
+
<span className="text-green-600 font-semibold">
|
|
401
|
+
${row.price.toFixed(2)}
|
|
402
|
+
</span>
|
|
403
|
+
)
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
key: "actions",
|
|
407
|
+
title: "Actions",
|
|
408
|
+
render: (row) => (
|
|
409
|
+
<button onClick={() => console.log(row)}>
|
|
410
|
+
Custom Action
|
|
411
|
+
</button>
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
],
|
|
415
|
+
data: [...]
|
|
416
|
+
};
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### 5. View Modal Variants
|
|
420
|
+
|
|
421
|
+
Display details in different layouts with view modal variants:
|
|
422
|
+
|
|
423
|
+
```js
|
|
424
|
+
// Default variant - standard grid layout
|
|
425
|
+
const config = {
|
|
426
|
+
modalConfig: {
|
|
427
|
+
viewModal: {
|
|
428
|
+
title: "User Details",
|
|
429
|
+
variant: "default", // Standard grid layout
|
|
430
|
+
fields: [
|
|
431
|
+
{ key: "name", label: "Full Name", type: "text" },
|
|
432
|
+
{ key: "email", label: "Email", type: "text" }
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// Card variant - each field in an elevated card
|
|
439
|
+
const cardConfig = {
|
|
440
|
+
modalConfig: {
|
|
441
|
+
viewModal: {
|
|
442
|
+
title: "User Details",
|
|
443
|
+
variant: "card", // Each field is a separate card
|
|
444
|
+
styles: {
|
|
445
|
+
containerClass: "grid grid-cols-12 gap-4",
|
|
446
|
+
cardGroupClass: "col-span-6 bg-white rounded-lg shadow p-4"
|
|
447
|
+
},
|
|
448
|
+
fields: [...]
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// Split variant - property sheet style with dividing lines
|
|
454
|
+
const splitConfig = {
|
|
455
|
+
modalConfig: {
|
|
456
|
+
viewModal: {
|
|
457
|
+
title: "User Details",
|
|
458
|
+
variant: "split", // Clean property-sheet layout
|
|
459
|
+
fields: [...]
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 6. Custom View Component
|
|
466
|
+
|
|
467
|
+
Use a fully custom component for the view modal:
|
|
468
|
+
|
|
469
|
+
```js
|
|
470
|
+
const CustomUserView = ({ data }) => (
|
|
471
|
+
<div className="space-y-4">
|
|
472
|
+
<div className="flex items-center gap-4">
|
|
473
|
+
<img
|
|
474
|
+
src={data.avatarUrl}
|
|
475
|
+
alt={data.name}
|
|
476
|
+
className="w-16 h-16 rounded-full"
|
|
477
|
+
/>
|
|
478
|
+
<div>
|
|
479
|
+
<h2 className="text-xl font-bold">{data.name}</h2>
|
|
480
|
+
<p className="text-gray-600">{data.email}</p>
|
|
481
|
+
</div>
|
|
482
|
+
</div>
|
|
483
|
+
<div className="grid grid-cols-2 gap-4">
|
|
484
|
+
<div>
|
|
485
|
+
<label className="text-sm font-semibold">Phone</label>
|
|
486
|
+
<p>{data.phone}</p>
|
|
487
|
+
</div>
|
|
488
|
+
<div>
|
|
489
|
+
<label className="text-sm font-semibold">Status</label>
|
|
490
|
+
<p>{data.status}</p>
|
|
491
|
+
</div>
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
const config = {
|
|
497
|
+
modalConfig: {
|
|
498
|
+
viewModal: {
|
|
499
|
+
title: "User Details",
|
|
500
|
+
component: CustomUserView, // Use custom component
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### 7. Row Click Handler
|
|
507
|
+
|
|
508
|
+
Handle clicks on table rows:
|
|
509
|
+
|
|
510
|
+
```js
|
|
511
|
+
// For a custom function
|
|
512
|
+
const config = {
|
|
513
|
+
tableConfig: {
|
|
514
|
+
table_head: [...],
|
|
515
|
+
data: [...],
|
|
516
|
+
rowClick: (row, rowIndex) => {
|
|
517
|
+
console.log(`Row ${rowIndex} clicked:`, row);
|
|
518
|
+
// Open custom drawer, navigate, or perform any action
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// For open details on row click
|
|
524
|
+
const config = {
|
|
525
|
+
tableConfig: {
|
|
526
|
+
table_head: [...],
|
|
527
|
+
data: [...],
|
|
528
|
+
rowClick: true,
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### 8. Server-Side Filtering
|
|
535
|
+
|
|
536
|
+
Implement server-side filtering with custom filter fields:
|
|
537
|
+
|
|
538
|
+
```js
|
|
539
|
+
const config = {
|
|
540
|
+
tableConfig: {
|
|
541
|
+
filter: {
|
|
542
|
+
enabled: true,
|
|
543
|
+
useServerSideFilters: true, // Enable server-side filtering
|
|
544
|
+
},
|
|
545
|
+
filterConfig: {
|
|
546
|
+
fields: [
|
|
547
|
+
{
|
|
548
|
+
key: "status",
|
|
549
|
+
label: "Status",
|
|
550
|
+
type: "select",
|
|
551
|
+
options: [
|
|
552
|
+
{ value: "active", label: "Active" },
|
|
553
|
+
{ value: "inactive", label: "Inactive" },
|
|
554
|
+
],
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
key: "createdFrom",
|
|
558
|
+
label: "Created From",
|
|
559
|
+
type: "date",
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
key: "createdTo",
|
|
563
|
+
label: "Created To",
|
|
564
|
+
type: "date",
|
|
565
|
+
},
|
|
566
|
+
],
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
fetchData: async ({
|
|
570
|
+
search,
|
|
571
|
+
rows_per_page,
|
|
572
|
+
current_page,
|
|
573
|
+
sort_by,
|
|
574
|
+
sort_order,
|
|
575
|
+
...filters // Additional filter params passed here
|
|
576
|
+
}) => {
|
|
577
|
+
const resp = await api.get("/users", {
|
|
578
|
+
params: {
|
|
579
|
+
q: search,
|
|
580
|
+
limit: rows_per_page,
|
|
581
|
+
page: current_page,
|
|
582
|
+
sort_by,
|
|
583
|
+
sort_order,
|
|
584
|
+
...filters, // Include filters in API call
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
return { data: resp.items, pagination: resp.pagination };
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### 9. Input Masking
|
|
593
|
+
|
|
594
|
+
Format input with masks using pattern syntax:
|
|
595
|
+
|
|
596
|
+
```js
|
|
597
|
+
const formFields = [
|
|
598
|
+
{
|
|
599
|
+
key: "phone",
|
|
600
|
+
label: "Phone Number",
|
|
601
|
+
type: "text",
|
|
602
|
+
mask: "(99) 99999-9999", // Mask pattern
|
|
603
|
+
maskApplyOnValue: true, // Apply mask to default value
|
|
604
|
+
placeholder: "(00) 00000-0000",
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
key: "zipCode",
|
|
608
|
+
label: "ZIP Code",
|
|
609
|
+
type: "text",
|
|
610
|
+
mask: "99999-999", // Pattern: 9 = digit, A = letter, X = alphanumeric, * = any
|
|
611
|
+
placeholder: "00000-000",
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
key: "creditCard",
|
|
615
|
+
label: "Credit Card",
|
|
616
|
+
type: "text",
|
|
617
|
+
mask: "9999 9999 9999 9999",
|
|
618
|
+
placeholder: "0000 0000 0000 0000",
|
|
619
|
+
},
|
|
620
|
+
];
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Mask Pattern Reference:**
|
|
624
|
+
|
|
625
|
+
- `9` — Digit (0-9)
|
|
626
|
+
- `A` — Letter (a-zA-Z)
|
|
627
|
+
- `X` — Alphanumeric (a-zA-Z0-9)
|
|
628
|
+
- `*` — Any character
|
|
629
|
+
- Any other character is literal (e.g., `-`, `/`, ` `)
|
|
630
|
+
|
|
631
|
+
### 10. Image Cropping
|
|
632
|
+
|
|
633
|
+
Enable image cropping in image picker:
|
|
634
|
+
|
|
635
|
+
```js
|
|
636
|
+
const formFields = [
|
|
637
|
+
{
|
|
638
|
+
key: "profileImage",
|
|
639
|
+
label: "Profile Picture",
|
|
640
|
+
type: "image",
|
|
641
|
+
cropImage: true, // Enable cropping modal
|
|
642
|
+
aspectRatio: 1, // 1:1 square ratio
|
|
643
|
+
dragDrop: true,
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
key: "bannerImage",
|
|
647
|
+
label: "Banner Image",
|
|
648
|
+
type: "image",
|
|
649
|
+
cropImage: true,
|
|
650
|
+
aspectRatio: 16 / 9, // 16:9 widescreen ratio
|
|
651
|
+
dragDrop: true,
|
|
652
|
+
},
|
|
653
|
+
];
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### 11. Sortable Columns
|
|
657
|
+
|
|
658
|
+
Configure sorting with custom options and auto-generation:
|
|
659
|
+
|
|
660
|
+
```js
|
|
661
|
+
const config = {
|
|
662
|
+
tableConfig: {
|
|
663
|
+
sort: {
|
|
664
|
+
enabled: true,
|
|
665
|
+
useServerSideSorting: false, // Client-side sorting
|
|
666
|
+
autoGenerate: true, // Auto-generate sort options from table headers
|
|
667
|
+
fields: ["name", "email", "createdAt"], // Fields to make sortable
|
|
668
|
+
defaultValue: "name", // Default sort field
|
|
669
|
+
clearLabel: "Clear Sort",
|
|
670
|
+
onChange: (payload) => {
|
|
671
|
+
console.log("Sort changed:", payload);
|
|
672
|
+
// payload contains: { value, option, key, order, type }
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
table_head: [
|
|
676
|
+
{ key: "name", title: "Name" },
|
|
677
|
+
{ key: "email", title: "Email" },
|
|
678
|
+
{ key: "createdAt", title: "Created Date" },
|
|
679
|
+
],
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
```
|
|
683
|
+
|
|
309
684
|
### Examples
|
|
310
|
-
|
|
685
|
+
|
|
686
|
+
#### Example 1: Minimal Client-Side CRUD
|
|
687
|
+
|
|
688
|
+
```js
|
|
689
|
+
import Crud from "react-admin-crud-manager";
|
|
690
|
+
|
|
691
|
+
const users = [
|
|
692
|
+
{ id: 1, name: "John Doe", email: "john@example.com", status: "active" },
|
|
693
|
+
{ id: 2, name: "Jane Smith", email: "jane@example.com", status: "inactive" },
|
|
694
|
+
];
|
|
695
|
+
|
|
696
|
+
function App() {
|
|
697
|
+
const config = {
|
|
698
|
+
title: "Users",
|
|
699
|
+
fetchData: async () => ({ data: users, pagination: null }),
|
|
700
|
+
isStaticData: true,
|
|
701
|
+
tableConfig: {
|
|
702
|
+
table_head: [
|
|
703
|
+
{ key: "id", title: "ID", type: "index" },
|
|
704
|
+
{ key: "name", title: "Name" },
|
|
705
|
+
{ key: "email", title: "Email" },
|
|
706
|
+
{
|
|
707
|
+
key: "status",
|
|
708
|
+
title: "Status",
|
|
709
|
+
type: "chip",
|
|
710
|
+
chipOptions: [
|
|
711
|
+
{ value: "active", label: "Active", color: "green" },
|
|
712
|
+
{ value: "inactive", label: "Inactive", color: "red" },
|
|
713
|
+
],
|
|
714
|
+
},
|
|
715
|
+
],
|
|
716
|
+
search: { enabled: true, searchKeys: ["name", "email"] },
|
|
717
|
+
pagination: { enabled: true },
|
|
718
|
+
},
|
|
719
|
+
modalConfig: {
|
|
720
|
+
addModal: {
|
|
721
|
+
title: "Add User",
|
|
722
|
+
formFields: [
|
|
723
|
+
{ key: "name", label: "Name", type: "text", required: true },
|
|
724
|
+
{ key: "email", label: "Email", type: "email", required: true },
|
|
725
|
+
{
|
|
726
|
+
key: "status",
|
|
727
|
+
label: "Status",
|
|
728
|
+
type: "select",
|
|
729
|
+
options: [
|
|
730
|
+
{ value: "active", label: "Active" },
|
|
731
|
+
{ value: "inactive", label: "Inactive" },
|
|
732
|
+
],
|
|
733
|
+
},
|
|
734
|
+
],
|
|
735
|
+
handleSubmit: async (formData) => ({
|
|
736
|
+
newObject: {
|
|
737
|
+
...formData,
|
|
738
|
+
id: Math.max(...users.map((u) => u.id)) + 1,
|
|
739
|
+
},
|
|
740
|
+
}),
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
return <Crud config={config} />;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export default App;
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### Example 2: Server-Side CRUD with Advanced Features
|
|
752
|
+
|
|
753
|
+
```js
|
|
754
|
+
import Crud from "react-admin-crud-manager";
|
|
755
|
+
import axios from "axios";
|
|
756
|
+
import { PlusIcon, EditIcon, TrashIcon } from "lucide-react";
|
|
757
|
+
|
|
758
|
+
function App() {
|
|
759
|
+
const api = axios.create({ baseURL: "https://api.example.com" });
|
|
760
|
+
|
|
761
|
+
const config = {
|
|
762
|
+
title: "Products",
|
|
763
|
+
description: "Manage your products inventory",
|
|
764
|
+
buttonText: "Add Product",
|
|
765
|
+
fetchData: async ({
|
|
766
|
+
search,
|
|
767
|
+
rows_per_page,
|
|
768
|
+
current_page,
|
|
769
|
+
sort_by,
|
|
770
|
+
sort_order,
|
|
771
|
+
category,
|
|
772
|
+
minPrice,
|
|
773
|
+
maxPrice,
|
|
774
|
+
}) => {
|
|
775
|
+
const resp = await api.get("/products", {
|
|
776
|
+
params: {
|
|
777
|
+
q: search,
|
|
778
|
+
limit: rows_per_page,
|
|
779
|
+
page: current_page,
|
|
780
|
+
sort_by,
|
|
781
|
+
sort_order,
|
|
782
|
+
category,
|
|
783
|
+
minPrice,
|
|
784
|
+
maxPrice,
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
return {
|
|
788
|
+
data: resp.data.items,
|
|
789
|
+
pagination: {
|
|
790
|
+
current_page: resp.data.page,
|
|
791
|
+
rows_per_page: resp.data.limit,
|
|
792
|
+
total_pages: resp.data.totalPages,
|
|
793
|
+
total_records: resp.data.total,
|
|
794
|
+
},
|
|
795
|
+
};
|
|
796
|
+
},
|
|
797
|
+
tableConfig: {
|
|
798
|
+
table_head: [
|
|
799
|
+
{ key: "id", title: "ID", type: "index" },
|
|
800
|
+
{
|
|
801
|
+
key: "image",
|
|
802
|
+
title: "Image",
|
|
803
|
+
type: "avatar",
|
|
804
|
+
imageKey: "image",
|
|
805
|
+
titleKey: "name",
|
|
806
|
+
},
|
|
807
|
+
{ key: "name", title: "Product Name" },
|
|
808
|
+
{
|
|
809
|
+
key: "price",
|
|
810
|
+
title: "Price",
|
|
811
|
+
render: (row) => `$${row.price.toFixed(2)}`,
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
key: "category",
|
|
815
|
+
title: "Category",
|
|
816
|
+
type: "chip",
|
|
817
|
+
variant: "soft",
|
|
818
|
+
chipOptions: [
|
|
819
|
+
{ value: "electronics", label: "Electronics", color: "blue" },
|
|
820
|
+
{ value: "clothing", label: "Clothing", color: "purple" },
|
|
821
|
+
],
|
|
822
|
+
},
|
|
823
|
+
],
|
|
824
|
+
search: {
|
|
825
|
+
enabled: true,
|
|
826
|
+
useServerSideSearch: true,
|
|
827
|
+
},
|
|
828
|
+
filter: { enabled: true, useServerSideFilters: true },
|
|
829
|
+
pagination: { enabled: true, useServerSidePagination: true },
|
|
830
|
+
sort: { enabled: true, useServerSideSorting: true, autoGenerate: true },
|
|
831
|
+
exportCSV: {
|
|
832
|
+
enabled: true,
|
|
833
|
+
fileName: "products.csv",
|
|
834
|
+
fields: [
|
|
835
|
+
{ label: "ID", key: "id" },
|
|
836
|
+
{ label: "Name", key: "name" },
|
|
837
|
+
{ label: "Price", key: "price" },
|
|
838
|
+
],
|
|
839
|
+
},
|
|
840
|
+
filterConfig: {
|
|
841
|
+
fields: [
|
|
842
|
+
{
|
|
843
|
+
key: "category",
|
|
844
|
+
label: "Category",
|
|
845
|
+
type: "select",
|
|
846
|
+
options: [
|
|
847
|
+
{ value: "electronics", label: "Electronics" },
|
|
848
|
+
{ value: "clothing", label: "Clothing" },
|
|
849
|
+
],
|
|
850
|
+
},
|
|
851
|
+
{ key: "minPrice", label: "Min Price", type: "number" },
|
|
852
|
+
{ key: "maxPrice", label: "Max Price", type: "number" },
|
|
853
|
+
],
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
modalConfig: {
|
|
857
|
+
addModal: {
|
|
858
|
+
title: "Add Product",
|
|
859
|
+
size: "lg",
|
|
860
|
+
icon: <PlusIcon />,
|
|
861
|
+
formFields: [
|
|
862
|
+
{
|
|
863
|
+
key: "name",
|
|
864
|
+
label: "Product Name",
|
|
865
|
+
type: "text",
|
|
866
|
+
required: true,
|
|
867
|
+
parentClass: "col-span-12",
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
key: "price",
|
|
871
|
+
label: "Price",
|
|
872
|
+
type: "number",
|
|
873
|
+
required: true,
|
|
874
|
+
parentClass: "col-span-6",
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
key: "category",
|
|
878
|
+
label: "Category",
|
|
879
|
+
type: "select",
|
|
880
|
+
required: true,
|
|
881
|
+
parentClass: "col-span-6",
|
|
882
|
+
options: [
|
|
883
|
+
{ value: "electronics", label: "Electronics" },
|
|
884
|
+
{ value: "clothing", label: "Clothing" },
|
|
885
|
+
],
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
key: "stock",
|
|
889
|
+
label: "Stock",
|
|
890
|
+
type: "number",
|
|
891
|
+
required: true,
|
|
892
|
+
parentClass: "col-span-6",
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
key: "description",
|
|
896
|
+
label: "Description",
|
|
897
|
+
type: "textarea",
|
|
898
|
+
rows: 4,
|
|
899
|
+
parentClass: "col-span-12",
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
key: "image",
|
|
903
|
+
label: "Product Image",
|
|
904
|
+
type: "image",
|
|
905
|
+
cropImage: true,
|
|
906
|
+
aspectRatio: 1,
|
|
907
|
+
dragDrop: true,
|
|
908
|
+
parentClass: "col-span-12",
|
|
909
|
+
},
|
|
910
|
+
],
|
|
911
|
+
handleSubmit: async (formData) => {
|
|
912
|
+
const resp = await api.post("/products", formData);
|
|
913
|
+
return {
|
|
914
|
+
newObject: resp.data,
|
|
915
|
+
message: "Product added successfully!",
|
|
916
|
+
};
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
editModal: {
|
|
920
|
+
title: "Edit Product",
|
|
921
|
+
size: "lg",
|
|
922
|
+
icon: <EditIcon />,
|
|
923
|
+
formFields: [
|
|
924
|
+
{
|
|
925
|
+
key: "name",
|
|
926
|
+
label: "Product Name",
|
|
927
|
+
type: "text",
|
|
928
|
+
required: true,
|
|
929
|
+
parentClass: "col-span-12",
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
key: "price",
|
|
933
|
+
label: "Price",
|
|
934
|
+
type: "number",
|
|
935
|
+
required: true,
|
|
936
|
+
parentClass: "col-span-6",
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
key: "stock",
|
|
940
|
+
label: "Stock",
|
|
941
|
+
type: "number",
|
|
942
|
+
required: true,
|
|
943
|
+
parentClass: "col-span-6",
|
|
944
|
+
},
|
|
945
|
+
],
|
|
946
|
+
handleSubmit: async (formData, item) => {
|
|
947
|
+
const resp = await api.put(`/products/${item.id}`, formData);
|
|
948
|
+
return { newObject: resp.data, targetObject: item };
|
|
949
|
+
},
|
|
950
|
+
},
|
|
951
|
+
deleteModal: {
|
|
952
|
+
title: "Delete Product",
|
|
953
|
+
icon: <TrashIcon />,
|
|
954
|
+
confirmText: "Are you sure you want to delete this product?",
|
|
955
|
+
referenceKey: "name",
|
|
956
|
+
actionButtons: [
|
|
957
|
+
{
|
|
958
|
+
type: "button",
|
|
959
|
+
label: "Delete",
|
|
960
|
+
color: "error",
|
|
961
|
+
variant: "contained",
|
|
962
|
+
onClick: async (event, item) => {
|
|
963
|
+
event.preventDefault();
|
|
964
|
+
await api.delete(`/products/${item.id}`);
|
|
965
|
+
return { targetObject: item };
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
],
|
|
969
|
+
},
|
|
970
|
+
viewModal: {
|
|
971
|
+
title: "Product Details",
|
|
972
|
+
variant: "card",
|
|
973
|
+
fields: [
|
|
974
|
+
{ key: "id", label: "ID" },
|
|
975
|
+
{ key: "name", label: "Product Name" },
|
|
976
|
+
{ key: "price", label: "Price" },
|
|
977
|
+
{ key: "stock", label: "Stock" },
|
|
978
|
+
{ key: "category", label: "Category", type: "chip" },
|
|
979
|
+
],
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
return <Crud config={config} />;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
export default App;
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
#### Example 3: Complex Form with Conditional Fields and Validation
|
|
311
991
|
|
|
312
992
|
```js
|
|
313
993
|
const config = {
|
|
314
|
-
title:
|
|
315
|
-
fetchData: async () => ({ data: users, pagination: null }),
|
|
316
|
-
isStaticData: true,
|
|
994
|
+
title: "Advanced User Registration",
|
|
317
995
|
tableConfig: {
|
|
318
|
-
table_head: [
|
|
319
|
-
|
|
996
|
+
table_head: [
|
|
997
|
+
{ key: "id", title: "ID", type: "index" },
|
|
998
|
+
{ key: "name", title: "Full Name" },
|
|
999
|
+
{ key: "email", title: "Email" },
|
|
1000
|
+
{ key: "userType", title: "Type", type: "chip" },
|
|
1001
|
+
],
|
|
320
1002
|
},
|
|
321
1003
|
modalConfig: {
|
|
322
|
-
addModal: {
|
|
1004
|
+
addModal: {
|
|
1005
|
+
title: "Register New User",
|
|
1006
|
+
formFields: [
|
|
1007
|
+
{
|
|
1008
|
+
key: "name",
|
|
1009
|
+
label: "Full Name",
|
|
1010
|
+
type: "text",
|
|
1011
|
+
required: true,
|
|
1012
|
+
parentClass: "col-span-12",
|
|
1013
|
+
customValidation: (value) => {
|
|
1014
|
+
if (value.trim().split(" ").length < 2) {
|
|
1015
|
+
return "Please enter your full name";
|
|
1016
|
+
}
|
|
1017
|
+
return true;
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
key: "email",
|
|
1022
|
+
label: "Email Address",
|
|
1023
|
+
type: "email",
|
|
1024
|
+
required: true,
|
|
1025
|
+
parentClass: "col-span-12",
|
|
1026
|
+
customValidation: (value) => {
|
|
1027
|
+
if (!value.includes("@company.com")) {
|
|
1028
|
+
return "Must use company email";
|
|
1029
|
+
}
|
|
1030
|
+
return true;
|
|
1031
|
+
},
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
key: "phone",
|
|
1035
|
+
label: "Phone",
|
|
1036
|
+
type: "phone",
|
|
1037
|
+
countriesList: true,
|
|
1038
|
+
mask: "(99) 99999-9999",
|
|
1039
|
+
parentClass: "col-span-12",
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
key: "userType",
|
|
1043
|
+
label: "User Type",
|
|
1044
|
+
type: "select",
|
|
1045
|
+
required: true,
|
|
1046
|
+
parentClass: "col-span-6",
|
|
1047
|
+
options: [
|
|
1048
|
+
{ value: "admin", label: "Administrator" },
|
|
1049
|
+
{ value: "user", label: "Regular User" },
|
|
1050
|
+
],
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
key: "adminLevel",
|
|
1054
|
+
label: "Admin Level",
|
|
1055
|
+
type: "select",
|
|
1056
|
+
parentClass: "col-span-6",
|
|
1057
|
+
renderCondition: (data) => data.userType === "admin",
|
|
1058
|
+
options: [
|
|
1059
|
+
{ value: "superadmin", label: "Super Admin" },
|
|
1060
|
+
{ value: "moderator", label: "Moderator" },
|
|
1061
|
+
],
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
key: "permissions",
|
|
1065
|
+
label: "Permissions",
|
|
1066
|
+
type: "checkbox",
|
|
1067
|
+
parentClass: "col-span-12",
|
|
1068
|
+
multiple: true,
|
|
1069
|
+
renderCondition: (data) => data.userType === "admin",
|
|
1070
|
+
options: [
|
|
1071
|
+
{ value: "read", label: "Can Read" },
|
|
1072
|
+
{ value: "write", label: "Can Write" },
|
|
1073
|
+
{ value: "delete", label: "Can Delete" },
|
|
1074
|
+
],
|
|
1075
|
+
},
|
|
1076
|
+
],
|
|
1077
|
+
handleSubmit: async (formData) => {
|
|
1078
|
+
const resp = await api.post("/users", formData);
|
|
1079
|
+
return {
|
|
1080
|
+
newObject: resp.data,
|
|
1081
|
+
message: `User ${formData.name} created successfully!`,
|
|
1082
|
+
};
|
|
1083
|
+
},
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
};
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
---
|
|
1090
|
+
|
|
1091
|
+
## Best Practices & Common Patterns
|
|
1092
|
+
|
|
1093
|
+
### 1. State Management with Static Data
|
|
1094
|
+
|
|
1095
|
+
For client-side CRUD operations, use `isStaticData: true` and manage data updates through modal handlers:
|
|
1096
|
+
|
|
1097
|
+
```js
|
|
1098
|
+
const [users, setUsers] = useState(initialUsers);
|
|
1099
|
+
|
|
1100
|
+
const config = {
|
|
1101
|
+
fetchData: async () => ({ data: users, pagination: null }),
|
|
1102
|
+
isStaticData: true,
|
|
1103
|
+
modalConfig: {
|
|
1104
|
+
addModal: {
|
|
1105
|
+
handleSubmit: async (formData) => {
|
|
1106
|
+
const newUser = { ...formData, id: Date.now() };
|
|
1107
|
+
setUsers([...users, newUser]);
|
|
1108
|
+
return { newObject: newUser };
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
editModal: {
|
|
1112
|
+
handleSubmit: async (formData, item) => {
|
|
1113
|
+
const updated = { ...item, ...formData };
|
|
1114
|
+
setUsers(users.map((u) => (u.id === item.id ? updated : u)));
|
|
1115
|
+
return { newObject: updated, targetObject: item };
|
|
1116
|
+
},
|
|
1117
|
+
},
|
|
1118
|
+
},
|
|
1119
|
+
};
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
### 2. Server-Side Operations Best Practices
|
|
1123
|
+
|
|
1124
|
+
```js
|
|
1125
|
+
// Always provide pagination info back to the component
|
|
1126
|
+
const fetchData = async (params) => {
|
|
1127
|
+
const resp = await api.get("/items", { params });
|
|
1128
|
+
return {
|
|
1129
|
+
data: resp.data.items || [],
|
|
1130
|
+
pagination: {
|
|
1131
|
+
current_page: resp.data.meta.currentPage,
|
|
1132
|
+
rows_per_page: resp.data.meta.perPage,
|
|
1133
|
+
total_pages: resp.data.meta.totalPages,
|
|
1134
|
+
total_records: resp.data.meta.total,
|
|
1135
|
+
},
|
|
1136
|
+
};
|
|
1137
|
+
};
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
### 3. Error Handling in Modal Submissions
|
|
1141
|
+
|
|
1142
|
+
```js
|
|
1143
|
+
const handleSubmit = async (formData) => {
|
|
1144
|
+
try {
|
|
1145
|
+
const resp = await api.post("/items", formData);
|
|
1146
|
+
return {
|
|
1147
|
+
newObject: resp.data,
|
|
1148
|
+
message: "Item created successfully!", // Optional success message
|
|
1149
|
+
};
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
// Throw error to trigger snackbar error notification
|
|
1152
|
+
throw new Error(error.response?.data?.message || "Failed to create item");
|
|
323
1153
|
}
|
|
324
1154
|
};
|
|
325
1155
|
```
|
|
326
1156
|
|
|
327
|
-
|
|
1157
|
+
### 4. Dynamic Form Fields Based on Data
|
|
1158
|
+
|
|
1159
|
+
Combine `renderCondition` and `defaultValue` for dynamic forms:
|
|
328
1160
|
|
|
329
1161
|
```js
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
1162
|
+
const formFields = [
|
|
1163
|
+
{
|
|
1164
|
+
key: "type",
|
|
1165
|
+
type: "select",
|
|
1166
|
+
options: [
|
|
1167
|
+
{ value: "personal", label: "Personal" },
|
|
1168
|
+
{ value: "business", label: "Business" },
|
|
1169
|
+
],
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
key: "companyName",
|
|
1173
|
+
label: "Company Name",
|
|
1174
|
+
type: "text",
|
|
1175
|
+
renderCondition: (data) => data.type === "business",
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
key: "phone",
|
|
1179
|
+
type: "phone",
|
|
1180
|
+
renderCondition: (data) => data.type === "personal",
|
|
1181
|
+
},
|
|
1182
|
+
];
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
### 5. Custom Table Rendering for Complex Data
|
|
1186
|
+
|
|
1187
|
+
```js
|
|
1188
|
+
const tableConfig = {
|
|
1189
|
+
table_head: [
|
|
1190
|
+
{
|
|
1191
|
+
key: "user",
|
|
1192
|
+
title: "User",
|
|
1193
|
+
render: (row) => (
|
|
1194
|
+
<div className="flex items-center gap-3">
|
|
1195
|
+
<img src={row.avatar} className="w-8 h-8 rounded-full" />
|
|
1196
|
+
<div>
|
|
1197
|
+
<p className="font-semibold">{row.name}</p>
|
|
1198
|
+
<p className="text-xs text-gray-500">{row.email}</p>
|
|
1199
|
+
</div>
|
|
1200
|
+
</div>
|
|
1201
|
+
),
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
key: "metrics",
|
|
1205
|
+
title: "Performance",
|
|
1206
|
+
render: (row) => (
|
|
1207
|
+
<div className="flex gap-2">
|
|
1208
|
+
<span className="bg-green-100 text-green-800 px-2 py-1 rounded text-xs">
|
|
1209
|
+
{row.completed}%
|
|
1210
|
+
</span>
|
|
1211
|
+
</div>
|
|
1212
|
+
),
|
|
1213
|
+
},
|
|
1214
|
+
],
|
|
1215
|
+
};
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
### 6. Validation Patterns
|
|
1219
|
+
|
|
1220
|
+
```js
|
|
1221
|
+
const formFields = [
|
|
1222
|
+
{
|
|
1223
|
+
key: "email",
|
|
1224
|
+
type: "email",
|
|
1225
|
+
customValidation: (value) => {
|
|
1226
|
+
// Async validation example
|
|
1227
|
+
const exists = checkEmailExists(value);
|
|
1228
|
+
if (exists) return "Email already in use";
|
|
1229
|
+
return true;
|
|
1230
|
+
},
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
key: "password",
|
|
1234
|
+
type: "password",
|
|
1235
|
+
customValidation: (value) => {
|
|
1236
|
+
if (value.length < 8) return "Password must be at least 8 characters";
|
|
1237
|
+
if (!/[A-Z]/.test(value)) return "Must contain uppercase letter";
|
|
1238
|
+
if (!/[0-9]/.test(value)) return "Must contain a number";
|
|
1239
|
+
return true;
|
|
1240
|
+
},
|
|
1241
|
+
},
|
|
1242
|
+
];
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
### 7. Handling File Uploads
|
|
1246
|
+
|
|
1247
|
+
```js
|
|
1248
|
+
const handleImageUpload = async (file) => {
|
|
1249
|
+
const formData = new FormData();
|
|
1250
|
+
formData.append("file", file);
|
|
1251
|
+
const resp = await api.post("/upload", formData);
|
|
1252
|
+
return resp.data.url;
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1255
|
+
const config = {
|
|
1256
|
+
modalConfig: {
|
|
1257
|
+
addModal: {
|
|
1258
|
+
formFields: [
|
|
1259
|
+
{
|
|
1260
|
+
key: "image",
|
|
1261
|
+
type: "image",
|
|
1262
|
+
dragDrop: true,
|
|
1263
|
+
cropImage: true,
|
|
1264
|
+
aspectRatio: 1,
|
|
1265
|
+
},
|
|
1266
|
+
],
|
|
1267
|
+
handleSubmit: async (formData) => {
|
|
1268
|
+
// Image data includes base64 or file data
|
|
1269
|
+
const imageUrl = await handleImageUpload(formData.image);
|
|
1270
|
+
const resp = await api.post("/items", {
|
|
1271
|
+
...formData,
|
|
1272
|
+
image: imageUrl,
|
|
1273
|
+
});
|
|
1274
|
+
return { newObject: resp.data };
|
|
1275
|
+
},
|
|
1276
|
+
},
|
|
1277
|
+
},
|
|
1278
|
+
};
|
|
1279
|
+
```
|
|
1280
|
+
|
|
1281
|
+
### 8. Conditional Rendering with Row Data
|
|
1282
|
+
|
|
1283
|
+
```js
|
|
1284
|
+
const viewModal = {
|
|
1285
|
+
fields: [
|
|
1286
|
+
{
|
|
1287
|
+
key: "status",
|
|
1288
|
+
label: "Status",
|
|
1289
|
+
type: "chip",
|
|
1290
|
+
renderCondition: (data) => data.status !== null,
|
|
1291
|
+
chipOptions: [
|
|
1292
|
+
{ value: "active", label: "Active", color: "green" },
|
|
1293
|
+
{ value: "suspended", label: "Suspended", color: "red" },
|
|
1294
|
+
],
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
key: "suspendReason",
|
|
1298
|
+
label: "Suspension Reason",
|
|
1299
|
+
renderCondition: (data) => data.status === "suspended", // Only show if suspended
|
|
1300
|
+
},
|
|
1301
|
+
],
|
|
1302
|
+
};
|
|
1303
|
+
```
|
|
1304
|
+
|
|
1305
|
+
### 9. Multiple Sort Options (Server-Side)
|
|
1306
|
+
|
|
1307
|
+
```js
|
|
1308
|
+
const tableConfig = {
|
|
1309
|
+
sort: {
|
|
1310
|
+
enabled: true,
|
|
1311
|
+
useServerSideSorting: true,
|
|
1312
|
+
options: [
|
|
1313
|
+
{
|
|
1314
|
+
value: "recent",
|
|
1315
|
+
label: "Most Recent",
|
|
1316
|
+
key: "createdAt",
|
|
1317
|
+
order: "desc",
|
|
1318
|
+
},
|
|
1319
|
+
{ value: "oldest", label: "Oldest", key: "createdAt", order: "asc" },
|
|
1320
|
+
{ value: "name-asc", label: "Name (A-Z)", key: "name", order: "asc" },
|
|
1321
|
+
{ value: "name-desc", label: "Name (Z-A)", key: "name", order: "desc" },
|
|
1322
|
+
{ value: "popular", label: "Most Popular", key: "views", order: "desc" },
|
|
1323
|
+
],
|
|
1324
|
+
onChange: (payload) => {
|
|
1325
|
+
console.log(`Sorted by: ${payload.option?.label}`);
|
|
1326
|
+
},
|
|
1327
|
+
},
|
|
333
1328
|
};
|
|
334
1329
|
```
|
|
335
1330
|
|
|
336
1331
|
---
|
|
337
1332
|
|
|
338
1333
|
## License
|
|
1334
|
+
|
|
339
1335
|
MIT
|