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