react-admin-crud-manager 1.2.0 → 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.
Files changed (2) hide show
  1. package/README.md +382 -501
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -38,7 +38,6 @@ function App() {
38
38
  ## Components
39
39
 
40
40
  - `Crud`: Main CRUD page component
41
- - `Table`, `Modal`, `Form`, etc.: Available for advanced use
42
41
 
43
42
  ## Props
44
43
 
@@ -58,7 +57,7 @@ Below is a complete reference of the public props accepted by this package (type
58
57
  | ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59
58
  | `title` | string | Yes | Title of the CRUD page |
60
59
  | `description` | string | No | Optional description text |
61
- | `buttonText` | string | No | Custom text for action buttons |
60
+ | `buttonText` | string | No | Custom text for add button |
62
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. |
63
62
  | `fetchRowDetails` | function | No | Async function to fetch additional details for a row. Signature: `async (item) => Promise<any>`. Used for view modal or details. |
64
63
  | `isStaticData` | boolean | No | Default: `false`. If true, add/edit/delete apply client-side instead of re-fetching |
@@ -72,41 +71,36 @@ Below is a complete reference of the public props accepted by this package (type
72
71
 
73
72
  #### Table Configuration Keys
74
73
 
75
- | Key | Type | Description | Accepted Values / Example |
76
- | ----------------------------- | ----------------------- | ------------------------------- | ------- |
77
- | `table_head` | array of column objects | Column definitions | Array of table column objects (see [Table column object](#table-column-object-table_head)) |
78
- | `data` | array | Rows displayed in the table | Array of objects (e.g., `[{ id: 1, name: "John" }, ...]`) |
79
- | `loading` | boolean | Show skeleton loader when true | `true`, `false`; default: `false` |
80
- | `search` | object | Search functionality config | `{ enabled: true, placeholder: "Search...", useServerSideSearch?: false, searchKeys?: ["name", "email"] }` |
81
- | `filter` | object | Filter drawer config | `{ enabled: true, useServerSideFilters?: false }` |
82
- | `pagination` | object | Pagination controls | `{ enabled: true, rows_per_page: 10, useServerSidePagination?: false, current_page?: 1, total_pages?: 5, total_records?: 50 }` |
83
- | `sort` | object | Sorting configuration | `{ enabled: true, useServerSideSorting?: false, autoGenerate: true, onChange?: (payload) => void, options?: [...], clearLabel?: "Clear Sort" }` |
84
- | `exportCSV` | object | Export data as CSV | `{ enabled: true, fileName: "users.csv", fields: [{ label: "Name", key: "name" }, ...] }` |
85
- | `emptyMessage` | string | Message when no rows exist | `"No data available"`, `"No records found"`, etc. |
86
- | `onMenuAction` | function | Callback for menu actions (edit/view/delete) | `(actionType: string, item: object) => void`; actionType: `"edit"`, `"view"`, `"delete"` |
87
- | `setServerSidePaginationData` | function | Update pagination/search state | `(data: { search, rows_per_page, current_page, sort_by, sort_order, ...filters }) => void` |
88
- | `onFilterApply` | function | Callback when filter applied | `(filters: object) => void` |
89
- | `filterConfig` | object | Filter drawer form fields | `{ fields: [{ key: "status", type: "select", options: [...] }, ...] }`; uses [Form Field Schema](#form-field-schema) |
90
- | `rowClick` | function | Callback on table row click | `(row: object, rowIndex: number) => void` |
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>
91
85
 
92
86
  #### Table Column Object (`table_head[]`)
93
87
 
94
- | Key | Type | Description | Accepted Values / Example |
95
- | ---------------- | -------- | ---------------------------------------------------------------- | ------------------------------------- |
96
- | `key` | string | Property name in row objects | `"id"`, `"name"`, `"email"` (must exist in data objects)|
97
- | `title` | string | Column header text | `"User ID"`, `"Full Name"`, `"Email Address"` |
98
- | `type` | string | Column renderer type | `"plain"` (default), `"index"` (row number), `"group"` (avatar+text), `"chip"` (badge), `"date"`, `"avatar"`, `"menu_actions"` |
99
- | `imageKey` | string | Image property for avatar/group types | `"profileImage"`, `"avatarUrl"` (path to image in data object) |
100
- | `titleKey` | string | Title property for group/avatar types | `"name"`, `"fullName"` (property key in data object) |
101
- | `subtitleKey` | string | Subtitle property for group/avatar types | `"email"`, `"department"` (property key in data object) |
102
- | `onClickDetails` | boolean | Clicking cell opens view details modal | `true`, `false` (default: `false`) |
103
- | `variant` | string | Chip styling variant | `"contained"`, `"outline"`, `"soft"` (used with `type: "chip"`) |
104
- | `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" }]` |
105
- | `defaultColor` | string | Default color for chips (if no match in chipOptions) | `"green"`, `"red"`, `"blue"`, `"yellow"`, `"purple"`, `"gray"`, etc. |
106
- | `className` | string | Custom CSS class for cell content | Tailwind classes: `"font-bold text-sm text-gray-600"` |
107
- | `format` | string | Date format pattern | `"DD MMM YYYY"`, `"YYYY-MM-DD"`, `"DD/MM/YYYY HH:mm"` (uses date-fns patterns) |
108
- | `render` | function | Custom cell renderer (overrides built-in logic) | `(row: object, rowIndex: number) => ReactNode`; e.g., `(row) => <span>{row.name.toUpperCase()}</span>` |
109
- | `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 /> }]` |
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>` |
110
104
 
111
105
  ---
112
106
 
@@ -114,41 +108,40 @@ Below is a complete reference of the public props accepted by this package (type
114
108
 
115
109
  #### Add & Edit Modal (`addModal`, `editModal`)
116
110
 
117
- | Property | Type | Required | Description | Accepted Values / Example |
118
- | --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ |
119
- | `title` | string | Yes | Modal title | `"Add New User"`, `"Edit User Profile"` |
120
- | `icon` | ReactNode | No | Icon element displayed in modal header | `<PlusIcon />`, `<EditIcon />` |
121
- | `size` | string | No | Modal width | `"sm"`, `"md"` (default), `"lg"`, `"xl"`, `"full"` |
122
- | `formClass` | string | No | Custom CSS class for form wrapper | Tailwind classes: `"grid grid-cols-12 gap-4"` |
123
- | `formFields` | array | Yes | Form field objects | Array of form field objects (see [Form Field Schema](#form-field-schema)) |
124
- | `handleSubmit` | function | Yes | Async callback on form submission | `async (formData) => Promise<{ newObject, message?: string }>` (add), `async (formData, item) => Promise<{ newObject, targetObject, message?: string }>` (edit) |
125
- | `actionButtons` | array | No | Custom action buttons | `[{ type: "submit", label: "Save", color: "primary", variant: "contained", onClick?: (e, item) => void, disabled?: boolean }]` |
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 }]` |
126
120
 
127
121
  #### Delete Modal (`deleteModal`)
128
122
 
129
- | Property | Type | Required | Description | Accepted Values / Example |
130
- | --------------- | -------- | -------- | -------------------------------- | ---------------------- |
131
- | `title` | string | No | Modal title | `"Delete User"`, `"Confirm Delete"` |
132
- | `icon` | ReactNode | No | Icon element displayed in modal header | `<TrashIcon />`, `<WarningIcon />` |
133
- | `size` | string | No | Modal width | `"sm"` (default), `"md"`, `"lg"`, `"xl"`, `"full"` |
134
- | `confirmText` | string | No | Confirmation message text | `"Are you sure you want to delete this user?"`, `"This action cannot be undone."` |
135
- | `referenceKey` | string | No | Property key to display as confirmation reference | `"name"`, `"email"` (shows the value from selected item) |
136
- | `action` | function | Yes | Async callback to delete item | `async (selectedItem) => Promise<{ targetObject }>` (returns the deleted row) |
137
- | `actionButtons` | array | No | Custom action buttons | `[{ type: "submit", label: "Delete", color: "error", variant: "contained" }]` |
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 | Type | Required | Description | Accepted Values / Example |
142
- | ----------- | --------------- | -------- | -------------------- | -------------------- |
143
- | `title` | string | Yes | Modal title | `"User Details"`, `"View Profile"` |
144
- | `icon` | ReactNode | No | Icon element displayed in modal header | `<EyeIcon />`, `<InfoIcon />` |
145
- | `size` | string | No | Modal width | `"sm"`, `"md"` (default), `"lg"`, `"xl"`, `"full"` |
146
- | `component` | React component | No | Custom component to render (receives `data` prop) | `(props) => <CustomViewComponent data={props.data} />` |
147
- | `variant` | string | No | View layout style | `"default"`, `"card"`, `"split"` |
148
- | `fields` | array | No | View field objects (if not using custom component) | Array of field objects (see [View Field Schema](#view-field-schema)) |
149
- | `styles` | object | No | Custom CSS classes for various view elements | `{ containerClass: "...", rowClass: "...", labelClass: "...", valueClass: "...", ... }` |
150
- | `modalClassNames` | object | No | Custom classes for modal structure | `{ overlay: "...", container: "...", header: "...", body: "...", footer: "...", closeButton: "..." }` |
151
- | `footer` | object | No | Footer configuration | `{ cancelButton: true, cancelText: "Close" }` |
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" }` |
152
145
 
153
146
  ---
154
147
 
@@ -158,136 +151,116 @@ Used by `modalConfig.*.formFields`, `filterConfig.fields`, and `viewModal.fields
158
151
 
159
152
  #### Common Field Properties (All Types)
160
153
 
161
- | Key | Type | Required | Description | Accepted Values / Example |
162
- | ---------------- | -------- | --- | ----------------------- | -------------------- |
163
- | `key` | string | Yes | Property name/identifier for form data | `"username"`, `"email"`, `"birth_date"` |
164
- | `label` | string | No | Human-readable label for the field | `"User Name"`, `"Email Address"`, `"Date of Birth"` |
165
- | `type` | string | Yes | Field type determining the input/renderer | `"text"`, `"number"`, `"email"`, `"password"`, `"select"`, `"checkbox"`, `"radio"`, `"switch"`, `"phone"`, `"textarea"`, `"image"`, `"video"`, `"audio"`, `"tinyEditor"`, `"group"`, `"cardGroup"` |
166
- | `required` | boolean | No | Field must have a value for form submission | `true`, `false` (default: `false`) |
167
- | `minLength` | number | No | Minimum character length (for text fields) | `5`, `10`, `50` |
168
- | `placeholder` | string | No | Placeholder text shown in empty field | `"Enter your name"`, `"user@example.com"` |
169
- | `disabled` | boolean | No | Field is disabled and read-only | `true`, `false` (default: `false`) |
170
- | `parentClass` | string | No | Custom CSS classes for field wrapper (grid) | Tailwind classes: `"col-span-6"`, `"col-span-12"` |
171
- | `defaultValue` | any | No | Initial value for the field | Depends on field type; e.g., `"John"`, `25`, `true`, `["option1", "option2"]` |
172
- | `renderCondition` | function | No | Show field based on form data | `(formData: Record<string, any>) => boolean`; e.g., `(data) => data.userType === 'admin'` |
173
- | `customValidation`| function | No | Custom validation logic | `(value: any) => boolean \| string`; return `false` for invalid, error message string, or `true` for valid |
174
- | `pattern` | string | No | Regex pattern for validation (HTML5) | Regex: `"^[a-zA-Z0-9]*$"`, `"^[0-9]{10}$"` |
175
- | `className` | string | No | Custom CSS class for input element | Tailwind classes: `"bg-gray-100 rounded-lg"` |
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"` |
176
167
 
177
168
  #### Type-Specific Properties
178
169
 
179
170
  ##### `"text"` Field
180
171
 
181
- | Property | Type | Description | Example |
182
- | ---- | --- | -------------------- | --- |
183
- | `minLength` | number | Minimum character length | `5` |
184
- | `placeholder` | string | Placeholder text | `"Enter your name"` |
185
- | `pattern` | string | Regex validation pattern | `"^[a-zA-Z ]*$"` (letters and spaces only) |
186
- | `mask` | string | Input mask pattern | `"(99) 99999-9999"` (format: 9=digit, A=letter, X=alphanumeric, *=any char) |
187
- | `maskApplyOnValue` | boolean | Apply mask to initial value | `true`, `false` (default: `true`) |
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`) |
188
177
 
189
178
  ##### `"number"` Field
190
179
 
191
- | Property | Type | Description | Example |
192
- | --- | --- | ------------- | --- |
193
- | `negativeNumberAllow` | boolean | Allow negative numbers | `true`, `false` (default: `false`) |
194
-
195
- ##### `"email"` Field
196
-
197
- | Property | Type | Description | Example |
198
- | ------ | ------- | -------------------- | --- |
199
- | `placeholder` | string | Placeholder text | `"user@example.com"` |
200
-
201
- ##### `"password"` Field
202
-
203
- | Property | Type | Description | Example |
204
- | --- | --- | -- | -- |
205
- | `minLength` | number | Minimum character length | `8` |
180
+ | Property | Type | Description | Example |
181
+ | --------------------- | ------- | ---------------------- | ---------------------------------- |
182
+ | `negativeNumberAllow` | boolean | Allow negative numbers | `true`, `false` (default: `false`) |
206
183
 
207
184
  ##### `"select"` Field
208
185
 
209
- | Property | Type | Description | Accepted Values / Example |
210
- | ----- | --- | -------------------- | -- |
211
- | `options` | array | Select options; `{ value, label, color? }[]` | `[{ value: "active", label: "Active", color: "green" }, { value: "inactive", label: "Inactive", color: "red" }]` |
212
- | `multiple` | boolean | Allow selecting multiple options | `true`, `false` (default: `false`) |
213
- | `search` | boolean | Enable searchable dropdown | `true`, `false` (default: `false`) |
214
- | `dropdownMaxHeight` | number | Max height of dropdown in pixels | `300`, `400` |
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"]` |
215
194
 
216
195
  ##### `"checkbox"` Field
217
196
 
218
- | Property | Type | Description | Accepted Values / Example |
219
- | -- | --- | -------------------- | -- |
220
- | `options` | array | Checkbox options; `{ value, label }[]` | `[{ value: "read", label: "Can Read" }, { value: "write", label: "Can Write" }]` |
221
- | `multiple` | boolean | Allow selecting multiple values | `true` (default: `true`); affects how data is stored |
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 |
222
201
 
223
202
  ##### `"radio"` Field
224
203
 
225
- | Property | Type | Description | Accepted Values / Example |
226
- | -- | -- | -------------------- | -- |
227
- | `options` | array | Radio button options; `{ value, label }[]` | `[{ value: "male", label: "Male" }, { value: "female", label: "Female" }, { value: "other", label: "Other" }]` |
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" }]` |
228
207
 
229
208
  ##### `"switch"` Field
230
209
 
231
- | Property | Type | Description | Accepted Values / Example |
232
- | -- | -- | --------- | -- |
233
- | `text` | string | Description/label shown next to switch | `"Enable notifications"`, `"Is admin"` |
234
- | `options` | array | Optional radio-like options (radio fallback) | `[{ value: "yes", label: "Yes" }, { value: "no", label: "No" }]` |
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" }]` |
235
214
 
236
215
  ##### `"phone"` Field
237
216
 
238
- | Property | Type | Description | Accepted Values / Example |
239
- | ---- | -- | --------- | - |
240
- | `countriesList` | boolean | Show country selector dropdown | `true`, `false` (default: `false`) |
241
- | `defaultCountry` | string | Default country ISO code | `"US"`, `"GB"`, `"IN"`, `"CA"`, `"AU"` |
242
- | `search` | boolean | Enable searching countries in selector | `true`, `false` (default: `false`) |
243
- | `placeholder` | string | Placeholder text | `"+1 (555) 000-0000"` |
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"` |
244
223
 
245
224
  ##### `"textarea"` Field
246
225
 
247
- | Property | Type | Description | Accepted Values / Example |
248
- | - | -- | -- | - |
249
- | `rows` | number | Number of visible rows | `3`, `5`, `10` |
226
+ | Property | Type | Description | Accepted Values / Example |
227
+ | -------- | ------ | ---------------------- | ------------------------- |
228
+ | `rows` | number | Number of visible rows | `3`, `5`, `10` |
250
229
 
251
230
  ##### `"image"` Field
252
231
 
253
- | Property | Type | Description | Accepted Values / Example |
254
- | - | -- | -- | - |
255
- | `accept` | string | MIME type filter | `"image/*"` (default), `"image/jpeg,image/png"` |
256
- | `dragDrop` | boolean | Enable drag-and-drop upload | `true`, `false` (default: `false`) |
257
- | `cropImage` | boolean | Enable image cropping modal | `true`, `false` (default: `false`) |
258
- | `aspectRatio` | number | Crop aspect ratio (width:height) | `1` (1:1 square), `16/9`, `4/3`, `1.5` |
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` |
259
240
 
260
241
  ##### `"video"` Field
261
242
 
262
- | Property | Type | Description | Accepted Values / Example |
263
- | - | -- | -- | - |
264
- | `accept` | string | MIME type filter | `"video/*"` (default), `"video/mp4,video/webm"` |
265
- | `dragDrop` | boolean | Enable drag-and-drop upload | `true`, `false` (default: `false`) |
266
- | `maxSize` | number | Maximum file size in bytes | `5242880` (5MB), `10485760` (10MB) |
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) |
267
248
 
268
249
  ##### `"audio"` Field
269
250
 
270
- | Property | Type | Description | Accepted Values / Example |
271
- | - | -- | -- | - |
272
- | `accept` | string | MIME type filter | `"audio/*"` (default), `"audio/mpeg,audio/wav"` |
273
- | `dragDrop` | boolean | Enable drag-and-drop upload | `true`, `false` (default: `false`) |
274
- | `maxSize` | number | Maximum file size in bytes | `5242880` (5MB), `10485760` (10MB) |
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) |
275
256
 
276
257
  ##### `"tinyEditor"` Field
277
258
 
278
- | Property | Type | Description | Accepted Values / Example |
279
- | --------- | ------ | ------------ | - |
280
- | `editorKey` | string | TinyMCE API key (required) | Get from `import.meta.env.VITE_EDITOR_KEY` or pass directly |
281
- | `fontFamily` | string | Default font family in editor | `"Arial"`, `"Georgia"`, `"Courier New"` |
282
- | `height` | number | Editor height in pixels | `300`, `500` |
283
- | `imageUploadHandler` | function | Async image upload handler | `async (blobInfo) => Promise<string>` (returns image URL) |
284
-
285
- ##### `"group"` & `"cardGroup"` Fields
286
-
287
- | Property | Type | Description | Accepted Values / Example |
288
- | -- | -- | -- | - |
289
- | `renderType` | string | Rendering type for grouped fields | `"default"`, `"card"`, or custom layout type |
290
- | (nested fields) | array | Child form fields within group | Array of FormField objects |
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` |
291
264
 
292
265
  ---
293
266
 
@@ -295,173 +268,22 @@ Used by `modalConfig.*.formFields`, `filterConfig.fields`, and `viewModal.fields
295
268
 
296
269
  Used by `viewModal.fields` to display data in view/details modals.
297
270
 
298
- | Key | Type | Description | Accepted Values / Example |
299
- | ----- | -- | - | - |
300
- | `key` | string | Property name to display | `"username"`, `"email"`, `"birth_date"` |
301
- | `label` | string | Display label for the field | `"User Name"`, `"Email Address"` |
302
- | `type` | string | Display type (similar to form field types) | `"text"`, `"date"`, `"chip"`, `"image"`, `"avatar"`, `"group"`, `"cardGroup"` |
303
- | `format` | string | Date format pattern | `"DD MMM YYYY"`, `"YYYY-MM-DD"`, `"DD/MM/YYYY HH:mm"` |
304
- | `imageKey` | string | Image property for avatar types | `"profileImage"`, `"avatarUrl"` |
305
- | `titleKey` | string | Title property for group/avatar types | `"name"`, `"fullName"` |
306
- | `subtitleKey` | string | Subtitle property for group/avatar types | `"email"`, `"department"` |
307
- | `variant` | string | Chip display variant | `"contained"`, `"outline"`, `"soft"` |
308
- | `chipOptions` | array | Map values to chip labels and colors | `[{ value: "active", label: "Active", color: "green" }]` |
309
- | `defaultColor` | string | Default chip color | `"green"`, `"red"`, `"blue"`, `"yellow"`, `"purple"`, `"gray"` |
310
- | `className` | string | Custom CSS class for display element | Tailwind classes |
311
- | `blockClass` | string | Custom CSS class for field block/wrapper | Tailwind classes |
312
- | `icon` | ReactNode | Icon element to display with field | `<UserIcon />`, `<MailIcon />` |
313
- | `renderCondition` | function | Show field based on data | `(data: Record<string, any>) => boolean` |
314
-
315
- ---
316
-
317
- ### Standalone UI Components
318
-
319
- In addition to the main CRUD component, the library exports reusable UI components that can be used independently:
320
-
321
- #### Button Component
322
-
323
- ```jsx
324
- import { Button } from 'react-admin-crud-manager';
325
-
326
- // Basic button
327
- <Button onClick={() => console.log('Clicked')}>Click Me</Button>
328
-
329
- // Variants
330
- <Button variant="contained" color="primary">Primary</Button>
331
- <Button variant="outlined" color="success">Success</Button>
332
- <Button variant="text" color="error">Error</Button>
333
-
334
- // Sizes
335
- <Button size="sm">Small</Button>
336
- <Button size="md">Medium</Button>
337
- <Button size="lg">Large</Button>
338
-
339
- // States
340
- <Button disabled>Disabled</Button>
341
- <Button fullWidth>Full Width</Button>
342
-
343
- // Submit button
344
- <Button type="submit">Submit Form</Button>
345
- ```
346
-
347
- | Prop | Type | Default | Description | Accepted Values |
348
- | ----------- | -------- | ----------- | ----------------------------------------------- | ----------- |
349
- | `variant` | string | `contained` | Style variant | `"contained"`, `"outlined"`, `"text"` |
350
- | `color` | string | `primary` | Button color theme | `"primary"`, `"success"`, `"error"`, `"warning"`, `"default"` |
351
- | `size` | string | `md` | Button size | `"sm"`, `"md"`, `"lg"`, `"xl"` |
352
- | `fullWidth` | boolean | `false` | Stretch to full width | `true`, `false` |
353
- | `className` | string | — | Additional CSS classes | Tailwind classes |
354
- | `onClick` | function | — | Click handler (can be async) | `(e) => void \| Promise<any>` |
355
- | `type` | string | `button` | HTML button type | `"button"`, `"submit"`, `"reset"` |
356
- | `disabled` | boolean | `false` | Disable interaction | `true`, `false` |
357
-
358
- #### Chip Component
359
-
360
- ```jsx
361
- import { Chip } from 'react-admin-crud-manager';
362
-
363
- // Basic chip
364
- <Chip label="Active" />
365
-
366
- // Variants with colors
367
- <Chip label="Active" color="green" variant="contained" />
368
- <Chip label="Pending" color="yellow" variant="outline" />
369
- <Chip label="Inactive" color="red" variant="soft" />
370
-
371
- // All available colors
372
- <Chip label="Blue" color="blue" />
373
- <Chip label="Teal" color="teal" />
374
- <Chip label="Purple" color="purple" />
375
- <Chip label="Green" color="green" />
376
- <Chip label="Red" color="red" />
377
- <Chip label="Yellow" color="yellow" />
378
- <Chip label="Gray" color="gray" />
379
- ```
380
-
381
- | Prop | Type | Default | Description | Accepted Values |
382
- | --------- | ------ | ----------- | ------------------------------------- | --------- |
383
- | `label` | string | Required | Badge text content | Any string |
384
- | `variant` | string | `contained` | Display style | `"contained"`, `"outline"`, `"soft"` |
385
- | `color` | string | `blue` | Color scheme | `"blue"`, `"teal"`, `"purple"`, `"green"`, `"red"`, `"yellow"`, `"gray"` |
386
-
387
- #### Modal Component
388
-
389
- ```jsx
390
- import { Modal } from 'react-admin-crud-manager';
391
- import { AlertIcon } from 'lucide-react';
392
-
393
- // Basic modal
394
- <Modal
395
- isOpen={isOpen}
396
- onClose={() => setIsOpen(false)}
397
- title="Confirm Action"
398
- icon={<AlertIcon />}
399
- actionButtons={[
400
- { type: "submit", label: "Confirm", color: "primary" },
401
- { type: "cancel", label: "Cancel", color: "default" }
402
- ]}
403
- >
404
- <p>Are you sure you want to continue?</p>
405
- </Modal>
406
-
407
- // Modal sizes
408
- <Modal isOpen={true} size="sm" title="Small Modal" />
409
- <Modal isOpen={true} size="md" title="Medium Modal" />
410
- <Modal isOpen={true} size="lg" title="Large Modal" />
411
- <Modal isOpen={true} size="xl" title="Extra Large Modal" />
412
- <Modal isOpen={true} size="full" title="Full Screen Modal" />
413
- ```
414
-
415
- | Prop | Type | Description | Accepted Values |
416
- | --------------- | ---------- | -------------------------------------------- | --------- --– |
417
- | `isOpen` | boolean | Show/hide modal | `true`, `false` |
418
- | `onClose` | function | Callback when modal closes | `() => void` |
419
- | `icon` | ReactNode | Icon displayed in header | Any React component or JSX element |
420
- | `title` | string | Modal title text | Any string |
421
- | `size` | string | Modal width | `"sm"`, `"md"`, `"lg"`, `"xl"`, `"full"` |
422
- | `actionButtons` | array | Footer action buttons | Array of `{ type, label, color, variant, onClick, disabled }` |
423
- | `loadingBtn` | boolean | Show loading spinner on action button | `true`, `false` |
424
- | `children` | ReactNode | Modal content | Any React components |
425
-
426
- #### FilterDrawer Component
427
-
428
- ```jsx
429
- import { FilterDrawer } from 'react-admin-crud-manager';
430
-
431
- <FilterDrawer
432
- isOpen={isOpen}
433
- onClose={() => setIsOpen(false)}
434
- config={{
435
- fields: [
436
- {
437
- key: "status",
438
- label: "Status",
439
- type: "select",
440
- options: [
441
- { value: "active", label: "Active" },
442
- { value: "inactive", label: "Inactive" }
443
- ]
444
- },
445
- {
446
- key: "dateFrom",
447
- label: "From Date",
448
- type: "date"
449
- }
450
- ]
451
- }}
452
- onApply={(filters) => {
453
- console.log("Applied filters:", filters);
454
- setIsOpen(false);
455
- }}
456
- />
457
- ```
458
-
459
- | Prop | Type | Description | Accepted Values |
460
- | --------- | -------- | ---------------- | --|
461
- | `isOpen` | boolean | Show/hide filter drawer | `true`, `false` |
462
- | `onClose` | function | Callback when drawer closes | `() => void` |
463
- | `config` | object | Filter field configuration | `{ fields: [...] }` (uses Form Field Schema) |
464
- | `onApply` | function | Callback with applied filter values | `(filters: object) => void` |
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` |
465
287
 
466
288
  ---
467
289
 
@@ -503,8 +325,8 @@ const formFields = [
503
325
  type: "select",
504
326
  options: [
505
327
  { value: "admin", label: "Administrator" },
506
- { value: "user", label: "Regular User" }
507
- ]
328
+ { value: "user", label: "Regular User" },
329
+ ],
508
330
  },
509
331
  {
510
332
  key: "adminLevel",
@@ -513,15 +335,15 @@ const formFields = [
513
335
  renderCondition: (formData) => formData.userType === "admin", // Only show if userType is admin
514
336
  options: [
515
337
  { value: "superadmin", label: "Super Admin" },
516
- { value: "moderator", label: "Moderator" }
517
- ]
338
+ { value: "moderator", label: "Moderator" },
339
+ ],
518
340
  },
519
341
  {
520
342
  key: "department",
521
343
  label: "Department",
522
344
  type: "text",
523
345
  renderCondition: (formData) => formData.userType === "user", // Only show if userType is user
524
- }
346
+ },
525
347
  ];
526
348
  ```
527
349
 
@@ -541,7 +363,7 @@ const formFields = [
541
363
  return "Email must be a company email"; // Error message
542
364
  }
543
365
  return true; // Valid
544
- }
366
+ },
545
367
  },
546
368
  {
547
369
  key: "age",
@@ -551,8 +373,8 @@ const formFields = [
551
373
  if (value < 18) return "Must be 18 or older";
552
374
  if (value > 120) return "Please enter a valid age";
553
375
  return true;
554
- }
555
- }
376
+ },
377
+ },
556
378
  ];
557
379
  ```
558
380
 
@@ -648,8 +470,8 @@ Use a fully custom component for the view modal:
648
470
  const CustomUserView = ({ data }) => (
649
471
  <div className="space-y-4">
650
472
  <div className="flex items-center gap-4">
651
- <img
652
- src={data.avatarUrl}
473
+ <img
474
+ src={data.avatarUrl}
653
475
  alt={data.name}
654
476
  className="w-16 h-16 rounded-full"
655
477
  />
@@ -675,9 +497,9 @@ const config = {
675
497
  modalConfig: {
676
498
  viewModal: {
677
499
  title: "User Details",
678
- component: CustomUserView // Use custom component
679
- }
680
- }
500
+ component: CustomUserView, // Use custom component
501
+ },
502
+ },
681
503
  };
682
504
  ```
683
505
 
@@ -686,6 +508,7 @@ const config = {
686
508
  Handle clicks on table rows:
687
509
 
688
510
  ```js
511
+ // For a custom function
689
512
  const config = {
690
513
  tableConfig: {
691
514
  table_head: [...],
@@ -696,6 +519,16 @@ const config = {
696
519
  }
697
520
  }
698
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
+
699
532
  ```
700
533
 
701
534
  ### 8. Server-Side Filtering
@@ -707,7 +540,7 @@ const config = {
707
540
  tableConfig: {
708
541
  filter: {
709
542
  enabled: true,
710
- useServerSideFilters: true // Enable server-side filtering
543
+ useServerSideFilters: true, // Enable server-side filtering
711
544
  },
712
545
  filterConfig: {
713
546
  fields: [
@@ -717,25 +550,21 @@ const config = {
717
550
  type: "select",
718
551
  options: [
719
552
  { value: "active", label: "Active" },
720
- { value: "inactive", label: "Inactive" }
721
- ]
553
+ { value: "inactive", label: "Inactive" },
554
+ ],
722
555
  },
723
556
  {
724
557
  key: "createdFrom",
725
558
  label: "Created From",
726
- type: "date"
559
+ type: "date",
727
560
  },
728
561
  {
729
562
  key: "createdTo",
730
563
  label: "Created To",
731
- type: "date"
732
- }
733
- ]
564
+ type: "date",
565
+ },
566
+ ],
734
567
  },
735
- onFilterApply: (filters) => {
736
- console.log("Filters applied:", filters);
737
- // Filters will be passed to fetchData as additional params
738
- }
739
568
  },
740
569
  fetchData: async ({
741
570
  search,
@@ -752,11 +581,11 @@ const config = {
752
581
  page: current_page,
753
582
  sort_by,
754
583
  sort_order,
755
- ...filters // Include filters in API call
756
- }
584
+ ...filters, // Include filters in API call
585
+ },
757
586
  });
758
587
  return { data: resp.items, pagination: resp.pagination };
759
- }
588
+ },
760
589
  };
761
590
  ```
762
591
 
@@ -772,26 +601,27 @@ const formFields = [
772
601
  type: "text",
773
602
  mask: "(99) 99999-9999", // Mask pattern
774
603
  maskApplyOnValue: true, // Apply mask to default value
775
- placeholder: "(00) 00000-0000"
604
+ placeholder: "(00) 00000-0000",
776
605
  },
777
606
  {
778
607
  key: "zipCode",
779
608
  label: "ZIP Code",
780
609
  type: "text",
781
610
  mask: "99999-999", // Pattern: 9 = digit, A = letter, X = alphanumeric, * = any
782
- placeholder: "00000-000"
611
+ placeholder: "00000-000",
783
612
  },
784
613
  {
785
614
  key: "creditCard",
786
615
  label: "Credit Card",
787
616
  type: "text",
788
617
  mask: "9999 9999 9999 9999",
789
- placeholder: "0000 0000 0000 0000"
790
- }
618
+ placeholder: "0000 0000 0000 0000",
619
+ },
791
620
  ];
792
621
  ```
793
622
 
794
623
  **Mask Pattern Reference:**
624
+
795
625
  - `9` — Digit (0-9)
796
626
  - `A` — Letter (a-zA-Z)
797
627
  - `X` — Alphanumeric (a-zA-Z0-9)
@@ -810,7 +640,7 @@ const formFields = [
810
640
  type: "image",
811
641
  cropImage: true, // Enable cropping modal
812
642
  aspectRatio: 1, // 1:1 square ratio
813
- dragDrop: true
643
+ dragDrop: true,
814
644
  },
815
645
  {
816
646
  key: "bannerImage",
@@ -818,40 +648,12 @@ const formFields = [
818
648
  type: "image",
819
649
  cropImage: true,
820
650
  aspectRatio: 16 / 9, // 16:9 widescreen ratio
821
- dragDrop: true
822
- }
651
+ dragDrop: true,
652
+ },
823
653
  ];
824
654
  ```
825
655
 
826
- ### 11. Notifications/Snackbar
827
-
828
- Display success/error messages automatically:
829
-
830
- Notifications are handled internally by the library using the `notistack` library. Success and error messages are shown automatically:
831
-
832
- - **Success messages**: Shown when add/edit/delete operations complete successfully
833
- - **Error messages**: Shown when operations fail
834
- - **Custom messages**: Handlers can return `{ newObject, message: "Custom message" }`
835
-
836
- ```js
837
- const config = {
838
- modalConfig: {
839
- addModal: {
840
- title: "Add User",
841
- formFields: [...],
842
- handleSubmit: async (formData) => {
843
- const resp = await api.post("/users", formData);
844
- return {
845
- newObject: resp.user,
846
- message: "User created successfully!" // Custom success message
847
- };
848
- }
849
- }
850
- }
851
- };
852
- ```
853
-
854
- ### 12. Sortable Columns
656
+ ### 11. Sortable Columns
855
657
 
856
658
  Configure sorting with custom options and auto-generation:
857
659
 
@@ -868,14 +670,14 @@ const config = {
868
670
  onChange: (payload) => {
869
671
  console.log("Sort changed:", payload);
870
672
  // payload contains: { value, option, key, order, type }
871
- }
673
+ },
872
674
  },
873
675
  table_head: [
874
676
  { key: "name", title: "Name" },
875
677
  { key: "email", title: "Email" },
876
- { key: "createdAt", title: "Created Date" }
877
- ]
878
- }
678
+ { key: "createdAt", title: "Created Date" },
679
+ ],
680
+ },
879
681
  };
880
682
  ```
881
683
 
@@ -884,11 +686,11 @@ const config = {
884
686
  #### Example 1: Minimal Client-Side CRUD
885
687
 
886
688
  ```js
887
- import Crud from 'react-admin-crud-manager';
689
+ import Crud from "react-admin-crud-manager";
888
690
 
889
691
  const users = [
890
692
  { id: 1, name: "John Doe", email: "john@example.com", status: "active" },
891
- { id: 2, name: "Jane Smith", email: "jane@example.com", status: "inactive" }
693
+ { id: 2, name: "Jane Smith", email: "jane@example.com", status: "inactive" },
892
694
  ];
893
695
 
894
696
  function App() {
@@ -901,13 +703,18 @@ function App() {
901
703
  { key: "id", title: "ID", type: "index" },
902
704
  { key: "name", title: "Name" },
903
705
  { key: "email", title: "Email" },
904
- { key: "status", title: "Status", type: "chip", chipOptions: [
905
- { value: "active", label: "Active", color: "green" },
906
- { value: "inactive", label: "Inactive", color: "red" }
907
- ]}
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
+ },
908
715
  ],
909
- search: { enabled: true },
910
- pagination: { enabled: true, rows_per_page: 10 }
716
+ search: { enabled: true, searchKeys: ["name", "email"] },
717
+ pagination: { enabled: true },
911
718
  },
912
719
  modalConfig: {
913
720
  addModal: {
@@ -921,15 +728,18 @@ function App() {
921
728
  type: "select",
922
729
  options: [
923
730
  { value: "active", label: "Active" },
924
- { value: "inactive", label: "Inactive" }
925
- ]
926
- }
731
+ { value: "inactive", label: "Inactive" },
732
+ ],
733
+ },
927
734
  ],
928
735
  handleSubmit: async (formData) => ({
929
- newObject: { ...formData, id: Math.max(...users.map(u => u.id)) + 1 }
930
- })
931
- }
932
- }
736
+ newObject: {
737
+ ...formData,
738
+ id: Math.max(...users.map((u) => u.id)) + 1,
739
+ },
740
+ }),
741
+ },
742
+ },
933
743
  };
934
744
 
935
745
  return <Crud config={config} />;
@@ -941,12 +751,12 @@ export default App;
941
751
  #### Example 2: Server-Side CRUD with Advanced Features
942
752
 
943
753
  ```js
944
- import Crud from 'react-admin-crud-manager';
945
- import axios from 'axios';
946
- import { PlusIcon, EditIcon, TrashIcon } from 'lucide-react';
754
+ import Crud from "react-admin-crud-manager";
755
+ import axios from "axios";
756
+ import { PlusIcon, EditIcon, TrashIcon } from "lucide-react";
947
757
 
948
758
  function App() {
949
- const api = axios.create({ baseURL: 'https://api.example.com' });
759
+ const api = axios.create({ baseURL: "https://api.example.com" });
950
760
 
951
761
  const config = {
952
762
  title: "Products",
@@ -960,7 +770,7 @@ function App() {
960
770
  sort_order,
961
771
  category,
962
772
  minPrice,
963
- maxPrice
773
+ maxPrice,
964
774
  }) => {
965
775
  const resp = await api.get("/products", {
966
776
  params: {
@@ -971,8 +781,8 @@ function App() {
971
781
  sort_order,
972
782
  category,
973
783
  minPrice,
974
- maxPrice
975
- }
784
+ maxPrice,
785
+ },
976
786
  });
977
787
  return {
978
788
  data: resp.data.items,
@@ -980,16 +790,26 @@ function App() {
980
790
  current_page: resp.data.page,
981
791
  rows_per_page: resp.data.limit,
982
792
  total_pages: resp.data.totalPages,
983
- total_records: resp.data.total
984
- }
793
+ total_records: resp.data.total,
794
+ },
985
795
  };
986
796
  },
987
797
  tableConfig: {
988
798
  table_head: [
989
799
  { key: "id", title: "ID", type: "index" },
990
- { key: "image", title: "Image", type: "avatar", imageKey: "image", titleKey: "name" },
800
+ {
801
+ key: "image",
802
+ title: "Image",
803
+ type: "avatar",
804
+ imageKey: "image",
805
+ titleKey: "name",
806
+ },
991
807
  { key: "name", title: "Product Name" },
992
- { key: "price", title: "Price", render: (row) => `$${row.price.toFixed(2)}` },
808
+ {
809
+ key: "price",
810
+ title: "Price",
811
+ render: (row) => `$${row.price.toFixed(2)}`,
812
+ },
993
813
  {
994
814
  key: "category",
995
815
  title: "Category",
@@ -997,11 +817,14 @@ function App() {
997
817
  variant: "soft",
998
818
  chipOptions: [
999
819
  { value: "electronics", label: "Electronics", color: "blue" },
1000
- { value: "clothing", label: "Clothing", color: "purple" }
1001
- ]
1002
- }
820
+ { value: "clothing", label: "Clothing", color: "purple" },
821
+ ],
822
+ },
1003
823
  ],
1004
- search: { enabled: true, useServerSideSearch: true, searchKeys: ["name", "sku"] },
824
+ search: {
825
+ enabled: true,
826
+ useServerSideSearch: true,
827
+ },
1005
828
  filter: { enabled: true, useServerSideFilters: true },
1006
829
  pagination: { enabled: true, useServerSidePagination: true },
1007
830
  sort: { enabled: true, useServerSideSorting: true, autoGenerate: true },
@@ -1011,8 +834,8 @@ function App() {
1011
834
  fields: [
1012
835
  { label: "ID", key: "id" },
1013
836
  { label: "Name", key: "name" },
1014
- { label: "Price", key: "price" }
1015
- ]
837
+ { label: "Price", key: "price" },
838
+ ],
1016
839
  },
1017
840
  filterConfig: {
1018
841
  fields: [
@@ -1022,13 +845,13 @@ function App() {
1022
845
  type: "select",
1023
846
  options: [
1024
847
  { value: "electronics", label: "Electronics" },
1025
- { value: "clothing", label: "Clothing" }
1026
- ]
848
+ { value: "clothing", label: "Clothing" },
849
+ ],
1027
850
  },
1028
851
  { key: "minPrice", label: "Min Price", type: "number" },
1029
- { key: "maxPrice", label: "Max Price", type: "number" }
1030
- ]
1031
- }
852
+ { key: "maxPrice", label: "Max Price", type: "number" },
853
+ ],
854
+ },
1032
855
  },
1033
856
  modalConfig: {
1034
857
  addModal: {
@@ -1036,8 +859,20 @@ function App() {
1036
859
  size: "lg",
1037
860
  icon: <PlusIcon />,
1038
861
  formFields: [
1039
- { key: "name", label: "Product Name", type: "text", required: true, parentClass: "col-span-12" },
1040
- { key: "price", label: "Price", type: "number", required: true, parentClass: "col-span-6" },
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
+ },
1041
876
  {
1042
877
  key: "category",
1043
878
  label: "Category",
@@ -1046,11 +881,23 @@ function App() {
1046
881
  parentClass: "col-span-6",
1047
882
  options: [
1048
883
  { value: "electronics", label: "Electronics" },
1049
- { value: "clothing", label: "Clothing" }
1050
- ]
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",
1051
900
  },
1052
- { key: "stock", label: "Stock", type: "number", required: true, parentClass: "col-span-6" },
1053
- { key: "description", label: "Description", type: "textarea", rows: 4, parentClass: "col-span-12" },
1054
901
  {
1055
902
  key: "image",
1056
903
  label: "Product Image",
@@ -1058,37 +905,67 @@ function App() {
1058
905
  cropImage: true,
1059
906
  aspectRatio: 1,
1060
907
  dragDrop: true,
1061
- parentClass: "col-span-12"
1062
- }
908
+ parentClass: "col-span-12",
909
+ },
1063
910
  ],
1064
911
  handleSubmit: async (formData) => {
1065
912
  const resp = await api.post("/products", formData);
1066
- return { newObject: resp.data, message: "Product added successfully!" };
1067
- }
913
+ return {
914
+ newObject: resp.data,
915
+ message: "Product added successfully!",
916
+ };
917
+ },
1068
918
  },
1069
919
  editModal: {
1070
920
  title: "Edit Product",
1071
921
  size: "lg",
1072
922
  icon: <EditIcon />,
1073
923
  formFields: [
1074
- { key: "name", label: "Product Name", type: "text", required: true, parentClass: "col-span-12" },
1075
- { key: "price", label: "Price", type: "number", required: true, parentClass: "col-span-6" },
1076
- { key: "stock", label: "Stock", type: "number", required: true, parentClass: "col-span-6" }
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
+ },
1077
945
  ],
1078
946
  handleSubmit: async (formData, item) => {
1079
947
  const resp = await api.put(`/products/${item.id}`, formData);
1080
948
  return { newObject: resp.data, targetObject: item };
1081
- }
949
+ },
1082
950
  },
1083
951
  deleteModal: {
1084
952
  title: "Delete Product",
1085
953
  icon: <TrashIcon />,
1086
954
  confirmText: "Are you sure you want to delete this product?",
1087
955
  referenceKey: "name",
1088
- action: async (item) => {
1089
- await api.delete(`/products/${item.id}`);
1090
- return { targetObject: item };
1091
- }
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
+ ],
1092
969
  },
1093
970
  viewModal: {
1094
971
  title: "Product Details",
@@ -1098,10 +975,10 @@ function App() {
1098
975
  { key: "name", label: "Product Name" },
1099
976
  { key: "price", label: "Price" },
1100
977
  { key: "stock", label: "Stock" },
1101
- { key: "category", label: "Category", type: "chip" }
1102
- ]
1103
- }
1104
- }
978
+ { key: "category", label: "Category", type: "chip" },
979
+ ],
980
+ },
981
+ },
1105
982
  };
1106
983
 
1107
984
  return <Crud config={config} />;
@@ -1120,8 +997,8 @@ const config = {
1120
997
  { key: "id", title: "ID", type: "index" },
1121
998
  { key: "name", title: "Full Name" },
1122
999
  { key: "email", title: "Email" },
1123
- { key: "userType", title: "Type", type: "chip" }
1124
- ]
1000
+ { key: "userType", title: "Type", type: "chip" },
1001
+ ],
1125
1002
  },
1126
1003
  modalConfig: {
1127
1004
  addModal: {
@@ -1138,7 +1015,7 @@ const config = {
1138
1015
  return "Please enter your full name";
1139
1016
  }
1140
1017
  return true;
1141
- }
1018
+ },
1142
1019
  },
1143
1020
  {
1144
1021
  key: "email",
@@ -1151,7 +1028,7 @@ const config = {
1151
1028
  return "Must use company email";
1152
1029
  }
1153
1030
  return true;
1154
- }
1031
+ },
1155
1032
  },
1156
1033
  {
1157
1034
  key: "phone",
@@ -1159,7 +1036,7 @@ const config = {
1159
1036
  type: "phone",
1160
1037
  countriesList: true,
1161
1038
  mask: "(99) 99999-9999",
1162
- parentClass: "col-span-12"
1039
+ parentClass: "col-span-12",
1163
1040
  },
1164
1041
  {
1165
1042
  key: "userType",
@@ -1169,8 +1046,8 @@ const config = {
1169
1046
  parentClass: "col-span-6",
1170
1047
  options: [
1171
1048
  { value: "admin", label: "Administrator" },
1172
- { value: "user", label: "Regular User" }
1173
- ]
1049
+ { value: "user", label: "Regular User" },
1050
+ ],
1174
1051
  },
1175
1052
  {
1176
1053
  key: "adminLevel",
@@ -1180,8 +1057,8 @@ const config = {
1180
1057
  renderCondition: (data) => data.userType === "admin",
1181
1058
  options: [
1182
1059
  { value: "superadmin", label: "Super Admin" },
1183
- { value: "moderator", label: "Moderator" }
1184
- ]
1060
+ { value: "moderator", label: "Moderator" },
1061
+ ],
1185
1062
  },
1186
1063
  {
1187
1064
  key: "permissions",
@@ -1193,19 +1070,19 @@ const config = {
1193
1070
  options: [
1194
1071
  { value: "read", label: "Can Read" },
1195
1072
  { value: "write", label: "Can Write" },
1196
- { value: "delete", label: "Can Delete" }
1197
- ]
1198
- }
1073
+ { value: "delete", label: "Can Delete" },
1074
+ ],
1075
+ },
1199
1076
  ],
1200
1077
  handleSubmit: async (formData) => {
1201
1078
  const resp = await api.post("/users", formData);
1202
1079
  return {
1203
1080
  newObject: resp.data,
1204
- message: `User ${formData.name} created successfully!`
1081
+ message: `User ${formData.name} created successfully!`,
1205
1082
  };
1206
- }
1207
- }
1208
- }
1083
+ },
1084
+ },
1085
+ },
1209
1086
  };
1210
1087
  ```
1211
1088
 
@@ -1229,16 +1106,16 @@ const config = {
1229
1106
  const newUser = { ...formData, id: Date.now() };
1230
1107
  setUsers([...users, newUser]);
1231
1108
  return { newObject: newUser };
1232
- }
1109
+ },
1233
1110
  },
1234
1111
  editModal: {
1235
1112
  handleSubmit: async (formData, item) => {
1236
1113
  const updated = { ...item, ...formData };
1237
- setUsers(users.map(u => u.id === item.id ? updated : u));
1114
+ setUsers(users.map((u) => (u.id === item.id ? updated : u)));
1238
1115
  return { newObject: updated, targetObject: item };
1239
- }
1240
- }
1241
- }
1116
+ },
1117
+ },
1118
+ },
1242
1119
  };
1243
1120
  ```
1244
1121
 
@@ -1254,8 +1131,8 @@ const fetchData = async (params) => {
1254
1131
  current_page: resp.data.meta.currentPage,
1255
1132
  rows_per_page: resp.data.meta.perPage,
1256
1133
  total_pages: resp.data.meta.totalPages,
1257
- total_records: resp.data.meta.total
1258
- }
1134
+ total_records: resp.data.meta.total,
1135
+ },
1259
1136
  };
1260
1137
  };
1261
1138
  ```
@@ -1268,7 +1145,7 @@ const handleSubmit = async (formData) => {
1268
1145
  const resp = await api.post("/items", formData);
1269
1146
  return {
1270
1147
  newObject: resp.data,
1271
- message: "Item created successfully!" // Optional success message
1148
+ message: "Item created successfully!", // Optional success message
1272
1149
  };
1273
1150
  } catch (error) {
1274
1151
  // Throw error to trigger snackbar error notification
@@ -1288,21 +1165,20 @@ const formFields = [
1288
1165
  type: "select",
1289
1166
  options: [
1290
1167
  { value: "personal", label: "Personal" },
1291
- { value: "business", label: "Business" }
1292
- ]
1168
+ { value: "business", label: "Business" },
1169
+ ],
1293
1170
  },
1294
1171
  {
1295
1172
  key: "companyName",
1296
1173
  label: "Company Name",
1297
1174
  type: "text",
1298
1175
  renderCondition: (data) => data.type === "business",
1299
- required: (data) => data.type === "business"
1300
1176
  },
1301
1177
  {
1302
1178
  key: "phone",
1303
1179
  type: "phone",
1304
- renderCondition: (data) => data.type === "personal"
1305
- }
1180
+ renderCondition: (data) => data.type === "personal",
1181
+ },
1306
1182
  ];
1307
1183
  ```
1308
1184
 
@@ -1322,7 +1198,7 @@ const tableConfig = {
1322
1198
  <p className="text-xs text-gray-500">{row.email}</p>
1323
1199
  </div>
1324
1200
  </div>
1325
- )
1201
+ ),
1326
1202
  },
1327
1203
  {
1328
1204
  key: "metrics",
@@ -1333,9 +1209,9 @@ const tableConfig = {
1333
1209
  {row.completed}%
1334
1210
  </span>
1335
1211
  </div>
1336
- )
1337
- }
1338
- ]
1212
+ ),
1213
+ },
1214
+ ],
1339
1215
  };
1340
1216
  ```
1341
1217
 
@@ -1346,12 +1222,12 @@ const formFields = [
1346
1222
  {
1347
1223
  key: "email",
1348
1224
  type: "email",
1349
- customValidation: async (value) => {
1225
+ customValidation: (value) => {
1350
1226
  // Async validation example
1351
- const exists = await checkEmailExists(value);
1227
+ const exists = checkEmailExists(value);
1352
1228
  if (exists) return "Email already in use";
1353
1229
  return true;
1354
- }
1230
+ },
1355
1231
  },
1356
1232
  {
1357
1233
  key: "password",
@@ -1361,8 +1237,8 @@ const formFields = [
1361
1237
  if (!/[A-Z]/.test(value)) return "Must contain uppercase letter";
1362
1238
  if (!/[0-9]/.test(value)) return "Must contain a number";
1363
1239
  return true;
1364
- }
1365
- }
1240
+ },
1241
+ },
1366
1242
  ];
1367
1243
  ```
1368
1244
 
@@ -1385,20 +1261,20 @@ const config = {
1385
1261
  type: "image",
1386
1262
  dragDrop: true,
1387
1263
  cropImage: true,
1388
- aspectRatio: 1
1389
- }
1264
+ aspectRatio: 1,
1265
+ },
1390
1266
  ],
1391
1267
  handleSubmit: async (formData) => {
1392
1268
  // Image data includes base64 or file data
1393
1269
  const imageUrl = await handleImageUpload(formData.image);
1394
1270
  const resp = await api.post("/items", {
1395
1271
  ...formData,
1396
- image: imageUrl
1272
+ image: imageUrl,
1397
1273
  });
1398
1274
  return { newObject: resp.data };
1399
- }
1400
- }
1401
- }
1275
+ },
1276
+ },
1277
+ },
1402
1278
  };
1403
1279
  ```
1404
1280
 
@@ -1414,15 +1290,15 @@ const viewModal = {
1414
1290
  renderCondition: (data) => data.status !== null,
1415
1291
  chipOptions: [
1416
1292
  { value: "active", label: "Active", color: "green" },
1417
- { value: "suspended", label: "Suspended", color: "red" }
1418
- ]
1293
+ { value: "suspended", label: "Suspended", color: "red" },
1294
+ ],
1419
1295
  },
1420
1296
  {
1421
1297
  key: "suspendReason",
1422
1298
  label: "Suspension Reason",
1423
- renderCondition: (data) => data.status === "suspended" // Only show if suspended
1424
- }
1425
- ]
1299
+ renderCondition: (data) => data.status === "suspended", // Only show if suspended
1300
+ },
1301
+ ],
1426
1302
  };
1427
1303
  ```
1428
1304
 
@@ -1434,16 +1310,21 @@ const tableConfig = {
1434
1310
  enabled: true,
1435
1311
  useServerSideSorting: true,
1436
1312
  options: [
1437
- { value: "recent", label: "Most Recent", key: "createdAt", order: "desc" },
1313
+ {
1314
+ value: "recent",
1315
+ label: "Most Recent",
1316
+ key: "createdAt",
1317
+ order: "desc",
1318
+ },
1438
1319
  { value: "oldest", label: "Oldest", key: "createdAt", order: "asc" },
1439
1320
  { value: "name-asc", label: "Name (A-Z)", key: "name", order: "asc" },
1440
1321
  { value: "name-desc", label: "Name (Z-A)", key: "name", order: "desc" },
1441
- { value: "popular", label: "Most Popular", key: "views", order: "desc" }
1322
+ { value: "popular", label: "Most Popular", key: "views", order: "desc" },
1442
1323
  ],
1443
1324
  onChange: (payload) => {
1444
1325
  console.log(`Sorted by: ${payload.option?.label}`);
1445
- }
1446
- }
1326
+ },
1327
+ },
1447
1328
  };
1448
1329
  ```
1449
1330