df-ae-forms-package 1.0.98 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +5 -16
- package/dist/index.esm.js +245 -86
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +243 -84
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4334,20 +4334,57 @@ const DraggableGridComponent = ({ component, selectedComponent, mode, onComponen
|
|
|
4334
4334
|
e.currentTarget.style.backgroundColor = '#ef4444';
|
|
4335
4335
|
}, children: jsxRuntime.jsx(lucideReact.Trash2, { size: 12 }) })] }))] }));
|
|
4336
4336
|
};
|
|
4337
|
-
|
|
4337
|
+
// Sub-component for the drop zone within the grid
|
|
4338
|
+
const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, selectedComponent, renderFormComponent, gridId, formData, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView, shouldShowComponent }) => {
|
|
4338
4339
|
const { setNodeRef, isOver } = core.useDroppable({
|
|
4339
4340
|
id: `grid-drop-zone-${gridId}`,
|
|
4340
|
-
disabled: mode !== 'edit'
|
|
4341
|
+
disabled: mode !== 'edit',
|
|
4342
|
+
data: {
|
|
4343
|
+
isGridDropZone: true,
|
|
4344
|
+
gridId: gridId
|
|
4345
|
+
}
|
|
4346
|
+
});
|
|
4347
|
+
// Sensors for drag and drop
|
|
4348
|
+
const sensors = core.useSensors(core.useSensor(core.PointerSensor, {
|
|
4349
|
+
activationConstraint: {
|
|
4350
|
+
distance: 8,
|
|
4351
|
+
},
|
|
4352
|
+
}), core.useSensor(core.KeyboardSensor, {
|
|
4353
|
+
coordinateGetter: sortable.sortableKeyboardCoordinates,
|
|
4354
|
+
}));
|
|
4355
|
+
const handleDragEnd = ((event) => {
|
|
4356
|
+
const { active, over } = event;
|
|
4357
|
+
if (over && active.id !== over.id) {
|
|
4358
|
+
gridComponents.findIndex((item) => item.id === active.id);
|
|
4359
|
+
gridComponents.findIndex((item) => item.id === over.id);
|
|
4360
|
+
}
|
|
4341
4361
|
});
|
|
4342
|
-
return (jsxRuntime.jsx("div", { ref: setNodeRef, className:
|
|
4343
|
-
border: isOver ? '2px dashed #3b82f6' : '
|
|
4362
|
+
return (jsxRuntime.jsx("div", { ref: setNodeRef, className: `grid-drop-zone ${gridComponents.length === 0 ? 'empty' : ''}`, style: {
|
|
4363
|
+
border: isOver ? '2px dashed #3b82f6' : '1px dashed #d1d5db',
|
|
4344
4364
|
borderRadius: '8px',
|
|
4345
4365
|
padding: '16px',
|
|
4346
|
-
backgroundColor: isOver ? 'var(--df-color-primary-light)' : '
|
|
4347
|
-
minHeight: '
|
|
4348
|
-
transition: 'all 0.2s ease'
|
|
4349
|
-
|
|
4350
|
-
|
|
4366
|
+
backgroundColor: isOver ? 'var(--df-color-primary-light)' : '#f9fafb',
|
|
4367
|
+
minHeight: '100px',
|
|
4368
|
+
transition: 'all 0.2s ease'
|
|
4369
|
+
}, children: gridComponents.length === 0 ? (jsxRuntime.jsxs("div", { style: {
|
|
4370
|
+
textAlign: 'center',
|
|
4371
|
+
color: 'var(--df-color-text-light)',
|
|
4372
|
+
fontSize: '14px',
|
|
4373
|
+
padding: '40px 20px',
|
|
4374
|
+
display: 'flex',
|
|
4375
|
+
flexDirection: 'column',
|
|
4376
|
+
alignItems: 'center',
|
|
4377
|
+
gap: '8px',
|
|
4378
|
+
backgroundColor: 'var(--df-color-fb-container)',
|
|
4379
|
+
border: '1px dashed var(--df-color-fb-border)',
|
|
4380
|
+
borderRadius: '8px'
|
|
4381
|
+
}, children: [jsxRuntime.jsx("div", { style: {
|
|
4382
|
+
fontWeight: '500',
|
|
4383
|
+
color: isOver ? 'var(--df-color-primary)' : 'var(--df-color-text-dark)'
|
|
4384
|
+
}, children: isOver ? 'Drop components here' : 'Empty DataGrid' }), jsxRuntime.jsx("div", { style: {
|
|
4385
|
+
fontSize: '12px',
|
|
4386
|
+
color: '#9ca3af'
|
|
4387
|
+
}, children: "Drag and drop components here to create your grid" })] })) : (jsxRuntime.jsxs(core.DndContext, { sensors: sensors, onDragEnd: handleDragEnd, children: [jsxRuntime.jsx(sortable.SortableContext, { items: gridComponents.map(c => c.id), strategy: columnView ? sortable.verticalListSortingStrategy : sortable.horizontalListSortingStrategy, children: jsxRuntime.jsx("div", { style: {
|
|
4351
4388
|
display: 'flex',
|
|
4352
4389
|
flexDirection: columnView ? 'column' : 'row',
|
|
4353
4390
|
flexWrap: 'nowrap',
|
|
@@ -4372,28 +4409,14 @@ const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDele
|
|
|
4372
4409
|
minHeight: '40px',
|
|
4373
4410
|
display: 'flex',
|
|
4374
4411
|
alignItems: 'center',
|
|
4375
|
-
justifyContent: 'center'
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
color: 'var(--df-color-text-light)',
|
|
4379
|
-
fontSize: '14px',
|
|
4380
|
-
padding: '40px 20px',
|
|
4381
|
-
display: 'flex',
|
|
4382
|
-
flexDirection: 'column',
|
|
4383
|
-
alignItems: 'center',
|
|
4384
|
-
gap: '8px',
|
|
4385
|
-
backgroundColor: 'var(--df-color-fb-container)',
|
|
4386
|
-
border: '1px dashed var(--df-color-fb-border)',
|
|
4387
|
-
borderRadius: '8px'
|
|
4388
|
-
}, children: [jsxRuntime.jsx("div", { style: {
|
|
4389
|
-
fontWeight: '500',
|
|
4390
|
-
color: isOver ? 'var(--df-color-primary)' : 'var(--df-color-text-dark)'
|
|
4391
|
-
}, children: isOver ? 'Drop components here' : 'Empty DataGrid' }), jsxRuntime.jsx("div", { style: {
|
|
4392
|
-
fontSize: '12px',
|
|
4393
|
-
color: '#9ca3af'
|
|
4394
|
-
}, children: "Drag and drop components here to create your grid" })] })) }));
|
|
4412
|
+
justifyContent: 'center',
|
|
4413
|
+
marginTop: '12px'
|
|
4414
|
+
}, children: isOver ? (jsxRuntime.jsx("span", { style: { color: '#3b82f6', fontWeight: '500' }, children: "Drop component here to add to grid" })) : (jsxRuntime.jsx("span", { children: "+ Drop more components here" })) })] })) }));
|
|
4395
4415
|
};
|
|
4396
|
-
|
|
4416
|
+
// Sub-component for displaying entries (TableView)
|
|
4417
|
+
const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode, allowAddRemoveEntries, addAnotherText, removeText, maxEntries, minEntries, displayAsGrid = true, onAddEntry, onRemoveEntry, formData, // Use current formData to render values
|
|
4418
|
+
formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView, shouldShowComponent }) => {
|
|
4419
|
+
const _formData = formData || {};
|
|
4397
4420
|
const visibleTemplateComponents = React.useMemo(() => {
|
|
4398
4421
|
if (!shouldShowComponent)
|
|
4399
4422
|
return templateComponents;
|
|
@@ -4455,21 +4478,23 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4455
4478
|
whiteSpace: 'nowrap',
|
|
4456
4479
|
overflow: 'hidden',
|
|
4457
4480
|
textOverflow: 'ellipsis'
|
|
4458
|
-
}, children: component.basic?.label || `Column ${index + 1}` }, `header-${component.id}`))) })), dataEntries.length > 0 ? (dataEntries.map((entry, entryIndex) => (jsxRuntime.jsxs("div", { className: "table-row", style: {
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4481
|
+
}, children: component.basic?.label || `Column ${index + 1}` }, `header-${component.id || index}`))) })), dataEntries.length > 0 ? (dataEntries.map((entry, entryIndex) => (jsxRuntime.jsxs("div", { className: "table-row", style: {
|
|
4482
|
+
// Use flex column for column view, grid for row view
|
|
4483
|
+
display: columnView ? 'flex' : 'grid',
|
|
4484
|
+
flexDirection: columnView ? 'column' : 'row',
|
|
4485
|
+
gridTemplateColumns: !columnView
|
|
4486
|
+
? `repeat(${visibleTemplateComponents.length}, minmax(150px, 1fr))`
|
|
4487
|
+
: undefined,
|
|
4464
4488
|
borderBottom: entryIndex < dataEntries.length - 1 ? '1px solid var(--df-color-fb-border)' : 'none',
|
|
4465
4489
|
backgroundColor: entryIndex % 2 === 0 ? 'var(--df-color-fb-container)' : 'var(--df-color-fb-bg)',
|
|
4466
4490
|
position: 'relative',
|
|
4467
|
-
minWidth: columnView ? '100%' : `${visibleTemplateComponents.length * 150}px
|
|
4491
|
+
minWidth: columnView ? '100%' : `${visibleTemplateComponents.length * 150}px`,
|
|
4492
|
+
padding: columnView ? '16px' : '0'
|
|
4468
4493
|
}, children: [visibleTemplateComponents.map((templateComponent, componentIndex) => {
|
|
4469
4494
|
let entryComponent = entry.components?.[componentIndex];
|
|
4470
4495
|
if (!entryComponent) {
|
|
4471
4496
|
// Use entry.id (which is unique) instead of entryIndex to prevent data collisions
|
|
4472
|
-
const uniqueId = `${templateComponent.id}-${entry.id}-${componentIndex}`;
|
|
4497
|
+
const uniqueId = `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`;
|
|
4473
4498
|
entryComponent = {
|
|
4474
4499
|
...templateComponent,
|
|
4475
4500
|
id: uniqueId,
|
|
@@ -4482,7 +4507,7 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4482
4507
|
else {
|
|
4483
4508
|
entryComponent = {
|
|
4484
4509
|
...entryComponent,
|
|
4485
|
-
id: entryComponent.id
|
|
4510
|
+
id: entryComponent.id || `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`,
|
|
4486
4511
|
basic: {
|
|
4487
4512
|
...entryComponent.basic,
|
|
4488
4513
|
showLabel: columnView // Show label in column view
|
|
@@ -4490,21 +4515,24 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4490
4515
|
};
|
|
4491
4516
|
}
|
|
4492
4517
|
return (jsxRuntime.jsx("div", { style: {
|
|
4493
|
-
padding: '12px 16px',
|
|
4518
|
+
padding: columnView ? '12px 0' : '12px 16px',
|
|
4494
4519
|
borderRight: !columnView && componentIndex < visibleTemplateComponents.length - 1 ? '1px solid var(--df-color-fb-border)' : 'none',
|
|
4495
4520
|
// Add bottom border for fields in column view except the last one
|
|
4496
4521
|
borderBottom: columnView && componentIndex < visibleTemplateComponents.length - 1 ? '1px dashed var(--df-color-fb-border)' : 'none',
|
|
4497
|
-
minHeight: '60px',
|
|
4522
|
+
minHeight: columnView ? 'auto' : '60px',
|
|
4498
4523
|
minWidth: columnView ? '100%' : '150px',
|
|
4524
|
+
width: columnView ? '100%' : 'auto',
|
|
4499
4525
|
display: 'flex',
|
|
4500
|
-
|
|
4501
|
-
|
|
4526
|
+
flexDirection: columnView ? 'column' : 'row',
|
|
4527
|
+
alignItems: columnView ? 'stretch' : 'center',
|
|
4528
|
+
overflow: 'hidden',
|
|
4529
|
+
gap: columnView ? '8px' : '0'
|
|
4502
4530
|
}, children: jsxRuntime.jsx("div", { style: {
|
|
4503
4531
|
width: '100%',
|
|
4504
|
-
minWidth: '120px',
|
|
4532
|
+
minWidth: columnView ? '100%' : '120px',
|
|
4505
4533
|
overflow: 'hidden'
|
|
4506
4534
|
}, children: renderFormComponent(entryComponent, !columnView) }) }, `${entry.id}-${componentIndex}`));
|
|
4507
|
-
}), mode === 'test' && allowAddRemoveEntries && dataEntries.length > minEntries && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry
|
|
4535
|
+
}), mode === 'test' && allowAddRemoveEntries && dataEntries.length > minEntries && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry(entryIndex), disabled: dataEntries.length <= minEntries, style: {
|
|
4508
4536
|
position: 'absolute',
|
|
4509
4537
|
top: '8px',
|
|
4510
4538
|
right: '8px',
|
|
@@ -4538,7 +4566,7 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4538
4566
|
position: 'sticky',
|
|
4539
4567
|
bottom: 0,
|
|
4540
4568
|
zIndex: 5
|
|
4541
|
-
}, children: jsxRuntime.jsxs("button", { onClick: onAddEntry, disabled: dataEntries.length >= maxEntries, style: {
|
|
4569
|
+
}, children: jsxRuntime.jsxs("button", { onClick: () => { console.log('[TableView] Add Entry button clicked (grid view)', 'entries:', dataEntries.length, 'max:', maxEntries); onAddEntry(); }, disabled: dataEntries.length >= maxEntries, style: {
|
|
4542
4570
|
padding: '8px 16px',
|
|
4543
4571
|
backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
|
|
4544
4572
|
color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
|
|
@@ -4576,7 +4604,7 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4576
4604
|
fontWeight: '600',
|
|
4577
4605
|
color: 'var(--df-color-text-dark)',
|
|
4578
4606
|
fontSize: '14px'
|
|
4579
|
-
}, children: ["Entry #", entryIndex + 1] }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > 1 && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry
|
|
4607
|
+
}, children: ["Entry #", entryIndex + 1] }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > 1 && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry(entryIndex), disabled: dataEntries.length <= minEntries, style: {
|
|
4580
4608
|
padding: '4px 8px',
|
|
4581
4609
|
backgroundColor: dataEntries.length <= minEntries ? '#f3f4f6' : '#ef4444',
|
|
4582
4610
|
color: dataEntries.length <= minEntries ? '#9ca3af' : '#ffffff',
|
|
@@ -4592,40 +4620,39 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4592
4620
|
height: '24px',
|
|
4593
4621
|
justifyContent: 'center'
|
|
4594
4622
|
}, title: removeText, children: jsxRuntime.jsx("span", { style: { fontSize: '14px' }, children: "\u00D7" }) }))] }), jsxRuntime.jsx("div", { style: {
|
|
4595
|
-
display: 'grid',
|
|
4596
|
-
|
|
4623
|
+
display: columnView ? 'flex' : 'grid',
|
|
4624
|
+
flexDirection: columnView ? 'column' : 'row',
|
|
4625
|
+
gridTemplateColumns: !columnView ? 'repeat(auto-fit, minmax(200px, 1fr))' : undefined,
|
|
4597
4626
|
gap: '16px'
|
|
4598
4627
|
}, children: visibleTemplateComponents.map((templateComponent, componentIndex) => {
|
|
4599
|
-
// Find the corresponding component in this entry by index first, then by name+label
|
|
4600
4628
|
let entryComponent = entry.components?.[componentIndex];
|
|
4601
|
-
// If no component at this index, try to find by name+label
|
|
4602
4629
|
if (!entryComponent) {
|
|
4603
4630
|
entryComponent = entry.components?.find((comp) => comp.name === templateComponent.name &&
|
|
4604
4631
|
comp.basic?.label === templateComponent.basic?.label);
|
|
4605
4632
|
}
|
|
4606
4633
|
if (!entryComponent) {
|
|
4607
|
-
|
|
4608
|
-
const uniqueId = `${templateComponent.id}-${entry.id}-${componentIndex}`;
|
|
4634
|
+
const uniqueId = `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`;
|
|
4609
4635
|
entryComponent = {
|
|
4610
4636
|
...templateComponent,
|
|
4611
4637
|
id: uniqueId,
|
|
4612
4638
|
basic: {
|
|
4613
4639
|
...templateComponent.basic,
|
|
4614
|
-
showLabel:
|
|
4640
|
+
showLabel: columnView // Show label in column view, hide in grid view
|
|
4615
4641
|
}
|
|
4616
4642
|
};
|
|
4617
4643
|
}
|
|
4618
4644
|
else {
|
|
4619
|
-
// Preserve the original ID to maintain form value connections
|
|
4620
4645
|
entryComponent = {
|
|
4621
4646
|
...entryComponent,
|
|
4622
|
-
id: entryComponent.id
|
|
4647
|
+
id: entryComponent.id || `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`,
|
|
4623
4648
|
basic: {
|
|
4624
4649
|
...entryComponent.basic,
|
|
4625
|
-
showLabel:
|
|
4650
|
+
showLabel: columnView // Show label in column view, hide in grid view
|
|
4626
4651
|
}
|
|
4627
4652
|
};
|
|
4628
4653
|
}
|
|
4654
|
+
if (!entryComponent)
|
|
4655
|
+
return null;
|
|
4629
4656
|
return (jsxRuntime.jsxs("div", { style: {
|
|
4630
4657
|
display: 'flex',
|
|
4631
4658
|
flexDirection: 'column',
|
|
@@ -4661,7 +4688,7 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4661
4688
|
borderRadius: '8px',
|
|
4662
4689
|
display: 'flex',
|
|
4663
4690
|
justifyContent: 'center'
|
|
4664
|
-
}, children: jsxRuntime.jsxs("button", { onClick: onAddEntry, disabled: dataEntries.length >= maxEntries, style: {
|
|
4691
|
+
}, children: jsxRuntime.jsxs("button", { onClick: () => { console.log('[TableView] Add Entry button clicked (list view)', 'entries:', dataEntries.length, 'max:', maxEntries); onAddEntry(); }, disabled: dataEntries.length >= maxEntries, style: {
|
|
4665
4692
|
padding: '8px 16px',
|
|
4666
4693
|
backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
|
|
4667
4694
|
color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
|
|
@@ -4674,14 +4701,21 @@ const TableView = ({ templateComponents, dataEntries, renderFormComponent, mode
|
|
|
4674
4701
|
alignItems: 'center',
|
|
4675
4702
|
gap: '8px',
|
|
4676
4703
|
transition: 'all 0.2s ease'
|
|
4704
|
+
}, onMouseEnter: (e) => {
|
|
4705
|
+
if (dataEntries.length < maxEntries) {
|
|
4706
|
+
e.currentTarget.style.backgroundColor = '#059669';
|
|
4707
|
+
}
|
|
4708
|
+
}, onMouseLeave: (e) => {
|
|
4709
|
+
e.currentTarget.style.backgroundColor = '#10b981';
|
|
4677
4710
|
}, children: [jsxRuntime.jsx("span", { children: "+" }), addAnotherText] }) }))] }));
|
|
4678
4711
|
};
|
|
4679
4712
|
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 }) => {
|
|
4680
4713
|
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
|
4681
4714
|
const hasInitialized = React.useRef(false);
|
|
4682
4715
|
// Get all components in the grid and sanitize them to ensure no data leaks into templates
|
|
4683
|
-
let gridComponents = (properties.templateComponents || []).map(comp => ({
|
|
4716
|
+
let gridComponents = (properties.templateComponents || []).map((comp, index) => ({
|
|
4684
4717
|
...comp,
|
|
4718
|
+
id: comp.id || `${id}-template-${index}`, // CRITICAL: Ensure template components have stable IDs
|
|
4685
4719
|
basic: {
|
|
4686
4720
|
...comp.basic,
|
|
4687
4721
|
// Use an empty string instead of undefined to satisfy component typing
|
|
@@ -4692,9 +4726,10 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4692
4726
|
const dataEntries = properties.entries || [];
|
|
4693
4727
|
// Fallback: If no template components but we have entries, extract template from first entry
|
|
4694
4728
|
if (gridComponents.length === 0 && dataEntries.length > 0 && dataEntries[0].components) {
|
|
4695
|
-
gridComponents = dataEntries[0].components.map((comp) => ({
|
|
4729
|
+
gridComponents = dataEntries[0].components.map((comp, index) => ({
|
|
4696
4730
|
...comp,
|
|
4697
|
-
|
|
4731
|
+
// Remove entry suffix for template, use stable ID if missing
|
|
4732
|
+
id: comp.id?.replace(/-entry-.*$/, '') || `${id}-template-${index}`,
|
|
4698
4733
|
basic: {
|
|
4699
4734
|
...comp.basic,
|
|
4700
4735
|
// Clear any data values while keeping types happy
|
|
@@ -4762,12 +4797,9 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4762
4797
|
// Check if entry is missing any template components or if existing components need updates
|
|
4763
4798
|
return gridComponents.some((templateComp, componentIndex) => {
|
|
4764
4799
|
const existingComponent = entry.components?.[componentIndex];
|
|
4765
|
-
if (!existingComponent) {
|
|
4766
|
-
return true; // Missing component at this index
|
|
4800
|
+
if (!existingComponent || !existingComponent.id) {
|
|
4801
|
+
return true; // Missing component or missing ID at this index
|
|
4767
4802
|
}
|
|
4768
|
-
// We don't check for ID matches here anymore
|
|
4769
|
-
// const expectedId = `${templateComp.id}-entry-${entry.index}-${componentIndex}`
|
|
4770
|
-
// const hasProperId = existingComponent.id === expectedId
|
|
4771
4803
|
// Check if existing component needs to be updated with new template properties
|
|
4772
4804
|
// Compare key properties that should be synced
|
|
4773
4805
|
const needsPropertyUpdate = JSON.stringify(existingComponent.basic?.options) !== JSON.stringify(templateComp.basic?.options) ||
|
|
@@ -4775,7 +4807,6 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4775
4807
|
existingComponent.basic?.defaultValue !== templateComp.basic?.defaultValue ||
|
|
4776
4808
|
existingComponent.basic?.label !== templateComp.basic?.label ||
|
|
4777
4809
|
existingComponent.validation?.required !== templateComp.validation?.required;
|
|
4778
|
-
// We do not check for ID mismatch anymore as we want to preserve unique IDs
|
|
4779
4810
|
return needsPropertyUpdate;
|
|
4780
4811
|
});
|
|
4781
4812
|
});
|
|
@@ -4796,7 +4827,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4796
4827
|
// Update existing component with new template properties while ensuring unique ID and preserving form values
|
|
4797
4828
|
const updatedComponent = {
|
|
4798
4829
|
...templateComp,
|
|
4799
|
-
|
|
4830
|
+
// Preserve existing ID or generate one if missing
|
|
4831
|
+
id: existingComponent.id || `${templateComp.id}-${entry.id}-${componentIndex}`,
|
|
4800
4832
|
basic: {
|
|
4801
4833
|
...templateComp.basic,
|
|
4802
4834
|
showLabel: false, // Hide label in datagrid cells
|
|
@@ -4905,6 +4937,10 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4905
4937
|
// In edit/preview modes, don't handle value changes as components are read-only
|
|
4906
4938
|
}, [mode, onValueChange]);
|
|
4907
4939
|
const handleAddEntry = React.useCallback(() => {
|
|
4940
|
+
console.log('[DfFormDataGrid] handleAddEntry called');
|
|
4941
|
+
console.log('[DfFormDataGrid] gridComponents:', gridComponents.length, gridComponents.map(c => c.id));
|
|
4942
|
+
console.log('[DfFormDataGrid] current entries:', properties.entries?.length);
|
|
4943
|
+
console.log('[DfFormDataGrid] onValueChange exists:', !!onValueChange);
|
|
4908
4944
|
// Use timestamp and random string to ensure uniqueness even if entries are deleted
|
|
4909
4945
|
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
|
|
4910
4946
|
const newEntryId = `entry-${uniqueSuffix}`;
|
|
@@ -4926,13 +4962,19 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4926
4962
|
}),
|
|
4927
4963
|
styles: {}
|
|
4928
4964
|
};
|
|
4965
|
+
console.log('[DfFormDataGrid] newEntry created:', newEntry.id, 'with', newEntry.components.length, 'components');
|
|
4929
4966
|
const updatedEntries = [...properties.entries, newEntry];
|
|
4967
|
+
console.log('[DfFormDataGrid] updatedEntries count:', updatedEntries.length);
|
|
4930
4968
|
if (onValueChange) {
|
|
4969
|
+
console.log('[DfFormDataGrid] calling onValueChange with updated entries');
|
|
4931
4970
|
onValueChange({
|
|
4932
4971
|
id,
|
|
4933
4972
|
value: { ...properties, entries: updatedEntries }
|
|
4934
4973
|
});
|
|
4935
4974
|
}
|
|
4975
|
+
else {
|
|
4976
|
+
console.log('[DfFormDataGrid] WARNING: onValueChange is not defined!');
|
|
4977
|
+
}
|
|
4936
4978
|
onEntryAdd?.();
|
|
4937
4979
|
}, [properties, onValueChange, id, onEntryAdd, gridComponents]);
|
|
4938
4980
|
const handleRemoveEntry = React.useCallback((entryIndex) => {
|
|
@@ -5029,7 +5071,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
5029
5071
|
, {
|
|
5030
5072
|
// Cast to FormComponentType[] to satisfy TableView typing; gridComponents
|
|
5031
5073
|
// are always child form components of the datagrid.
|
|
5032
|
-
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:
|
|
5074
|
+
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 })) }))] }));
|
|
5033
5075
|
};
|
|
5034
5076
|
|
|
5035
5077
|
const DraggableChild = ({ child, selectedChild, mode, onChildSelect, onChildDelete, renderFormComponent, isOverlay = false, isChildrenEditMode = false, formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, workOrderNumber, assetNumber, user, onCreateIssue, onUpdateIssue }) => {
|
|
@@ -5420,7 +5462,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5420
5462
|
});
|
|
5421
5463
|
};
|
|
5422
5464
|
// Validate component IDs for uniqueness without mutating original props
|
|
5423
|
-
const getValidatedComponents = (components) => {
|
|
5465
|
+
const getValidatedComponents = (components, parentId) => {
|
|
5424
5466
|
return components.map((component, index) => {
|
|
5425
5467
|
let validatedComponent = { ...component };
|
|
5426
5468
|
// Ensure component has an ID
|
|
@@ -5429,33 +5471,42 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5429
5471
|
const name = validatedComponent.name || 'component';
|
|
5430
5472
|
const label = validatedComponent.basic?.label || '';
|
|
5431
5473
|
const safeLabel = label.replace(/[^a-zA-Z0-9]/g, '').toLowerCase().substring(0, 10);
|
|
5432
|
-
// Generate a unique ID
|
|
5433
|
-
|
|
5474
|
+
// Generate a stable unique ID if parentId is provided, otherwise fallback to semi-stable
|
|
5475
|
+
if (parentId) {
|
|
5476
|
+
validatedComponent.id = `${parentId}-${name}-${index}`;
|
|
5477
|
+
}
|
|
5478
|
+
else {
|
|
5479
|
+
// For root components, use label if available for stability
|
|
5480
|
+
const labelPart = safeLabel ? `-${safeLabel}` : '';
|
|
5481
|
+
validatedComponent.id = `${name}${labelPart}-${index}`;
|
|
5482
|
+
}
|
|
5434
5483
|
console.warn(`[DfFormPreview] Assigned missing ID: ${validatedComponent.id}`);
|
|
5435
5484
|
}
|
|
5436
5485
|
else {
|
|
5437
5486
|
// Check for duplicates
|
|
5438
5487
|
if (seenIds.has(validatedComponent.id)) {
|
|
5439
5488
|
console.error(`[DfFormPreview] Duplicate component ID detected: ${validatedComponent.id}`);
|
|
5440
|
-
// Generate a unique ID for duplicate
|
|
5441
|
-
validatedComponent.id = `${validatedComponent.id}-
|
|
5489
|
+
// Generate a unique ID for duplicate - using index to keep it somewhat stable
|
|
5490
|
+
validatedComponent.id = `${validatedComponent.id}-dup-${index}`;
|
|
5442
5491
|
console.warn(`[DfFormPreview] Generated new unique ID: ${validatedComponent.id}`);
|
|
5443
5492
|
}
|
|
5444
5493
|
}
|
|
5445
5494
|
seenIds.add(validatedComponent.id);
|
|
5446
5495
|
// Recursively validate nested components
|
|
5447
5496
|
if (validatedComponent.children && Array.isArray(validatedComponent.children)) {
|
|
5448
|
-
validatedComponent.children = getValidatedComponents(validatedComponent.children);
|
|
5497
|
+
validatedComponent.children = getValidatedComponents(validatedComponent.children, validatedComponent.id);
|
|
5449
5498
|
}
|
|
5450
5499
|
if (validatedComponent.cells && Array.isArray(validatedComponent.cells)) {
|
|
5451
|
-
validatedComponent.cells = validatedComponent.cells.map((row) => {
|
|
5500
|
+
validatedComponent.cells = validatedComponent.cells.map((row, rowIndex) => {
|
|
5452
5501
|
const normalizedRow = normalizeTableRow(row);
|
|
5453
5502
|
if (normalizedRow.length > 0) {
|
|
5454
|
-
return normalizedRow.map((cell) => {
|
|
5503
|
+
return normalizedRow.map((cell, cellIndex) => {
|
|
5455
5504
|
if (cell && cell.components && Array.isArray(cell.components)) {
|
|
5505
|
+
// Stable ID for table cells: tableId-row-X-cell-Y
|
|
5506
|
+
const cellContextId = `${validatedComponent.id}-row-${rowIndex}-cell-${cellIndex}`;
|
|
5456
5507
|
return {
|
|
5457
5508
|
...cell,
|
|
5458
|
-
components: getValidatedComponents(cell.components)
|
|
5509
|
+
components: getValidatedComponents(cell.components, cellContextId)
|
|
5459
5510
|
};
|
|
5460
5511
|
}
|
|
5461
5512
|
return cell;
|
|
@@ -5467,9 +5518,10 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5467
5518
|
if (validatedComponent.entries && Array.isArray(validatedComponent.entries)) {
|
|
5468
5519
|
validatedComponent.entries = validatedComponent.entries.map((entry) => {
|
|
5469
5520
|
if (entry && entry.components && Array.isArray(entry.components)) {
|
|
5521
|
+
// Stable ID for datagrid entries: datagridId-entryId or using entry.id
|
|
5470
5522
|
return {
|
|
5471
5523
|
...entry,
|
|
5472
|
-
components: getValidatedComponents(entry.components)
|
|
5524
|
+
components: getValidatedComponents(entry.components, entry.id || `${validatedComponent.id}-entry-${entry.index}`)
|
|
5473
5525
|
};
|
|
5474
5526
|
}
|
|
5475
5527
|
return entry;
|
|
@@ -6452,12 +6504,9 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6452
6504
|
onFormDataChange?.(updatedComponents);
|
|
6453
6505
|
} }) }));
|
|
6454
6506
|
case 'datagrid':
|
|
6455
|
-
// Align package datagrid wiring with main app behaviour:
|
|
6456
|
-
// - Let DfFormDataGrid manage entry structure via onValueChange
|
|
6457
|
-
// - Use the shared onFormValueChange handler for nested field values
|
|
6458
|
-
// - Keep notes/attachments wiring as before
|
|
6459
6507
|
return (jsxRuntime.jsx(DfFormDataGrid, { ...commonProps, properties: component, formData: formValues, formTemplateId: formTemplateId, mode: commonProps.mode, onThresholdActionCompletion: handleThresholdActionCompletion, onThresholdIssueRaised: handleThresholdIssueRaised, onNotesChange: (componentId, notes) => {
|
|
6460
6508
|
handleComponentNotesChange(componentId, notes);
|
|
6509
|
+
// Handle notes change for datagrid entry components
|
|
6461
6510
|
const updatedComponents = localFormComponents.map(comp => {
|
|
6462
6511
|
if (comp.id === component.id && comp.entries) {
|
|
6463
6512
|
const updatedEntries = comp.entries.map((entry) => {
|
|
@@ -6485,6 +6534,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6485
6534
|
onFormDataChange?.(updatedComponents);
|
|
6486
6535
|
}, onAttachmentChange: (componentId, attachments) => {
|
|
6487
6536
|
handleComponentAttachmentChange(componentId, attachments);
|
|
6537
|
+
// Handle attachment change for datagrid entry components
|
|
6488
6538
|
const updatedComponents = localFormComponents.map(comp => {
|
|
6489
6539
|
if (comp.id === component.id && comp.entries) {
|
|
6490
6540
|
const updatedEntries = comp.entries.map((entry) => {
|
|
@@ -6510,7 +6560,116 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6510
6560
|
return comp;
|
|
6511
6561
|
});
|
|
6512
6562
|
onFormDataChange?.(updatedComponents);
|
|
6513
|
-
}, onValueChange:
|
|
6563
|
+
}, onValueChange: (change) => {
|
|
6564
|
+
console.log('[DfFormPreview] datagrid onValueChange received:', change.id, 'hasEntries:', change.value && typeof change.value === 'object' && 'entries' in change.value);
|
|
6565
|
+
// Handle datagrid value changes (entries updates)
|
|
6566
|
+
if (change.id === component.id && change.value && typeof change.value === 'object' && 'entries' in change.value) {
|
|
6567
|
+
console.log('[DfFormPreview] datagrid entries update - entries count:', change.value.entries?.length);
|
|
6568
|
+
// Update localFormComponents with new entries structure
|
|
6569
|
+
const updatedComponents = localFormComponents.map(comp => {
|
|
6570
|
+
if (comp.id === component.id) {
|
|
6571
|
+
return {
|
|
6572
|
+
...comp,
|
|
6573
|
+
...change.value
|
|
6574
|
+
};
|
|
6575
|
+
}
|
|
6576
|
+
return comp;
|
|
6577
|
+
});
|
|
6578
|
+
// CRITICAL: Update local state immediately so new entries render without Angular round-trip
|
|
6579
|
+
setLocalFormComponents(updatedComponents);
|
|
6580
|
+
onFormDataChange?.(updatedComponents);
|
|
6581
|
+
// Also update formValues for nested components
|
|
6582
|
+
if (change.value.entries && Array.isArray(change.value.entries)) {
|
|
6583
|
+
change.value.entries.forEach((entry) => {
|
|
6584
|
+
if (entry.components && Array.isArray(entry.components)) {
|
|
6585
|
+
entry.components.forEach((nestedComp) => {
|
|
6586
|
+
const nestedValue = formValues[nestedComp.id];
|
|
6587
|
+
if (nestedValue !== undefined) ;
|
|
6588
|
+
else {
|
|
6589
|
+
// Initialize with defaultValue if available
|
|
6590
|
+
const defaultValue = nestedComp.basic?.defaultValue;
|
|
6591
|
+
if (defaultValue !== undefined) {
|
|
6592
|
+
setFormValues(prev => ({
|
|
6593
|
+
...prev,
|
|
6594
|
+
[nestedComp.id]: defaultValue
|
|
6595
|
+
}));
|
|
6596
|
+
}
|
|
6597
|
+
}
|
|
6598
|
+
});
|
|
6599
|
+
}
|
|
6600
|
+
});
|
|
6601
|
+
}
|
|
6602
|
+
}
|
|
6603
|
+
else {
|
|
6604
|
+
// For nested component value changes, use the regular handler
|
|
6605
|
+
onFormValueChange(change);
|
|
6606
|
+
}
|
|
6607
|
+
}, onEntryAdd: () => {
|
|
6608
|
+
// CRITICAL: Entry has already been added via onValueChange in DfFormDataGrid
|
|
6609
|
+
// Get the updated component from localFormComponents (which should have been updated by onValueChange)
|
|
6610
|
+
const currentComponent = localFormComponents.find(comp => comp.id === component.id);
|
|
6611
|
+
if (currentComponent && currentComponent.entries) {
|
|
6612
|
+
// Entry should already be in the component via onValueChange
|
|
6613
|
+
// Just ensure localFormComponents is in sync (no-op if already synced)
|
|
6614
|
+
const updatedComponents = localFormComponents.map(comp => {
|
|
6615
|
+
if (comp.id === component.id) {
|
|
6616
|
+
// Ensure entries are properly structured
|
|
6617
|
+
return {
|
|
6618
|
+
...comp,
|
|
6619
|
+
entries: comp.entries || []
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
return comp;
|
|
6623
|
+
});
|
|
6624
|
+
onFormDataChange?.(updatedComponents);
|
|
6625
|
+
}
|
|
6626
|
+
else {
|
|
6627
|
+
// Fallback: If component doesn't have entries yet, try to get from formValues
|
|
6628
|
+
setTimeout(() => {
|
|
6629
|
+
const datagridValue = formValues[component.id];
|
|
6630
|
+
if (datagridValue && typeof datagridValue === 'object' && 'entries' in datagridValue) {
|
|
6631
|
+
const updatedComponents = localFormComponents.map(comp => {
|
|
6632
|
+
if (comp.id === component.id) {
|
|
6633
|
+
return {
|
|
6634
|
+
...comp,
|
|
6635
|
+
entries: datagridValue.entries
|
|
6636
|
+
};
|
|
6637
|
+
}
|
|
6638
|
+
return comp;
|
|
6639
|
+
});
|
|
6640
|
+
onFormDataChange?.(updatedComponents);
|
|
6641
|
+
}
|
|
6642
|
+
}, 100);
|
|
6643
|
+
}
|
|
6644
|
+
}, onEntryRemove: (entryIndex) => {
|
|
6645
|
+
// Handle entry remove - update form components
|
|
6646
|
+
const updatedComponents = localFormComponents.map(comp => {
|
|
6647
|
+
if (comp.id === component.id && comp.entries) {
|
|
6648
|
+
const currentEntries = comp.entries || [];
|
|
6649
|
+
const updatedEntries = currentEntries
|
|
6650
|
+
.filter((_, index) => index !== entryIndex)
|
|
6651
|
+
.map((entry, index) => ({
|
|
6652
|
+
...entry,
|
|
6653
|
+
index,
|
|
6654
|
+
id: `entry-${comp.id}-${index}`,
|
|
6655
|
+
components: entry.components?.map((comp, compIndex) => {
|
|
6656
|
+
const templateComp = (comp.templateComponents || [])[compIndex];
|
|
6657
|
+
return {
|
|
6658
|
+
...comp,
|
|
6659
|
+
id: templateComp ? `${templateComp.id}-entry-${index}-${compIndex}` : comp.id
|
|
6660
|
+
};
|
|
6661
|
+
}) || []
|
|
6662
|
+
}));
|
|
6663
|
+
return {
|
|
6664
|
+
...comp,
|
|
6665
|
+
entries: updatedEntries
|
|
6666
|
+
};
|
|
6667
|
+
}
|
|
6668
|
+
return comp;
|
|
6669
|
+
});
|
|
6670
|
+
onFormDataChange?.(updatedComponents);
|
|
6671
|
+
}, renderFormComponent: (field) => {
|
|
6672
|
+
// Ensure the nested component gets the proper form value
|
|
6514
6673
|
return renderFormComponent(field);
|
|
6515
6674
|
} }));
|
|
6516
6675
|
case 'file':
|