df-ae-forms-package 1.0.96 → 1.0.98
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/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +16 -5
- package/dist/index.esm.js +147 -223
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +145 -221
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -716,27 +716,38 @@ declare const DfFormSection: React.FC<DfFormSectionProps>;
|
|
|
716
716
|
interface DfFormDataGridProps {
|
|
717
717
|
id: string;
|
|
718
718
|
properties: IDataGridComponent;
|
|
719
|
+
validationErrors?: Record<string, any>;
|
|
720
|
+
formValue?: any;
|
|
721
|
+
formData?: Record<string, any>;
|
|
722
|
+
readonly?: boolean;
|
|
723
|
+
disabled?: boolean;
|
|
724
|
+
touchedFields?: Record<string, boolean>;
|
|
725
|
+
formSubmitted?: boolean;
|
|
719
726
|
mode?: 'edit' | 'preview' | 'test';
|
|
720
|
-
formData?: any;
|
|
721
727
|
onValueChange?: (change: IFormControlChange) => void;
|
|
728
|
+
onBlur?: () => void;
|
|
729
|
+
onFocus?: () => void;
|
|
722
730
|
onSelect?: () => void;
|
|
723
731
|
isSelected?: boolean;
|
|
724
732
|
className?: string;
|
|
725
|
-
onDataGridSelect?: (
|
|
733
|
+
onDataGridSelect?: (dataGrid: IDataGridComponent) => void;
|
|
734
|
+
onDataGridDelete?: (dataGridId: string) => void;
|
|
735
|
+
onEntryChange?: (entryIndex: number, components: FormComponentType[]) => void;
|
|
726
736
|
onComponentSelect?: (component: FormComponentType) => void;
|
|
727
737
|
onComponentDelete?: (component: FormComponentType, event: React.MouseEvent) => void;
|
|
728
738
|
onComponentEdit?: (component: FormComponentType) => void;
|
|
729
739
|
onComponentUpdate?: (componentId: string, updates: Partial<FormComponentType>) => void;
|
|
730
740
|
selectedComponent?: FormComponentType | null;
|
|
731
|
-
renderFormComponent?: (
|
|
741
|
+
renderFormComponent?: (field: FormComponentType, hideLabel?: boolean) => React.ReactNode;
|
|
742
|
+
onDataGridUpdate?: (dataGridId: string, updates: Partial<IDataGridComponent>) => void;
|
|
732
743
|
onEntryAdd?: () => void;
|
|
733
|
-
onEntryRemove?: (
|
|
744
|
+
onEntryRemove?: (entryIndex: number) => void;
|
|
734
745
|
formTemplateId?: string;
|
|
735
746
|
onThresholdActionCompletion?: (conditionId: string, action: 'notes' | 'attachments' | 'email', completed: boolean) => void;
|
|
736
747
|
onThresholdIssueRaised?: (conditionId: string) => void;
|
|
737
748
|
onNotesChange?: (componentId: string, notes: string) => void;
|
|
738
749
|
onAttachmentChange?: (componentId: string, attachments: File[] | null) => void;
|
|
739
|
-
shouldShowComponent?: (
|
|
750
|
+
shouldShowComponent?: (id: string) => boolean;
|
|
740
751
|
}
|
|
741
752
|
declare const DfFormDataGrid: React.FC<DfFormDataGridProps>;
|
|
742
753
|
|
package/dist/index.esm.js
CHANGED
|
@@ -2,8 +2,8 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
3
3
|
import { AlertTriangle, X as X$1, User, Calendar, MessageSquare, UploadCloud, Paperclip, Loader2, Navigation, MapPin, Bold, Italic, List, ListOrdered, Image as Image$1, Check, Mail, AlertCircle, ChevronUp, ChevronDown, ChevronRight, Grid, GripVertical, Edit, Trash2, Table } from 'lucide-react';
|
|
4
4
|
import require$$0, { createPortal } from 'react-dom';
|
|
5
|
-
import { useDroppable
|
|
6
|
-
import {
|
|
5
|
+
import { useDroppable } from '@dnd-kit/core';
|
|
6
|
+
import { SortableContext, horizontalListSortingStrategy, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
7
7
|
import { CSS } from '@dnd-kit/utilities';
|
|
8
8
|
import { v4 } from 'uuid';
|
|
9
9
|
|
|
@@ -4332,57 +4332,20 @@ const DraggableGridComponent = ({ component, selectedComponent, mode, onComponen
|
|
|
4332
4332
|
e.currentTarget.style.backgroundColor = '#ef4444';
|
|
4333
4333
|
}, children: jsx(Trash2, { size: 12 }) })] }))] }));
|
|
4334
4334
|
};
|
|
4335
|
-
|
|
4336
|
-
const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, selectedComponent, renderFormComponent, gridId, formData, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView, shouldShowComponent }) => {
|
|
4335
|
+
const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, selectedComponent, renderFormComponent, gridId, formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView = false, shouldShowComponent }) => {
|
|
4337
4336
|
const { setNodeRef, isOver } = useDroppable({
|
|
4338
4337
|
id: `grid-drop-zone-${gridId}`,
|
|
4339
|
-
disabled: mode !== 'edit'
|
|
4340
|
-
data: {
|
|
4341
|
-
isGridDropZone: true,
|
|
4342
|
-
gridId: gridId
|
|
4343
|
-
}
|
|
4344
|
-
});
|
|
4345
|
-
// Sensors for drag and drop
|
|
4346
|
-
const sensors = useSensors(useSensor(PointerSensor, {
|
|
4347
|
-
activationConstraint: {
|
|
4348
|
-
distance: 8,
|
|
4349
|
-
},
|
|
4350
|
-
}), useSensor(KeyboardSensor, {
|
|
4351
|
-
coordinateGetter: sortableKeyboardCoordinates,
|
|
4352
|
-
}));
|
|
4353
|
-
const handleDragEnd = ((event) => {
|
|
4354
|
-
const { active, over } = event;
|
|
4355
|
-
if (over && active.id !== over.id) {
|
|
4356
|
-
gridComponents.findIndex((item) => item.id === active.id);
|
|
4357
|
-
gridComponents.findIndex((item) => item.id === over.id);
|
|
4358
|
-
}
|
|
4338
|
+
disabled: mode !== 'edit'
|
|
4359
4339
|
});
|
|
4360
|
-
return (jsx("div", { ref: setNodeRef, className:
|
|
4361
|
-
border: isOver ? '2px dashed #3b82f6' : '
|
|
4340
|
+
return (jsx("div", { ref: setNodeRef, className: "grid-drop-zone", style: {
|
|
4341
|
+
border: isOver ? '2px dashed #3b82f6' : '2px dashed #d1d5db',
|
|
4362
4342
|
borderRadius: '8px',
|
|
4363
4343
|
padding: '16px',
|
|
4364
|
-
backgroundColor: isOver ? 'var(--df-color-primary-light)' : '
|
|
4365
|
-
minHeight: '
|
|
4366
|
-
transition: 'all 0.2s ease'
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
color: 'var(--df-color-text-light)',
|
|
4370
|
-
fontSize: '14px',
|
|
4371
|
-
padding: '40px 20px',
|
|
4372
|
-
display: 'flex',
|
|
4373
|
-
flexDirection: 'column',
|
|
4374
|
-
alignItems: 'center',
|
|
4375
|
-
gap: '8px',
|
|
4376
|
-
backgroundColor: 'var(--df-color-fb-container)',
|
|
4377
|
-
border: '1px dashed var(--df-color-fb-border)',
|
|
4378
|
-
borderRadius: '8px'
|
|
4379
|
-
}, children: [jsx("div", { style: {
|
|
4380
|
-
fontWeight: '500',
|
|
4381
|
-
color: isOver ? 'var(--df-color-primary)' : 'var(--df-color-text-dark)'
|
|
4382
|
-
}, children: isOver ? 'Drop components here' : 'Empty DataGrid' }), jsx("div", { style: {
|
|
4383
|
-
fontSize: '12px',
|
|
4384
|
-
color: '#9ca3af'
|
|
4385
|
-
}, children: "Drag and drop components here to create your grid" })] })) : (jsxs(DndContext, { sensors: sensors, onDragEnd: handleDragEnd, children: [jsx(SortableContext, { items: gridComponents.map(c => c.id), strategy: columnView ? verticalListSortingStrategy : horizontalListSortingStrategy, children: jsx("div", { style: {
|
|
4344
|
+
backgroundColor: isOver ? 'var(--df-color-primary-light)' : 'var(--df-color-fb-container)',
|
|
4345
|
+
minHeight: '120px',
|
|
4346
|
+
transition: 'all 0.2s ease',
|
|
4347
|
+
position: 'relative'
|
|
4348
|
+
}, children: gridComponents.length > 0 ? (jsxs(Fragment, { children: [jsx(SortableContext, { items: gridComponents.map(c => c.id), strategy: horizontalListSortingStrategy, children: jsx("div", { style: {
|
|
4386
4349
|
display: 'flex',
|
|
4387
4350
|
flexDirection: columnView ? 'column' : 'row',
|
|
4388
4351
|
flexWrap: 'nowrap',
|
|
@@ -4407,14 +4370,28 @@ const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDele
|
|
|
4407
4370
|
minHeight: '40px',
|
|
4408
4371
|
display: 'flex',
|
|
4409
4372
|
alignItems: 'center',
|
|
4410
|
-
justifyContent: 'center'
|
|
4411
|
-
|
|
4412
|
-
|
|
4373
|
+
justifyContent: 'center'
|
|
4374
|
+
}, children: isOver ? (jsx("span", { style: { color: '#3b82f6', fontWeight: '500' }, children: "Drop component here to add to grid" })) : (jsx("span", { children: "+ Drop more components here" })) })] })) : (jsxs("div", { style: {
|
|
4375
|
+
textAlign: 'center',
|
|
4376
|
+
color: 'var(--df-color-text-light)',
|
|
4377
|
+
fontSize: '14px',
|
|
4378
|
+
padding: '40px 20px',
|
|
4379
|
+
display: 'flex',
|
|
4380
|
+
flexDirection: 'column',
|
|
4381
|
+
alignItems: 'center',
|
|
4382
|
+
gap: '8px',
|
|
4383
|
+
backgroundColor: 'var(--df-color-fb-container)',
|
|
4384
|
+
border: '1px dashed var(--df-color-fb-border)',
|
|
4385
|
+
borderRadius: '8px'
|
|
4386
|
+
}, children: [jsx("div", { style: {
|
|
4387
|
+
fontWeight: '500',
|
|
4388
|
+
color: isOver ? 'var(--df-color-primary)' : 'var(--df-color-text-dark)'
|
|
4389
|
+
}, children: isOver ? 'Drop components here' : 'Empty DataGrid' }), jsx("div", { style: {
|
|
4390
|
+
fontSize: '12px',
|
|
4391
|
+
color: '#9ca3af'
|
|
4392
|
+
}, children: "Drag and drop components here to create your grid" })] })) }));
|
|
4413
4393
|
};
|
|
4414
|
-
|
|
4415
|
-
const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode, allowAddRemoveEntries, addAnotherText, removeText, maxEntries, minEntries, displayAsGrid = true, onAddEntry, onRemoveEntry, formData, // Use current formData to render values
|
|
4416
|
-
formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView, shouldShowComponent }) => {
|
|
4417
|
-
const _formData = formData || {};
|
|
4394
|
+
const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode = 'preview', allowAddRemoveEntries = true, addAnotherText = 'Add Another', removeText = 'Remove', maxEntries = 10, minEntries = 1, displayAsGrid = true, onAddEntry, onRemoveEntry, formData: _formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView = false, shouldShowComponent }) => {
|
|
4418
4395
|
const visibleTemplateComponents = React.useMemo(() => {
|
|
4419
4396
|
if (!shouldShowComponent)
|
|
4420
4397
|
return templateComponents;
|
|
@@ -4476,18 +4453,16 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4476
4453
|
whiteSpace: 'nowrap',
|
|
4477
4454
|
overflow: 'hidden',
|
|
4478
4455
|
textOverflow: 'ellipsis'
|
|
4479
|
-
}, children: component.basic?.label || `Column ${index + 1}` }, `header-${component.id
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
: undefined,
|
|
4456
|
+
}, children: component.basic?.label || `Column ${index + 1}` }, `header-${component.id}`))) })), dataEntries.length > 0 ? (dataEntries.map((entry, entryIndex) => (jsxs("div", { className: "table-row", style: {
|
|
4457
|
+
display: 'grid',
|
|
4458
|
+
// Change grid columns to 1fr for column view
|
|
4459
|
+
gridTemplateColumns: columnView
|
|
4460
|
+
? '1fr'
|
|
4461
|
+
: `repeat(${visibleTemplateComponents.length}, minmax(150px, 1fr))`,
|
|
4486
4462
|
borderBottom: entryIndex < dataEntries.length - 1 ? '1px solid var(--df-color-fb-border)' : 'none',
|
|
4487
4463
|
backgroundColor: entryIndex % 2 === 0 ? 'var(--df-color-fb-container)' : 'var(--df-color-fb-bg)',
|
|
4488
4464
|
position: 'relative',
|
|
4489
|
-
minWidth: columnView ? '100%' : `${visibleTemplateComponents.length * 150}px
|
|
4490
|
-
padding: columnView ? '16px' : '0'
|
|
4465
|
+
minWidth: columnView ? '100%' : `${visibleTemplateComponents.length * 150}px`
|
|
4491
4466
|
}, children: [visibleTemplateComponents.map((templateComponent, componentIndex) => {
|
|
4492
4467
|
let entryComponent = entry.components?.[componentIndex];
|
|
4493
4468
|
if (!entryComponent) {
|
|
@@ -4513,24 +4488,21 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4513
4488
|
};
|
|
4514
4489
|
}
|
|
4515
4490
|
return (jsx("div", { style: {
|
|
4516
|
-
padding:
|
|
4491
|
+
padding: '12px 16px',
|
|
4517
4492
|
borderRight: !columnView && componentIndex < visibleTemplateComponents.length - 1 ? '1px solid var(--df-color-fb-border)' : 'none',
|
|
4518
4493
|
// Add bottom border for fields in column view except the last one
|
|
4519
4494
|
borderBottom: columnView && componentIndex < visibleTemplateComponents.length - 1 ? '1px dashed var(--df-color-fb-border)' : 'none',
|
|
4520
|
-
minHeight:
|
|
4495
|
+
minHeight: '60px',
|
|
4521
4496
|
minWidth: columnView ? '100%' : '150px',
|
|
4522
|
-
width: columnView ? '100%' : 'auto',
|
|
4523
4497
|
display: 'flex',
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
overflow: 'hidden',
|
|
4527
|
-
gap: columnView ? '8px' : '0'
|
|
4498
|
+
alignItems: 'center',
|
|
4499
|
+
overflow: 'hidden'
|
|
4528
4500
|
}, children: jsx("div", { style: {
|
|
4529
4501
|
width: '100%',
|
|
4530
|
-
minWidth:
|
|
4502
|
+
minWidth: '120px',
|
|
4531
4503
|
overflow: 'hidden'
|
|
4532
4504
|
}, children: renderFormComponent(entryComponent, !columnView) }) }, `${entry.id}-${componentIndex}`));
|
|
4533
|
-
}), mode === 'test' && allowAddRemoveEntries && dataEntries.length > minEntries && (jsx("button", { onClick: () => onRemoveEntry(entryIndex), disabled: dataEntries.length <= minEntries, style: {
|
|
4505
|
+
}), mode === 'test' && allowAddRemoveEntries && dataEntries.length > minEntries && (jsx("button", { onClick: () => onRemoveEntry?.(entryIndex), disabled: dataEntries.length <= minEntries, style: {
|
|
4534
4506
|
position: 'absolute',
|
|
4535
4507
|
top: '8px',
|
|
4536
4508
|
right: '8px',
|
|
@@ -4564,7 +4536,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4564
4536
|
position: 'sticky',
|
|
4565
4537
|
bottom: 0,
|
|
4566
4538
|
zIndex: 5
|
|
4567
|
-
}, children: jsxs("button", { onClick:
|
|
4539
|
+
}, children: jsxs("button", { onClick: onAddEntry, disabled: dataEntries.length >= maxEntries, style: {
|
|
4568
4540
|
padding: '8px 16px',
|
|
4569
4541
|
backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
|
|
4570
4542
|
color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
|
|
@@ -4602,7 +4574,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4602
4574
|
fontWeight: '600',
|
|
4603
4575
|
color: 'var(--df-color-text-dark)',
|
|
4604
4576
|
fontSize: '14px'
|
|
4605
|
-
}, children: ["Entry #", entryIndex + 1] }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > 1 && (jsx("button", { onClick: () => onRemoveEntry(entryIndex), disabled: dataEntries.length <= minEntries, style: {
|
|
4577
|
+
}, children: ["Entry #", entryIndex + 1] }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > 1 && (jsx("button", { onClick: () => onRemoveEntry?.(entryIndex), disabled: dataEntries.length <= minEntries, style: {
|
|
4606
4578
|
padding: '4px 8px',
|
|
4607
4579
|
backgroundColor: dataEntries.length <= minEntries ? '#f3f4f6' : '#ef4444',
|
|
4608
4580
|
color: dataEntries.length <= minEntries ? '#9ca3af' : '#ffffff',
|
|
@@ -4618,39 +4590,40 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4618
4590
|
height: '24px',
|
|
4619
4591
|
justifyContent: 'center'
|
|
4620
4592
|
}, title: removeText, children: jsx("span", { style: { fontSize: '14px' }, children: "\u00D7" }) }))] }), jsx("div", { style: {
|
|
4621
|
-
display:
|
|
4622
|
-
|
|
4623
|
-
gridTemplateColumns: !columnView ? 'repeat(auto-fit, minmax(200px, 1fr))' : undefined,
|
|
4593
|
+
display: 'grid',
|
|
4594
|
+
gridTemplateColumns: columnView ? '1fr' : 'repeat(auto-fit, minmax(200px, 1fr))',
|
|
4624
4595
|
gap: '16px'
|
|
4625
4596
|
}, children: visibleTemplateComponents.map((templateComponent, componentIndex) => {
|
|
4597
|
+
// Find the corresponding component in this entry by index first, then by name+label
|
|
4626
4598
|
let entryComponent = entry.components?.[componentIndex];
|
|
4599
|
+
// If no component at this index, try to find by name+label
|
|
4627
4600
|
if (!entryComponent) {
|
|
4628
4601
|
entryComponent = entry.components?.find((comp) => comp.name === templateComponent.name &&
|
|
4629
4602
|
comp.basic?.label === templateComponent.basic?.label);
|
|
4630
4603
|
}
|
|
4631
4604
|
if (!entryComponent) {
|
|
4605
|
+
// Use entry.id (which is unique) instead of entryIndex
|
|
4632
4606
|
const uniqueId = `${templateComponent.id}-${entry.id}-${componentIndex}`;
|
|
4633
4607
|
entryComponent = {
|
|
4634
4608
|
...templateComponent,
|
|
4635
4609
|
id: uniqueId,
|
|
4636
4610
|
basic: {
|
|
4637
4611
|
...templateComponent.basic,
|
|
4638
|
-
showLabel:
|
|
4612
|
+
showLabel: false // Hide label in datagrid cells
|
|
4639
4613
|
}
|
|
4640
4614
|
};
|
|
4641
4615
|
}
|
|
4642
4616
|
else {
|
|
4617
|
+
// Preserve the original ID to maintain form value connections
|
|
4643
4618
|
entryComponent = {
|
|
4644
4619
|
...entryComponent,
|
|
4645
|
-
id: entryComponent.id,
|
|
4620
|
+
id: entryComponent.id, // Keep the original ID
|
|
4646
4621
|
basic: {
|
|
4647
4622
|
...entryComponent.basic,
|
|
4648
|
-
showLabel:
|
|
4623
|
+
showLabel: false // Hide label in datagrid cells
|
|
4649
4624
|
}
|
|
4650
4625
|
};
|
|
4651
4626
|
}
|
|
4652
|
-
if (!entryComponent)
|
|
4653
|
-
return null;
|
|
4654
4627
|
return (jsxs("div", { style: {
|
|
4655
4628
|
display: 'flex',
|
|
4656
4629
|
flexDirection: 'column',
|
|
@@ -4686,7 +4659,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4686
4659
|
borderRadius: '8px',
|
|
4687
4660
|
display: 'flex',
|
|
4688
4661
|
justifyContent: 'center'
|
|
4689
|
-
}, children: jsxs("button", { onClick:
|
|
4662
|
+
}, children: jsxs("button", { onClick: onAddEntry, disabled: dataEntries.length >= maxEntries, style: {
|
|
4690
4663
|
padding: '8px 16px',
|
|
4691
4664
|
backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
|
|
4692
4665
|
color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
|
|
@@ -4699,19 +4672,11 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4699
4672
|
alignItems: 'center',
|
|
4700
4673
|
gap: '8px',
|
|
4701
4674
|
transition: 'all 0.2s ease'
|
|
4702
|
-
}, onMouseEnter: (e) => {
|
|
4703
|
-
if (dataEntries.length < maxEntries) {
|
|
4704
|
-
e.currentTarget.style.backgroundColor = '#059669';
|
|
4705
|
-
}
|
|
4706
|
-
}, onMouseLeave: (e) => {
|
|
4707
|
-
e.currentTarget.style.backgroundColor = '#10b981';
|
|
4708
4675
|
}, children: [jsx("span", { children: "+" }), addAnotherText] }) }))] }));
|
|
4709
4676
|
};
|
|
4710
4677
|
const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueChange, onSelect, isSelected = false, className = '', onDataGridSelect, onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, selectedComponent, renderFormComponent, onEntryAdd, onEntryRemove, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, shouldShowComponent }) => {
|
|
4711
4678
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
4712
4679
|
const hasInitialized = useRef(false);
|
|
4713
|
-
// Track local form values for entry components to prevent value loss during parent round-trip
|
|
4714
|
-
const localFormValuesRef = useRef({});
|
|
4715
4680
|
// Get all components in the grid and sanitize them to ensure no data leaks into templates
|
|
4716
4681
|
let gridComponents = (properties.templateComponents || []).map(comp => ({
|
|
4717
4682
|
...comp,
|
|
@@ -4789,6 +4754,87 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4789
4754
|
}
|
|
4790
4755
|
}
|
|
4791
4756
|
}
|
|
4757
|
+
else {
|
|
4758
|
+
// Update existing entries to include all template components and sync their properties
|
|
4759
|
+
const needsUpdate = dataEntries.some(entry => {
|
|
4760
|
+
// Check if entry is missing any template components or if existing components need updates
|
|
4761
|
+
return gridComponents.some((templateComp, componentIndex) => {
|
|
4762
|
+
const existingComponent = entry.components?.[componentIndex];
|
|
4763
|
+
if (!existingComponent) {
|
|
4764
|
+
return true; // Missing component at this index
|
|
4765
|
+
}
|
|
4766
|
+
// We don't check for ID matches here anymore
|
|
4767
|
+
// const expectedId = `${templateComp.id}-entry-${entry.index}-${componentIndex}`
|
|
4768
|
+
// const hasProperId = existingComponent.id === expectedId
|
|
4769
|
+
// Check if existing component needs to be updated with new template properties
|
|
4770
|
+
// Compare key properties that should be synced
|
|
4771
|
+
const needsPropertyUpdate = JSON.stringify(existingComponent.basic?.options) !== JSON.stringify(templateComp.basic?.options) ||
|
|
4772
|
+
existingComponent.basic?.placeholder !== templateComp.basic?.placeholder ||
|
|
4773
|
+
existingComponent.basic?.defaultValue !== templateComp.basic?.defaultValue ||
|
|
4774
|
+
existingComponent.basic?.label !== templateComp.basic?.label ||
|
|
4775
|
+
existingComponent.validation?.required !== templateComp.validation?.required;
|
|
4776
|
+
// We do not check for ID mismatch anymore as we want to preserve unique IDs
|
|
4777
|
+
return needsPropertyUpdate;
|
|
4778
|
+
});
|
|
4779
|
+
});
|
|
4780
|
+
if (needsUpdate && onValueChange) {
|
|
4781
|
+
const updatedEntries = dataEntries.map(entry => {
|
|
4782
|
+
// Use index-based matching to ensure each template component maps to the correct entry component
|
|
4783
|
+
const updatedComponents = gridComponents.map((templateComp, componentIndex) => {
|
|
4784
|
+
// Find existing component by index first
|
|
4785
|
+
let existingComponent = entry.components?.[componentIndex];
|
|
4786
|
+
// If no component at this index, try to find by name+label (for backward compatibility)
|
|
4787
|
+
if (!existingComponent) {
|
|
4788
|
+
existingComponent = entry.components?.find((comp) => comp.name === templateComp.name &&
|
|
4789
|
+
comp.basic?.label === templateComp.basic?.label);
|
|
4790
|
+
}
|
|
4791
|
+
// Always ensure a valid ID exists, but respect existing one if possible
|
|
4792
|
+
// const uniqueId = `${templateComp.id}-entry-${entry.index}-${componentIndex}`
|
|
4793
|
+
if (existingComponent) {
|
|
4794
|
+
// Update existing component with new template properties while ensuring unique ID and preserving form values
|
|
4795
|
+
const updatedComponent = {
|
|
4796
|
+
...templateComp,
|
|
4797
|
+
id: existingComponent.id, // Preserve existing ID !!
|
|
4798
|
+
basic: {
|
|
4799
|
+
...templateComp.basic,
|
|
4800
|
+
showLabel: false, // Hide label in datagrid cells
|
|
4801
|
+
// Preserve any user-entered values
|
|
4802
|
+
value: existingComponent.basic?.value || templateComp.basic?.defaultValue || ''
|
|
4803
|
+
}
|
|
4804
|
+
};
|
|
4805
|
+
return updatedComponent;
|
|
4806
|
+
}
|
|
4807
|
+
else {
|
|
4808
|
+
// Create new component based on template
|
|
4809
|
+
// Only for NEW components in existing entries (e.g. column added) do we generate a new ID
|
|
4810
|
+
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
|
|
4811
|
+
const newId = `${templateComp.id}-${uniqueSuffix}-${componentIndex}`;
|
|
4812
|
+
const newComponent = {
|
|
4813
|
+
...templateComp,
|
|
4814
|
+
id: newId,
|
|
4815
|
+
basic: {
|
|
4816
|
+
...templateComp.basic,
|
|
4817
|
+
showLabel: false // Hide label in datagrid cells
|
|
4818
|
+
}
|
|
4819
|
+
};
|
|
4820
|
+
return newComponent;
|
|
4821
|
+
}
|
|
4822
|
+
});
|
|
4823
|
+
return {
|
|
4824
|
+
...entry,
|
|
4825
|
+
components: updatedComponents
|
|
4826
|
+
};
|
|
4827
|
+
});
|
|
4828
|
+
const newValue = { ...properties, entries: updatedEntries };
|
|
4829
|
+
// Only call onValueChange if the data actually changed
|
|
4830
|
+
if (JSON.stringify(newValue) !== JSON.stringify(properties)) {
|
|
4831
|
+
onValueChange({
|
|
4832
|
+
id,
|
|
4833
|
+
value: newValue
|
|
4834
|
+
});
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4792
4838
|
}
|
|
4793
4839
|
}, [gridComponents, dataEntries, id, onValueChange, properties, mode, properties.templateComponents]);
|
|
4794
4840
|
const handleDataGridClick = useCallback((event) => {
|
|
@@ -4845,9 +4891,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4845
4891
|
// Handle component value change for form data updates (test mode)
|
|
4846
4892
|
const handleComponentValueChange = useCallback((change) => {
|
|
4847
4893
|
if (mode === 'test') {
|
|
4848
|
-
//
|
|
4849
|
-
|
|
4850
|
-
// Also propagate up to parent for persistence
|
|
4894
|
+
// In test mode, update form data through the parent's onValueChange
|
|
4895
|
+
// This allows the form data to be updated for interactive components
|
|
4851
4896
|
if (onValueChange) {
|
|
4852
4897
|
onValueChange({
|
|
4853
4898
|
id: change.id,
|
|
@@ -4858,10 +4903,6 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4858
4903
|
// In edit/preview modes, don't handle value changes as components are read-only
|
|
4859
4904
|
}, [mode, onValueChange]);
|
|
4860
4905
|
const handleAddEntry = useCallback(() => {
|
|
4861
|
-
console.log('[DfFormDataGrid] handleAddEntry called');
|
|
4862
|
-
console.log('[DfFormDataGrid] gridComponents:', gridComponents.length, gridComponents.map(c => c.id));
|
|
4863
|
-
console.log('[DfFormDataGrid] current entries:', properties.entries?.length);
|
|
4864
|
-
console.log('[DfFormDataGrid] onValueChange exists:', !!onValueChange);
|
|
4865
4906
|
// Use timestamp and random string to ensure uniqueness even if entries are deleted
|
|
4866
4907
|
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
|
|
4867
4908
|
const newEntryId = `entry-${uniqueSuffix}`;
|
|
@@ -4883,19 +4924,13 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4883
4924
|
}),
|
|
4884
4925
|
styles: {}
|
|
4885
4926
|
};
|
|
4886
|
-
console.log('[DfFormDataGrid] newEntry created:', newEntry.id, 'with', newEntry.components.length, 'components');
|
|
4887
4927
|
const updatedEntries = [...properties.entries, newEntry];
|
|
4888
|
-
console.log('[DfFormDataGrid] updatedEntries count:', updatedEntries.length);
|
|
4889
4928
|
if (onValueChange) {
|
|
4890
|
-
console.log('[DfFormDataGrid] calling onValueChange with updated entries');
|
|
4891
4929
|
onValueChange({
|
|
4892
4930
|
id,
|
|
4893
4931
|
value: { ...properties, entries: updatedEntries }
|
|
4894
4932
|
});
|
|
4895
4933
|
}
|
|
4896
|
-
else {
|
|
4897
|
-
console.log('[DfFormDataGrid] WARNING: onValueChange is not defined!');
|
|
4898
|
-
}
|
|
4899
4934
|
onEntryAdd?.();
|
|
4900
4935
|
}, [properties, onValueChange, id, onEntryAdd, gridComponents]);
|
|
4901
4936
|
const handleRemoveEntry = useCallback((entryIndex) => {
|
|
@@ -4911,11 +4946,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4911
4946
|
}, [properties, onValueChange, id, onEntryRemove]);
|
|
4912
4947
|
// Use our own render function to ensure proper onComponentUpdate handling
|
|
4913
4948
|
const renderComponent = useCallback((field, hideLabel = false) => {
|
|
4914
|
-
|
|
4915
|
-
// This ensures typed values in entry 2+ persist immediately
|
|
4916
|
-
const formValue = mode === 'test'
|
|
4917
|
-
? (localFormValuesRef.current[field.id] ?? formData[field.id] ?? field.basic?.value ?? ('defaultValue' in field.basic ? field.basic.defaultValue || '' : ''))
|
|
4918
|
-
: ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '');
|
|
4949
|
+
const formValue = mode === 'test' ? (formData[field.id] || ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '')) : ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '');
|
|
4919
4950
|
const commonProps = {
|
|
4920
4951
|
id: field.id,
|
|
4921
4952
|
properties: field,
|
|
@@ -4996,7 +5027,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4996
5027
|
, {
|
|
4997
5028
|
// Cast to FormComponentType[] to satisfy TableView typing; gridComponents
|
|
4998
5029
|
// are always child form components of the datagrid.
|
|
4999
|
-
templateComponents: gridComponents, dataEntries: dataEntries, renderFormComponent: renderFormComponent || renderComponent, mode: mode, allowAddRemoveEntries: properties.datagrid?.allowAddRemoveEntries ?? true, addAnotherText: properties.datagrid?.addAnotherText ?? 'Add Entry', removeText: properties.datagrid?.removeText ?? 'Remove', maxEntries: properties.datagrid?.maxEntries ?? 10, minEntries: properties.datagrid?.minEntries ?? 1, displayAsGrid: properties.datagrid?.displayAsGrid ?? true, onAddEntry: handleAddEntry, onRemoveEntry: handleRemoveEntry, formData: formData, formTemplateId: formTemplateId, onThresholdActionCompletion: onThresholdActionCompletion, onThresholdIssueRaised: onThresholdIssueRaised, onNotesChange: onNotesChange, onAttachmentChange: onAttachmentChange, columnView: properties.datagrid?.columnView, shouldShowComponent: shouldShowComponent })) }))] }));
|
|
5030
|
+
templateComponents: gridComponents, dataEntries: dataEntries, renderFormComponent: renderFormComponent || renderComponent, mode: mode, allowAddRemoveEntries: properties.datagrid?.allowAddRemoveEntries ?? true, addAnotherText: properties.datagrid?.addAnotherText ?? 'Add Entry', removeText: properties.datagrid?.removeText ?? 'Remove', maxEntries: properties.datagrid?.maxEntries ?? 10, minEntries: properties.datagrid?.minEntries ?? 1, displayAsGrid: properties.datagrid?.displayAsGrid ?? true, onAddEntry: onEntryAdd ? onEntryAdd : handleAddEntry, onRemoveEntry: onEntryRemove ? onEntryRemove : handleRemoveEntry, formData: formData, formTemplateId: formTemplateId, onThresholdActionCompletion: onThresholdActionCompletion, onThresholdIssueRaised: onThresholdIssueRaised, onNotesChange: onNotesChange, onAttachmentChange: onAttachmentChange, columnView: properties.datagrid?.columnView, shouldShowComponent: shouldShowComponent })) }))] }));
|
|
5000
5031
|
};
|
|
5001
5032
|
|
|
5002
5033
|
const DraggableChild = ({ child, selectedChild, mode, onChildSelect, onChildDelete, renderFormComponent, isOverlay = false, isChildrenEditMode = false, formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, workOrderNumber, assetNumber, user, onCreateIssue, onUpdateIssue }) => {
|
|
@@ -6419,9 +6450,12 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6419
6450
|
onFormDataChange?.(updatedComponents);
|
|
6420
6451
|
} }) }));
|
|
6421
6452
|
case 'datagrid':
|
|
6453
|
+
// Align package datagrid wiring with main app behaviour:
|
|
6454
|
+
// - Let DfFormDataGrid manage entry structure via onValueChange
|
|
6455
|
+
// - Use the shared onFormValueChange handler for nested field values
|
|
6456
|
+
// - Keep notes/attachments wiring as before
|
|
6422
6457
|
return (jsx(DfFormDataGrid, { ...commonProps, properties: component, formData: formValues, formTemplateId: formTemplateId, mode: commonProps.mode, onThresholdActionCompletion: handleThresholdActionCompletion, onThresholdIssueRaised: handleThresholdIssueRaised, onNotesChange: (componentId, notes) => {
|
|
6423
6458
|
handleComponentNotesChange(componentId, notes);
|
|
6424
|
-
// Handle notes change for datagrid entry components
|
|
6425
6459
|
const updatedComponents = localFormComponents.map(comp => {
|
|
6426
6460
|
if (comp.id === component.id && comp.entries) {
|
|
6427
6461
|
const updatedEntries = comp.entries.map((entry) => {
|
|
@@ -6449,7 +6483,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6449
6483
|
onFormDataChange?.(updatedComponents);
|
|
6450
6484
|
}, onAttachmentChange: (componentId, attachments) => {
|
|
6451
6485
|
handleComponentAttachmentChange(componentId, attachments);
|
|
6452
|
-
// Handle attachment change for datagrid entry components
|
|
6453
6486
|
const updatedComponents = localFormComponents.map(comp => {
|
|
6454
6487
|
if (comp.id === component.id && comp.entries) {
|
|
6455
6488
|
const updatedEntries = comp.entries.map((entry) => {
|
|
@@ -6475,116 +6508,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6475
6508
|
return comp;
|
|
6476
6509
|
});
|
|
6477
6510
|
onFormDataChange?.(updatedComponents);
|
|
6478
|
-
}, onValueChange: (
|
|
6479
|
-
console.log('[DfFormPreview] datagrid onValueChange received:', change.id, 'hasEntries:', change.value && typeof change.value === 'object' && 'entries' in change.value);
|
|
6480
|
-
// Handle datagrid value changes (entries updates)
|
|
6481
|
-
if (change.id === component.id && change.value && typeof change.value === 'object' && 'entries' in change.value) {
|
|
6482
|
-
console.log('[DfFormPreview] datagrid entries update - entries count:', change.value.entries?.length);
|
|
6483
|
-
// Update localFormComponents with new entries structure
|
|
6484
|
-
const updatedComponents = localFormComponents.map(comp => {
|
|
6485
|
-
if (comp.id === component.id) {
|
|
6486
|
-
return {
|
|
6487
|
-
...comp,
|
|
6488
|
-
...change.value
|
|
6489
|
-
};
|
|
6490
|
-
}
|
|
6491
|
-
return comp;
|
|
6492
|
-
});
|
|
6493
|
-
// CRITICAL: Update local state immediately so new entries render without Angular round-trip
|
|
6494
|
-
setLocalFormComponents(updatedComponents);
|
|
6495
|
-
onFormDataChange?.(updatedComponents);
|
|
6496
|
-
// Also update formValues for nested components
|
|
6497
|
-
if (change.value.entries && Array.isArray(change.value.entries)) {
|
|
6498
|
-
change.value.entries.forEach((entry) => {
|
|
6499
|
-
if (entry.components && Array.isArray(entry.components)) {
|
|
6500
|
-
entry.components.forEach((nestedComp) => {
|
|
6501
|
-
const nestedValue = formValues[nestedComp.id];
|
|
6502
|
-
if (nestedValue !== undefined) ;
|
|
6503
|
-
else {
|
|
6504
|
-
// Initialize with defaultValue if available
|
|
6505
|
-
const defaultValue = nestedComp.basic?.defaultValue;
|
|
6506
|
-
if (defaultValue !== undefined) {
|
|
6507
|
-
setFormValues(prev => ({
|
|
6508
|
-
...prev,
|
|
6509
|
-
[nestedComp.id]: defaultValue
|
|
6510
|
-
}));
|
|
6511
|
-
}
|
|
6512
|
-
}
|
|
6513
|
-
});
|
|
6514
|
-
}
|
|
6515
|
-
});
|
|
6516
|
-
}
|
|
6517
|
-
}
|
|
6518
|
-
else {
|
|
6519
|
-
// For nested component value changes, use the regular handler
|
|
6520
|
-
onFormValueChange(change);
|
|
6521
|
-
}
|
|
6522
|
-
}, onEntryAdd: () => {
|
|
6523
|
-
// CRITICAL: Entry has already been added via onValueChange in DfFormDataGrid
|
|
6524
|
-
// Get the updated component from localFormComponents (which should have been updated by onValueChange)
|
|
6525
|
-
const currentComponent = localFormComponents.find(comp => comp.id === component.id);
|
|
6526
|
-
if (currentComponent && currentComponent.entries) {
|
|
6527
|
-
// Entry should already be in the component via onValueChange
|
|
6528
|
-
// Just ensure localFormComponents is in sync (no-op if already synced)
|
|
6529
|
-
const updatedComponents = localFormComponents.map(comp => {
|
|
6530
|
-
if (comp.id === component.id) {
|
|
6531
|
-
// Ensure entries are properly structured
|
|
6532
|
-
return {
|
|
6533
|
-
...comp,
|
|
6534
|
-
entries: comp.entries || []
|
|
6535
|
-
};
|
|
6536
|
-
}
|
|
6537
|
-
return comp;
|
|
6538
|
-
});
|
|
6539
|
-
onFormDataChange?.(updatedComponents);
|
|
6540
|
-
}
|
|
6541
|
-
else {
|
|
6542
|
-
// Fallback: If component doesn't have entries yet, try to get from formValues
|
|
6543
|
-
setTimeout(() => {
|
|
6544
|
-
const datagridValue = formValues[component.id];
|
|
6545
|
-
if (datagridValue && typeof datagridValue === 'object' && 'entries' in datagridValue) {
|
|
6546
|
-
const updatedComponents = localFormComponents.map(comp => {
|
|
6547
|
-
if (comp.id === component.id) {
|
|
6548
|
-
return {
|
|
6549
|
-
...comp,
|
|
6550
|
-
entries: datagridValue.entries
|
|
6551
|
-
};
|
|
6552
|
-
}
|
|
6553
|
-
return comp;
|
|
6554
|
-
});
|
|
6555
|
-
onFormDataChange?.(updatedComponents);
|
|
6556
|
-
}
|
|
6557
|
-
}, 100);
|
|
6558
|
-
}
|
|
6559
|
-
}, onEntryRemove: (entryIndex) => {
|
|
6560
|
-
// Handle entry remove - update form components
|
|
6561
|
-
const updatedComponents = localFormComponents.map(comp => {
|
|
6562
|
-
if (comp.id === component.id && comp.entries) {
|
|
6563
|
-
const currentEntries = comp.entries || [];
|
|
6564
|
-
const updatedEntries = currentEntries
|
|
6565
|
-
.filter((_, index) => index !== entryIndex)
|
|
6566
|
-
.map((entry, index) => ({
|
|
6567
|
-
...entry,
|
|
6568
|
-
index,
|
|
6569
|
-
id: `entry-${comp.id}-${index}`,
|
|
6570
|
-
components: entry.components?.map((comp, compIndex) => {
|
|
6571
|
-
const templateComp = (comp.templateComponents || [])[compIndex];
|
|
6572
|
-
return {
|
|
6573
|
-
...comp,
|
|
6574
|
-
id: templateComp ? `${templateComp.id}-entry-${index}-${compIndex}` : comp.id
|
|
6575
|
-
};
|
|
6576
|
-
}) || []
|
|
6577
|
-
}));
|
|
6578
|
-
return {
|
|
6579
|
-
...comp,
|
|
6580
|
-
entries: updatedEntries
|
|
6581
|
-
};
|
|
6582
|
-
}
|
|
6583
|
-
return comp;
|
|
6584
|
-
});
|
|
6585
|
-
onFormDataChange?.(updatedComponents);
|
|
6586
|
-
}, renderFormComponent: (field) => {
|
|
6587
|
-
// Ensure the nested component gets the proper form value
|
|
6511
|
+
}, onValueChange: onFormValueChange, renderFormComponent: (field) => {
|
|
6588
6512
|
return renderFormComponent(field);
|
|
6589
6513
|
} }));
|
|
6590
6514
|
case 'file':
|