@aatulwork/customform-renderer 1.1.0 → 1.3.0

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 CHANGED
@@ -1,4 +1,4 @@
1
- # @custom-form/renderer
1
+ # @aatulwork/customform-renderer
2
2
 
3
3
  A powerful, reusable form renderer component for React with Material-UI support. This package provides a dynamic form rendering system that can generate forms from JSON schemas with support for multiple field types, validation, file uploads, and more.
4
4
 
@@ -16,7 +16,7 @@ A powerful, reusable form renderer component for React with Material-UI support.
16
16
  ## Installation
17
17
 
18
18
  ```bash
19
- npm install @custom-form/renderer
19
+ npm install @aatulwork/customform-renderer
20
20
  ```
21
21
 
22
22
  ### Peer Dependencies
@@ -27,9 +27,11 @@ Make sure you have these peer dependencies installed:
27
27
  npm install react react-dom @mui/material @mui/icons-material @mui/x-date-pickers react-hook-form @tanstack/react-query dayjs @ckeditor/ckeditor5-react
28
28
  ```
29
29
 
30
+ Peer versions: React ^18, MUI ^6, @mui/x-date-pickers ^7, react-hook-form ^7, @tanstack/react-query ^5, dayjs ^1.11, @ckeditor/ckeditor5-react ^11.
31
+
30
32
  ### CKEditor Setup
31
33
 
32
- The package includes CKEditor in the `lib/ckeditor/` directory. You need to load it before using CKEditor fields.
34
+ The package includes CKEditor in the `lib/ckeditor/` directory. You need to load it before using CKEditor fields. **If the CKEditor field shows "CKEditor failed to load" or stays on "Loading editor...",** the script URL is not reachable—copy `lib/ckeditor/ckeditor.js` to your app’s `public/lib/ckeditor/` or set `services.ckEditorScriptPath`. See [CKEDITOR_SETUP.md](./CKEDITOR_SETUP.md) for details and troubleshooting.
33
35
 
34
36
  **Option 1: Include in HTML (Recommended)**
35
37
  ```html
@@ -38,23 +40,34 @@ The package includes CKEditor in the `lib/ckeditor/` directory. You need to load
38
40
 
39
41
  **Option 2: Copy to your public directory**
40
42
  ```bash
41
- cp node_modules/@custom-form/renderer/lib/ckeditor/ckeditor.js public/lib/ckeditor/
43
+ cp node_modules/@aatulwork/customform-renderer/lib/ckeditor/ckeditor.js public/lib/ckeditor/
42
44
  ```
43
45
 
44
46
  **Option 3: Use dynamic loading**
45
47
  ```tsx
46
- import { useCKEditor } from '@custom-form/renderer';
48
+ import { useCKEditor } from '@aatulwork/customform-renderer';
47
49
 
48
50
  const { isReady } = useCKEditor({ autoLoad: true });
49
51
  ```
50
52
 
51
53
  See [CKEditor Setup Guide](#ckeditor-setup) for more details.
52
54
 
55
+ ## Package Exports
56
+
57
+ The package exports the following:
58
+
59
+ - **Components:** `FormRenderer`, `FieldRenderer`, `FormViewMode`, `FieldView`
60
+ - **Field components:** `TextField`, `SelectField`, `CheckboxField`, `RadioField`, `ToggleField`, `ColorField`, `DateTimePickerField`, `CKEditorField`, `FileField`, `FormReferenceField`, `ApiReferenceField`
61
+ - **Common:** `SimpleSelect` (and types `SimpleSelectProps`, `SimpleSelectOption`)
62
+ - **Types:** `FormSchema`, `FormField`, `FormSection`, `FormRendererProps`, `FieldRendererProps`, `FormServices`, `FileUploadService`, `FormReferenceService`, `ApiReferenceService`, `DateFormatterService`, `OptionItem`, `FieldType`, `FieldValidation`, `UploadedFile`, `FormColors`
63
+ - **Utils:** `getAllFields`, `normalizeInitialValues`, `transformFormValues`, `getDefaultValue`, `formatFileSize`, `validateFile`, `buildFieldRules`, `normalizeOptions`
64
+ - **CKEditor:** `loadCKEditor`, `isCKEditorAvailable`, `waitForCKEditor`, `useCKEditor`
65
+ - **Default services:** `defaultFileUploadService`, `defaultFormReferenceService`, `defaultApiReferenceService`, `defaultDateFormatterService` (throw if used without override; provide your own via `services`)
66
+
53
67
  ## Quick Start
54
68
 
55
69
  ```tsx
56
- import { FormRenderer } from '@custom-form/renderer';
57
- import { FormSchema } from '@custom-form/renderer';
70
+ import { FormRenderer, FormSchema } from '@aatulwork/customform-renderer';
58
71
 
59
72
  const formSchema: FormSchema = {
60
73
  title: 'User Registration',
@@ -103,14 +116,22 @@ function App() {
103
116
 
104
117
  ```typescript
105
118
  interface FormSchema {
119
+ _id?: string;
120
+ id?: string; // Legacy support
106
121
  title: string;
107
- name: string; // Unique identifier
122
+ name: string; // Unique identifier (lowercase)
123
+ module?: string | null;
124
+ formType?: 'system' | 'custom';
125
+ collectionName?: string;
108
126
  sections?: FormSection[];
109
127
  fields?: FormField[]; // Legacy support
110
128
  settings?: {
111
129
  sectionDisplayMode?: 'panel' | 'stepper';
112
- fieldsPerRow?: 1 | 2 | 3;
130
+ fieldsPerRow?: number; // 1, 2, or 3
131
+ [key: string]: any;
113
132
  };
133
+ createdAt?: string;
134
+ updatedAt?: string;
114
135
  }
115
136
  ```
116
137
 
@@ -129,24 +150,32 @@ interface FormSection {
129
150
 
130
151
  ```typescript
131
152
  interface FormField {
132
- type: 'text' | 'email' | 'number' | 'select' | 'checkbox' | 'radio' |
133
- 'datepicker' | 'file' | 'ckeditor' | 'toggle' | 'color' |
153
+ type: 'text' | 'email' | 'number' | 'select' | 'checkbox' | 'radio' |
154
+ 'datepicker' | 'file' | 'ckeditor' | 'toggle' | 'color' |
134
155
  'formReference' | 'apiReference';
135
156
  name: string;
136
157
  label: string;
137
158
  required?: boolean;
138
159
  placeholder?: string;
160
+ allowFilter?: boolean;
139
161
  options?: OptionItem[] | string[]; // For select/radio
140
162
  validation?: {
141
163
  min?: number;
142
164
  max?: number;
143
165
  pattern?: string;
144
- maxFileSize?: number; // For file fields
166
+ maxFileSize?: number; // For file fields (bytes)
145
167
  allowedFileTypes?: string[]; // For file fields
146
168
  };
147
- allowMultiple?: boolean; // For select/file fields
169
+ // Reference fields
170
+ referenceFormName?: string;
171
+ referenceFieldName?: string;
172
+ apiEndpoint?: string;
173
+ referenceModel?: string;
174
+ apiLabelField?: string;
175
+ apiValueField?: string;
176
+ allowMultiple?: boolean; // For select/file fields
148
177
  datePickerMode?: 'date' | 'datetime' | 'time'; // For datepicker
149
- // ... other field-specific properties
178
+ displayTime?: boolean; // @deprecated Use datePickerMode instead
150
179
  }
151
180
  ```
152
181
 
@@ -187,6 +216,8 @@ interface FormField {
187
216
 
188
217
  ### Date Picker
189
218
 
219
+ Field type is `datepicker`; the exported component is `DateTimePickerField`.
220
+
190
221
  ```typescript
191
222
  {
192
223
  type: 'datepicker',
@@ -224,29 +255,375 @@ interface FormField {
224
255
 
225
256
  ### Reference Fields
226
257
 
258
+ Reference fields allow you to create dropdown selects that fetch options from other forms or external APIs. There are two types: **Form Reference** and **API Reference**.
259
+
260
+ #### Form Reference (`formReference`)
261
+
262
+ Form Reference fields fetch options from entries of another form in your system. This is useful for creating relationships between forms (e.g., selecting a user, product, or category).
263
+
264
+ **Field Configuration:**
227
265
  ```typescript
228
- // Form Reference
229
266
  {
230
267
  type: 'formReference',
231
- name: 'relatedForm',
232
- label: 'Related Form Entry',
233
- referenceFormName: 'users',
234
- referenceFieldName: 'name',
235
- allowMultiple: false
268
+ name: 'userId',
269
+ label: 'Select User',
270
+ required: true,
271
+ referenceFormName: 'users', // Name of the form to reference
272
+ referenceFieldName: 'fullName', // Field name to display as label
273
+ allowMultiple: false, // Allow selecting multiple items
274
+ placeholder: 'Select a user...'
275
+ }
276
+ ```
277
+
278
+ **Required Service Setup:**
279
+
280
+ You must provide a `formReference` service that fetches options from your form entries:
281
+
282
+ ```tsx
283
+ import { FormRenderer, FormServices } from '@aatulwork/customform-renderer';
284
+
285
+ const services: FormServices = {
286
+ formReference: {
287
+ fetchOptions: async (formName: string, fieldName: string) => {
288
+ // Fetch entries from the referenced form
289
+ const response = await fetch(`/api/forms/${formName}/entries`);
290
+ const data = await response.json();
291
+
292
+ // Transform entries into OptionItem format
293
+ return data.map((entry: any) => ({
294
+ label: entry.payload[fieldName] || entry[fieldName] || entry._id,
295
+ value: entry._id,
296
+ }));
297
+ },
298
+ },
299
+ };
300
+
301
+ <FormRenderer
302
+ formSchema={formSchema}
303
+ services={services}
304
+ onSubmit={handleSubmit}
305
+ />
306
+ ```
307
+
308
+ **Example: Complete Form Reference Setup**
309
+
310
+ ```tsx
311
+ import { FormRenderer, FormServices, FormSchema } from '@aatulwork/customform-renderer';
312
+
313
+ // Define your form schema with formReference field
314
+ const formSchema: FormSchema = {
315
+ title: 'Task Assignment',
316
+ name: 'task-assignment',
317
+ sections: [
318
+ {
319
+ id: 'assignment',
320
+ title: 'Assignment Details',
321
+ fields: [
322
+ {
323
+ type: 'formReference',
324
+ name: 'assignedTo',
325
+ label: 'Assign To',
326
+ required: true,
327
+ referenceFormName: 'users', // References the 'users' form
328
+ referenceFieldName: 'fullName', // Displays the 'fullName' field
329
+ placeholder: 'Select a user...',
330
+ },
331
+ {
332
+ type: 'formReference',
333
+ name: 'project',
334
+ label: 'Project',
335
+ required: true,
336
+ referenceFormName: 'projects',
337
+ referenceFieldName: 'title',
338
+ allowMultiple: false,
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ };
344
+
345
+ // Provide the formReference service
346
+ const services: FormServices = {
347
+ formReference: {
348
+ fetchOptions: async (formName: string, fieldName: string) => {
349
+ try {
350
+ // Example: Fetch from your API
351
+ const response = await fetch(`/api/forms/${formName}/entries?status=active`);
352
+
353
+ if (!response.ok) {
354
+ throw new Error(`Failed to fetch ${formName} entries`);
355
+ }
356
+
357
+ const entries = await response.json();
358
+
359
+ // Transform to OptionItem format
360
+ return entries.map((entry: any) => ({
361
+ label: entry.payload?.[fieldName] || entry[fieldName] || `Entry ${entry._id}`,
362
+ value: entry._id,
363
+ }));
364
+ } catch (error) {
365
+ console.error(`Error fetching ${formName} options:`, error);
366
+ return [];
367
+ }
368
+ },
369
+ },
370
+ };
371
+
372
+ function TaskForm() {
373
+ const handleSubmit = async (data: Record<string, any>) => {
374
+ console.log('Assigned to:', data.assignedTo); // Will be the _id of selected user
375
+ console.log('Project:', data.project); // Will be the _id of selected project
376
+ // Submit to your API...
377
+ };
378
+
379
+ return (
380
+ <FormRenderer
381
+ formSchema={formSchema}
382
+ services={services}
383
+ onSubmit={handleSubmit}
384
+ />
385
+ );
236
386
  }
387
+ ```
237
388
 
238
- // API Reference
389
+ #### API Reference (`apiReference`)
390
+
391
+ API Reference fields fetch options from any external API endpoint. This is useful for integrating with third-party APIs or your own REST endpoints.
392
+
393
+ **Field Configuration:**
394
+ ```typescript
239
395
  {
240
396
  type: 'apiReference',
241
397
  name: 'role',
242
- label: 'Role',
243
- apiEndpoint: '/api/roles',
244
- apiLabelField: 'name',
245
- apiValueField: '_id',
246
- allowMultiple: false
398
+ label: 'Select Role',
399
+ required: true,
400
+ apiEndpoint: '/api/roles', // API endpoint to fetch from
401
+ apiLabelField: 'name', // Field name to display as label
402
+ apiValueField: '_id', // Field name to use as value (default: '_id')
403
+ allowMultiple: false, // Allow selecting multiple items
404
+ placeholder: 'Select a role...'
247
405
  }
248
406
  ```
249
407
 
408
+ **Required Service Setup:**
409
+
410
+ You must provide an `apiReference` service that fetches options from your API:
411
+
412
+ ```tsx
413
+ import { FormRenderer, FormServices } from '@aatulwork/customform-renderer';
414
+
415
+ const services: FormServices = {
416
+ apiReference: {
417
+ fetchOptions: async (endpoint: string, labelField: string, valueField = '_id') => {
418
+ const response = await fetch(endpoint);
419
+ const data = await response.json();
420
+
421
+ // Handle both array responses and object responses
422
+ const items = Array.isArray(data) ? data : data.items || data.data || [];
423
+
424
+ return items.map((item: any) => ({
425
+ label: item[labelField],
426
+ value: item[valueField],
427
+ }));
428
+ },
429
+ },
430
+ };
431
+
432
+ <FormRenderer
433
+ formSchema={formSchema}
434
+ services={services}
435
+ onSubmit={handleSubmit}
436
+ />
437
+ ```
438
+
439
+ **Example: Complete API Reference Setup**
440
+
441
+ ```tsx
442
+ import { FormRenderer, FormServices, FormSchema } from '@aatulwork/customform-renderer';
443
+
444
+ // Define your form schema with apiReference field
445
+ const formSchema: FormSchema = {
446
+ title: 'User Registration',
447
+ name: 'user-registration',
448
+ sections: [
449
+ {
450
+ id: 'user-info',
451
+ title: 'User Information',
452
+ fields: [
453
+ {
454
+ type: 'text',
455
+ name: 'firstName',
456
+ label: 'First Name',
457
+ required: true,
458
+ },
459
+ {
460
+ type: 'apiReference',
461
+ name: 'country',
462
+ label: 'Country',
463
+ required: true,
464
+ apiEndpoint: '/api/countries',
465
+ apiLabelField: 'name',
466
+ apiValueField: 'code',
467
+ },
468
+ {
469
+ type: 'apiReference',
470
+ name: 'role',
471
+ label: 'Role',
472
+ required: true,
473
+ apiEndpoint: '/api/roles',
474
+ apiLabelField: 'name',
475
+ apiValueField: '_id', // Default, can be omitted
476
+ },
477
+ ],
478
+ },
479
+ ],
480
+ };
481
+
482
+ // Provide the apiReference service
483
+ const services: FormServices = {
484
+ apiReference: {
485
+ fetchOptions: async (endpoint: string, labelField: string, valueField = '_id') => {
486
+ try {
487
+ const response = await fetch(endpoint, {
488
+ headers: {
489
+ 'Authorization': `Bearer ${yourAuthToken}`, // Add auth if needed
490
+ 'Content-Type': 'application/json',
491
+ },
492
+ });
493
+
494
+ if (!response.ok) {
495
+ throw new Error(`Failed to fetch from ${endpoint}`);
496
+ }
497
+
498
+ const data = await response.json();
499
+
500
+ // Handle different response formats
501
+ const items = Array.isArray(data)
502
+ ? data
503
+ : data.items || data.data || data.results || [];
504
+
505
+ // Transform to OptionItem format
506
+ return items.map((item: any) => ({
507
+ label: item[labelField] || String(item[valueField]),
508
+ value: item[valueField],
509
+ }));
510
+ } catch (error) {
511
+ console.error(`Error fetching options from ${endpoint}:`, error);
512
+ return [];
513
+ }
514
+ },
515
+ },
516
+ };
517
+
518
+ function RegistrationForm() {
519
+ const handleSubmit = async (data: Record<string, any>) => {
520
+ console.log('Country code:', data.country); // Will be the country code
521
+ console.log('Role ID:', data.role); // Will be the role _id
522
+ // Submit to your API...
523
+ };
524
+
525
+ return (
526
+ <FormRenderer
527
+ formSchema={formSchema}
528
+ services={services}
529
+ onSubmit={handleSubmit}
530
+ />
531
+ );
532
+ }
533
+ ```
534
+
535
+ #### Multiple Selection Support
536
+
537
+ Both reference field types support multiple selections:
538
+
539
+ ```typescript
540
+ {
541
+ type: 'formReference',
542
+ name: 'tags',
543
+ label: 'Tags',
544
+ referenceFormName: 'tags',
545
+ referenceFieldName: 'name',
546
+ allowMultiple: true, // Enable multiple selection
547
+ }
548
+ ```
549
+
550
+ When `allowMultiple: true`, the field will return an array of selected values.
551
+
552
+ #### Advanced: Custom API with Query Parameters
553
+
554
+ You can create dynamic endpoints by modifying the service:
555
+
556
+ ```tsx
557
+ const services: FormServices = {
558
+ apiReference: {
559
+ fetchOptions: async (endpoint: string, labelField: string, valueField = '_id') => {
560
+ // Add query parameters
561
+ const url = new URL(endpoint, window.location.origin);
562
+ url.searchParams.append('status', 'active');
563
+ url.searchParams.append('limit', '100');
564
+
565
+ const response = await fetch(url.toString());
566
+ const data = await response.json();
567
+
568
+ return data.map((item: any) => ({
569
+ label: item[labelField],
570
+ value: item[valueField],
571
+ }));
572
+ },
573
+ },
574
+ };
575
+ ```
576
+
577
+ #### Error Handling
578
+
579
+ Both services should handle errors gracefully:
580
+
581
+ ```tsx
582
+ const services: FormServices = {
583
+ formReference: {
584
+ fetchOptions: async (formName: string, fieldName: string) => {
585
+ try {
586
+ const response = await fetch(`/api/forms/${formName}/entries`);
587
+ if (!response.ok) throw new Error('Failed to fetch');
588
+ const data = await response.json();
589
+ return data.map((entry: any) => ({
590
+ label: entry.payload?.[fieldName] || entry._id,
591
+ value: entry._id,
592
+ }));
593
+ } catch (error) {
594
+ console.error('Form reference error:', error);
595
+ return []; // Return empty array on error
596
+ }
597
+ },
598
+ },
599
+ apiReference: {
600
+ fetchOptions: async (endpoint: string, labelField: string, valueField = '_id') => {
601
+ try {
602
+ const response = await fetch(endpoint);
603
+ if (!response.ok) throw new Error('Failed to fetch');
604
+ const data = await response.json();
605
+ const items = Array.isArray(data) ? data : data.items || [];
606
+ return items.map((item: any) => ({
607
+ label: item[labelField],
608
+ value: item[valueField],
609
+ }));
610
+ } catch (error) {
611
+ console.error('API reference error:', error);
612
+ return []; // Return empty array on error
613
+ }
614
+ },
615
+ },
616
+ };
617
+ ```
618
+
619
+ #### Notes
620
+
621
+ - **Loading State**: Reference fields automatically show a loading indicator while fetching options
622
+ - **Disabled State**: Fields are disabled if required configuration is missing (e.g., `referenceFormName` or `apiEndpoint`)
623
+ - **Error Handling**: If the service throws an error or returns empty array, the field will be disabled
624
+ - **Caching**: Options are fetched once when the component mounts and when dependencies change
625
+ - **Custom Select Component**: You can provide a custom `SelectComponent` in services to use your own select component
626
+
250
627
  ## Service Injection
251
628
 
252
629
  The package uses a service injection pattern to handle external dependencies. You can provide custom services for file uploads, form references, API references, and more.
@@ -254,7 +631,7 @@ The package uses a service injection pattern to handle external dependencies. Yo
254
631
  ### Providing Services
255
632
 
256
633
  ```tsx
257
- import { FormRenderer, FormServices } from '@custom-form/renderer';
634
+ import { FormRenderer, FormServices } from '@aatulwork/customform-renderer';
258
635
 
259
636
  const services: FormServices = {
260
637
  // File upload service
@@ -339,7 +716,11 @@ interface FormRendererProps {
339
716
  allowResetOnValuesChange?: boolean;
340
717
  mode?: 'edit' | 'view';
341
718
  services?: FormServices;
719
+ colors?: FormColors; // Override theme colors (primary, secondary, error, etc.)
342
720
  }
721
+
722
+ // FormColors: primary?, secondary?, error?, success?, warning?, info?,
723
+ // textPrimary?, textSecondary?, divider?, background?, backgroundPaper?
343
724
  ```
344
725
 
345
726
  ## View Mode
@@ -575,7 +956,7 @@ If using Vite, Create React App, or similar:
575
956
 
576
957
  ```bash
577
958
  # Copy CKEditor to your public directory
578
- cp node_modules/@custom-form/renderer/lib/ckeditor/ckeditor.js public/lib/ckeditor/ckeditor.js
959
+ cp node_modules/@aatulwork/customform-renderer/lib/ckeditor/ckeditor.js public/lib/ckeditor/ckeditor.js
579
960
  ```
580
961
 
581
962
  Then include in HTML:
@@ -588,7 +969,7 @@ Then include in HTML:
588
969
  Use the provided hook to load CKEditor dynamically:
589
970
 
590
971
  ```tsx
591
- import { useCKEditor } from '@custom-form/renderer';
972
+ import { useCKEditor } from '@aatulwork/customform-renderer';
592
973
 
593
974
  function App() {
594
975
  const { isReady, isLoading, error } = useCKEditor({
@@ -622,12 +1003,12 @@ const services: FormServices = {
622
1003
  ### CKEditor Utilities
623
1004
 
624
1005
  ```tsx
625
- import {
626
- loadCKEditor,
627
- isCKEditorAvailable,
1006
+ import {
1007
+ loadCKEditor,
1008
+ isCKEditorAvailable,
628
1009
  waitForCKEditor,
629
- useCKEditor
630
- } from '@custom-form/renderer';
1010
+ useCKEditor,
1011
+ } from '@aatulwork/customform-renderer';
631
1012
 
632
1013
  // Check if available
633
1014
  if (isCKEditorAvailable()) {
package/dist/index.js CHANGED
@@ -568,12 +568,20 @@ var loadCKEditor = (scriptPath = "/lib/ckeditor/ckeditor.js") => {
568
568
  setTimeout(() => {
569
569
  clearInterval(checkInterval);
570
570
  if (!window.ClassicEditor) {
571
- reject(new Error("CKEditor script loaded but ClassicEditor not found on window after 10 seconds"));
571
+ reject(
572
+ new Error(
573
+ "CKEditor script loaded but ClassicEditor was not found on window. Ensure you are using the package-provided build from lib/ckeditor/ckeditor.js (see CKEDITOR_SETUP.md)."
574
+ )
575
+ );
572
576
  }
573
577
  }, 1e4);
574
578
  };
575
579
  script.onerror = () => {
576
- reject(new Error(`Failed to load CKEditor from ${scriptPath}`));
580
+ reject(
581
+ new Error(
582
+ `Failed to load CKEditor from ${scriptPath}. Ensure the file exists at that URL (e.g. copy from node_modules/@aatulwork/customform-renderer/lib/ckeditor/ckeditor.js to public/lib/ckeditor/ckeditor.js) or set services.ckEditorScriptPath to a working URL.`
583
+ )
584
+ );
577
585
  };
578
586
  document.head.appendChild(script);
579
587
  });
@@ -705,9 +713,10 @@ var CKEditorField = ({ field, control, defaultValue, rules, errors, setValue, fo
705
713
  ] });
706
714
  }
707
715
  if (ckEditorError || !isCKEditorReady) {
716
+ const message = ckEditorError?.message || `CKEditor failed to load. Script path: ${ckEditorScriptPath}. Copy ckeditor.js to public/lib/ckeditor/ or set services.ckEditorScriptPath. See CKEDITOR_SETUP.md.`;
708
717
  return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { children: [
709
718
  /* @__PURE__ */ jsxRuntime.jsx(material.FormLabel, { required: field.required, error: !!errors[field.name], children: field.label }),
710
- /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { p: 2, border: "1px solid", borderColor: formColors.error, borderRadius: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(material.FormHelperText, { error: true, children: ckEditorError?.message || "CKEditor failed to load. Please ensure the CKEditor script is loaded." }) })
719
+ /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { p: 2, border: "1px solid", borderColor: formColors.error, borderRadius: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(material.FormHelperText, { error: true, children: message }) })
711
720
  ] });
712
721
  }
713
722
  return /* @__PURE__ */ jsxRuntime.jsx(