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.js CHANGED
@@ -4334,57 +4334,20 @@ 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
- // 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 }) => {
4337
+ const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, selectedComponent, renderFormComponent, gridId, formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, columnView = false, shouldShowComponent }) => {
4339
4338
  const { setNodeRef, isOver } = core.useDroppable({
4340
4339
  id: `grid-drop-zone-${gridId}`,
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
- }
4340
+ disabled: mode !== 'edit'
4361
4341
  });
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',
4342
+ return (jsxRuntime.jsx("div", { ref: setNodeRef, className: "grid-drop-zone", style: {
4343
+ border: isOver ? '2px dashed #3b82f6' : '2px dashed #d1d5db',
4364
4344
  borderRadius: '8px',
4365
4345
  padding: '16px',
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: {
4346
+ backgroundColor: isOver ? 'var(--df-color-primary-light)' : 'var(--df-color-fb-container)',
4347
+ minHeight: '120px',
4348
+ transition: 'all 0.2s ease',
4349
+ position: 'relative'
4350
+ }, children: gridComponents.length > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(sortable.SortableContext, { items: gridComponents.map(c => c.id), strategy: sortable.horizontalListSortingStrategy, children: jsxRuntime.jsx("div", { style: {
4388
4351
  display: 'flex',
4389
4352
  flexDirection: columnView ? 'column' : 'row',
4390
4353
  flexWrap: 'nowrap',
@@ -4409,14 +4372,28 @@ const GridDropZone = ({ gridComponents, mode, onComponentSelect, onComponentDele
4409
4372
  minHeight: '40px',
4410
4373
  display: 'flex',
4411
4374
  alignItems: 'center',
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" })) })] })) }));
4375
+ justifyContent: 'center'
4376
+ }, 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" })) })] })) : (jsxRuntime.jsxs("div", { style: {
4377
+ textAlign: 'center',
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" })] })) }));
4415
4395
  };
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 || {};
4396
+ 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 }) => {
4420
4397
  const visibleTemplateComponents = React.useMemo(() => {
4421
4398
  if (!shouldShowComponent)
4422
4399
  return templateComponents;
@@ -4478,18 +4455,16 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4478
4455
  whiteSpace: 'nowrap',
4479
4456
  overflow: 'hidden',
4480
4457
  textOverflow: 'ellipsis'
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,
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
+ display: 'grid',
4460
+ // Change grid columns to 1fr for column view
4461
+ gridTemplateColumns: columnView
4462
+ ? '1fr'
4463
+ : `repeat(${visibleTemplateComponents.length}, minmax(150px, 1fr))`,
4488
4464
  borderBottom: entryIndex < dataEntries.length - 1 ? '1px solid var(--df-color-fb-border)' : 'none',
4489
4465
  backgroundColor: entryIndex % 2 === 0 ? 'var(--df-color-fb-container)' : 'var(--df-color-fb-bg)',
4490
4466
  position: 'relative',
4491
- minWidth: columnView ? '100%' : `${visibleTemplateComponents.length * 150}px`,
4492
- padding: columnView ? '16px' : '0'
4467
+ minWidth: columnView ? '100%' : `${visibleTemplateComponents.length * 150}px`
4493
4468
  }, children: [visibleTemplateComponents.map((templateComponent, componentIndex) => {
4494
4469
  let entryComponent = entry.components?.[componentIndex];
4495
4470
  if (!entryComponent) {
@@ -4515,24 +4490,21 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4515
4490
  };
4516
4491
  }
4517
4492
  return (jsxRuntime.jsx("div", { style: {
4518
- padding: columnView ? '12px 0' : '12px 16px',
4493
+ padding: '12px 16px',
4519
4494
  borderRight: !columnView && componentIndex < visibleTemplateComponents.length - 1 ? '1px solid var(--df-color-fb-border)' : 'none',
4520
4495
  // Add bottom border for fields in column view except the last one
4521
4496
  borderBottom: columnView && componentIndex < visibleTemplateComponents.length - 1 ? '1px dashed var(--df-color-fb-border)' : 'none',
4522
- minHeight: columnView ? 'auto' : '60px',
4497
+ minHeight: '60px',
4523
4498
  minWidth: columnView ? '100%' : '150px',
4524
- width: columnView ? '100%' : 'auto',
4525
4499
  display: 'flex',
4526
- flexDirection: columnView ? 'column' : 'row',
4527
- alignItems: columnView ? 'stretch' : 'center',
4528
- overflow: 'hidden',
4529
- gap: columnView ? '8px' : '0'
4500
+ alignItems: 'center',
4501
+ overflow: 'hidden'
4530
4502
  }, children: jsxRuntime.jsx("div", { style: {
4531
4503
  width: '100%',
4532
- minWidth: columnView ? '100%' : '120px',
4504
+ minWidth: '120px',
4533
4505
  overflow: 'hidden'
4534
4506
  }, children: renderFormComponent(entryComponent, !columnView) }) }, `${entry.id}-${componentIndex}`));
4535
- }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > minEntries && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry(entryIndex), disabled: dataEntries.length <= minEntries, style: {
4507
+ }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > minEntries && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry?.(entryIndex), disabled: dataEntries.length <= minEntries, style: {
4536
4508
  position: 'absolute',
4537
4509
  top: '8px',
4538
4510
  right: '8px',
@@ -4566,7 +4538,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4566
4538
  position: 'sticky',
4567
4539
  bottom: 0,
4568
4540
  zIndex: 5
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: {
4541
+ }, children: jsxRuntime.jsxs("button", { onClick: onAddEntry, disabled: dataEntries.length >= maxEntries, style: {
4570
4542
  padding: '8px 16px',
4571
4543
  backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
4572
4544
  color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
@@ -4604,7 +4576,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4604
4576
  fontWeight: '600',
4605
4577
  color: 'var(--df-color-text-dark)',
4606
4578
  fontSize: '14px'
4607
- }, children: ["Entry #", entryIndex + 1] }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > 1 && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry(entryIndex), disabled: dataEntries.length <= minEntries, style: {
4579
+ }, children: ["Entry #", entryIndex + 1] }), mode === 'test' && allowAddRemoveEntries && dataEntries.length > 1 && (jsxRuntime.jsx("button", { onClick: () => onRemoveEntry?.(entryIndex), disabled: dataEntries.length <= minEntries, style: {
4608
4580
  padding: '4px 8px',
4609
4581
  backgroundColor: dataEntries.length <= minEntries ? '#f3f4f6' : '#ef4444',
4610
4582
  color: dataEntries.length <= minEntries ? '#9ca3af' : '#ffffff',
@@ -4620,39 +4592,40 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4620
4592
  height: '24px',
4621
4593
  justifyContent: 'center'
4622
4594
  }, title: removeText, children: jsxRuntime.jsx("span", { style: { fontSize: '14px' }, children: "\u00D7" }) }))] }), jsxRuntime.jsx("div", { style: {
4623
- display: columnView ? 'flex' : 'grid',
4624
- flexDirection: columnView ? 'column' : 'row',
4625
- gridTemplateColumns: !columnView ? 'repeat(auto-fit, minmax(200px, 1fr))' : undefined,
4595
+ display: 'grid',
4596
+ gridTemplateColumns: columnView ? '1fr' : 'repeat(auto-fit, minmax(200px, 1fr))',
4626
4597
  gap: '16px'
4627
4598
  }, children: visibleTemplateComponents.map((templateComponent, componentIndex) => {
4599
+ // Find the corresponding component in this entry by index first, then by name+label
4628
4600
  let entryComponent = entry.components?.[componentIndex];
4601
+ // If no component at this index, try to find by name+label
4629
4602
  if (!entryComponent) {
4630
4603
  entryComponent = entry.components?.find((comp) => comp.name === templateComponent.name &&
4631
4604
  comp.basic?.label === templateComponent.basic?.label);
4632
4605
  }
4633
4606
  if (!entryComponent) {
4607
+ // Use entry.id (which is unique) instead of entryIndex
4634
4608
  const uniqueId = `${templateComponent.id}-${entry.id}-${componentIndex}`;
4635
4609
  entryComponent = {
4636
4610
  ...templateComponent,
4637
4611
  id: uniqueId,
4638
4612
  basic: {
4639
4613
  ...templateComponent.basic,
4640
- showLabel: columnView // Show label in column view, hide in grid view
4614
+ showLabel: false // Hide label in datagrid cells
4641
4615
  }
4642
4616
  };
4643
4617
  }
4644
4618
  else {
4619
+ // Preserve the original ID to maintain form value connections
4645
4620
  entryComponent = {
4646
4621
  ...entryComponent,
4647
- id: entryComponent.id,
4622
+ id: entryComponent.id, // Keep the original ID
4648
4623
  basic: {
4649
4624
  ...entryComponent.basic,
4650
- showLabel: columnView // Show label in column view, hide in grid view
4625
+ showLabel: false // Hide label in datagrid cells
4651
4626
  }
4652
4627
  };
4653
4628
  }
4654
- if (!entryComponent)
4655
- return null;
4656
4629
  return (jsxRuntime.jsxs("div", { style: {
4657
4630
  display: 'flex',
4658
4631
  flexDirection: 'column',
@@ -4688,7 +4661,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4688
4661
  borderRadius: '8px',
4689
4662
  display: 'flex',
4690
4663
  justifyContent: 'center'
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: {
4664
+ }, children: jsxRuntime.jsxs("button", { onClick: onAddEntry, disabled: dataEntries.length >= maxEntries, style: {
4692
4665
  padding: '8px 16px',
4693
4666
  backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
4694
4667
  color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
@@ -4701,19 +4674,11 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4701
4674
  alignItems: 'center',
4702
4675
  gap: '8px',
4703
4676
  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';
4710
4677
  }, children: [jsxRuntime.jsx("span", { children: "+" }), addAnotherText] }) }))] }));
4711
4678
  };
4712
4679
  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 }) => {
4713
4680
  const [isCollapsed, setIsCollapsed] = React.useState(false);
4714
4681
  const hasInitialized = React.useRef(false);
4715
- // Track local form values for entry components to prevent value loss during parent round-trip
4716
- const localFormValuesRef = React.useRef({});
4717
4682
  // Get all components in the grid and sanitize them to ensure no data leaks into templates
4718
4683
  let gridComponents = (properties.templateComponents || []).map(comp => ({
4719
4684
  ...comp,
@@ -4791,6 +4756,87 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4791
4756
  }
4792
4757
  }
4793
4758
  }
4759
+ else {
4760
+ // Update existing entries to include all template components and sync their properties
4761
+ const needsUpdate = dataEntries.some(entry => {
4762
+ // Check if entry is missing any template components or if existing components need updates
4763
+ return gridComponents.some((templateComp, componentIndex) => {
4764
+ const existingComponent = entry.components?.[componentIndex];
4765
+ if (!existingComponent) {
4766
+ return true; // Missing component at this index
4767
+ }
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
+ // Check if existing component needs to be updated with new template properties
4772
+ // Compare key properties that should be synced
4773
+ const needsPropertyUpdate = JSON.stringify(existingComponent.basic?.options) !== JSON.stringify(templateComp.basic?.options) ||
4774
+ existingComponent.basic?.placeholder !== templateComp.basic?.placeholder ||
4775
+ existingComponent.basic?.defaultValue !== templateComp.basic?.defaultValue ||
4776
+ existingComponent.basic?.label !== templateComp.basic?.label ||
4777
+ existingComponent.validation?.required !== templateComp.validation?.required;
4778
+ // We do not check for ID mismatch anymore as we want to preserve unique IDs
4779
+ return needsPropertyUpdate;
4780
+ });
4781
+ });
4782
+ if (needsUpdate && onValueChange) {
4783
+ const updatedEntries = dataEntries.map(entry => {
4784
+ // Use index-based matching to ensure each template component maps to the correct entry component
4785
+ const updatedComponents = gridComponents.map((templateComp, componentIndex) => {
4786
+ // Find existing component by index first
4787
+ let existingComponent = entry.components?.[componentIndex];
4788
+ // If no component at this index, try to find by name+label (for backward compatibility)
4789
+ if (!existingComponent) {
4790
+ existingComponent = entry.components?.find((comp) => comp.name === templateComp.name &&
4791
+ comp.basic?.label === templateComp.basic?.label);
4792
+ }
4793
+ // Always ensure a valid ID exists, but respect existing one if possible
4794
+ // const uniqueId = `${templateComp.id}-entry-${entry.index}-${componentIndex}`
4795
+ if (existingComponent) {
4796
+ // Update existing component with new template properties while ensuring unique ID and preserving form values
4797
+ const updatedComponent = {
4798
+ ...templateComp,
4799
+ id: existingComponent.id, // Preserve existing ID !!
4800
+ basic: {
4801
+ ...templateComp.basic,
4802
+ showLabel: false, // Hide label in datagrid cells
4803
+ // Preserve any user-entered values
4804
+ value: existingComponent.basic?.value || templateComp.basic?.defaultValue || ''
4805
+ }
4806
+ };
4807
+ return updatedComponent;
4808
+ }
4809
+ else {
4810
+ // Create new component based on template
4811
+ // Only for NEW components in existing entries (e.g. column added) do we generate a new ID
4812
+ const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
4813
+ const newId = `${templateComp.id}-${uniqueSuffix}-${componentIndex}`;
4814
+ const newComponent = {
4815
+ ...templateComp,
4816
+ id: newId,
4817
+ basic: {
4818
+ ...templateComp.basic,
4819
+ showLabel: false // Hide label in datagrid cells
4820
+ }
4821
+ };
4822
+ return newComponent;
4823
+ }
4824
+ });
4825
+ return {
4826
+ ...entry,
4827
+ components: updatedComponents
4828
+ };
4829
+ });
4830
+ const newValue = { ...properties, entries: updatedEntries };
4831
+ // Only call onValueChange if the data actually changed
4832
+ if (JSON.stringify(newValue) !== JSON.stringify(properties)) {
4833
+ onValueChange({
4834
+ id,
4835
+ value: newValue
4836
+ });
4837
+ }
4838
+ }
4839
+ }
4794
4840
  }
4795
4841
  }, [gridComponents, dataEntries, id, onValueChange, properties, mode, properties.templateComponents]);
4796
4842
  const handleDataGridClick = React.useCallback((event) => {
@@ -4847,9 +4893,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4847
4893
  // Handle component value change for form data updates (test mode)
4848
4894
  const handleComponentValueChange = React.useCallback((change) => {
4849
4895
  if (mode === 'test') {
4850
- // CRITICAL: Store value locally to prevent loss during parent round-trip
4851
- localFormValuesRef.current[change.id] = change.value;
4852
- // Also propagate up to parent for persistence
4896
+ // In test mode, update form data through the parent's onValueChange
4897
+ // This allows the form data to be updated for interactive components
4853
4898
  if (onValueChange) {
4854
4899
  onValueChange({
4855
4900
  id: change.id,
@@ -4860,10 +4905,6 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4860
4905
  // In edit/preview modes, don't handle value changes as components are read-only
4861
4906
  }, [mode, onValueChange]);
4862
4907
  const handleAddEntry = React.useCallback(() => {
4863
- console.log('[DfFormDataGrid] handleAddEntry called');
4864
- console.log('[DfFormDataGrid] gridComponents:', gridComponents.length, gridComponents.map(c => c.id));
4865
- console.log('[DfFormDataGrid] current entries:', properties.entries?.length);
4866
- console.log('[DfFormDataGrid] onValueChange exists:', !!onValueChange);
4867
4908
  // Use timestamp and random string to ensure uniqueness even if entries are deleted
4868
4909
  const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
4869
4910
  const newEntryId = `entry-${uniqueSuffix}`;
@@ -4885,19 +4926,13 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4885
4926
  }),
4886
4927
  styles: {}
4887
4928
  };
4888
- console.log('[DfFormDataGrid] newEntry created:', newEntry.id, 'with', newEntry.components.length, 'components');
4889
4929
  const updatedEntries = [...properties.entries, newEntry];
4890
- console.log('[DfFormDataGrid] updatedEntries count:', updatedEntries.length);
4891
4930
  if (onValueChange) {
4892
- console.log('[DfFormDataGrid] calling onValueChange with updated entries');
4893
4931
  onValueChange({
4894
4932
  id,
4895
4933
  value: { ...properties, entries: updatedEntries }
4896
4934
  });
4897
4935
  }
4898
- else {
4899
- console.log('[DfFormDataGrid] WARNING: onValueChange is not defined!');
4900
- }
4901
4936
  onEntryAdd?.();
4902
4937
  }, [properties, onValueChange, id, onEntryAdd, gridComponents]);
4903
4938
  const handleRemoveEntry = React.useCallback((entryIndex) => {
@@ -4913,11 +4948,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4913
4948
  }, [properties, onValueChange, id, onEntryRemove]);
4914
4949
  // Use our own render function to ensure proper onComponentUpdate handling
4915
4950
  const renderComponent = React.useCallback((field, hideLabel = false) => {
4916
- // CRITICAL: Use local values first, then fall back to formData prop, then defaults
4917
- // This ensures typed values in entry 2+ persist immediately
4918
- const formValue = mode === 'test'
4919
- ? (localFormValuesRef.current[field.id] ?? formData[field.id] ?? field.basic?.value ?? ('defaultValue' in field.basic ? field.basic.defaultValue || '' : ''))
4920
- : ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '');
4951
+ const formValue = mode === 'test' ? (formData[field.id] || ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '')) : ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '');
4921
4952
  const commonProps = {
4922
4953
  id: field.id,
4923
4954
  properties: field,
@@ -4998,7 +5029,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4998
5029
  , {
4999
5030
  // Cast to FormComponentType[] to satisfy TableView typing; gridComponents
5000
5031
  // are always child form components of the datagrid.
5001
- 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 })) }))] }));
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: 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 })) }))] }));
5002
5033
  };
5003
5034
 
5004
5035
  const DraggableChild = ({ child, selectedChild, mode, onChildSelect, onChildDelete, renderFormComponent, isOverlay = false, isChildrenEditMode = false, formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, workOrderNumber, assetNumber, user, onCreateIssue, onUpdateIssue }) => {
@@ -6421,9 +6452,12 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6421
6452
  onFormDataChange?.(updatedComponents);
6422
6453
  } }) }));
6423
6454
  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
6424
6459
  return (jsxRuntime.jsx(DfFormDataGrid, { ...commonProps, properties: component, formData: formValues, formTemplateId: formTemplateId, mode: commonProps.mode, onThresholdActionCompletion: handleThresholdActionCompletion, onThresholdIssueRaised: handleThresholdIssueRaised, onNotesChange: (componentId, notes) => {
6425
6460
  handleComponentNotesChange(componentId, notes);
6426
- // Handle notes change for datagrid entry components
6427
6461
  const updatedComponents = localFormComponents.map(comp => {
6428
6462
  if (comp.id === component.id && comp.entries) {
6429
6463
  const updatedEntries = comp.entries.map((entry) => {
@@ -6451,7 +6485,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6451
6485
  onFormDataChange?.(updatedComponents);
6452
6486
  }, onAttachmentChange: (componentId, attachments) => {
6453
6487
  handleComponentAttachmentChange(componentId, attachments);
6454
- // Handle attachment change for datagrid entry components
6455
6488
  const updatedComponents = localFormComponents.map(comp => {
6456
6489
  if (comp.id === component.id && comp.entries) {
6457
6490
  const updatedEntries = comp.entries.map((entry) => {
@@ -6477,116 +6510,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6477
6510
  return comp;
6478
6511
  });
6479
6512
  onFormDataChange?.(updatedComponents);
6480
- }, onValueChange: (change) => {
6481
- console.log('[DfFormPreview] datagrid onValueChange received:', change.id, 'hasEntries:', change.value && typeof change.value === 'object' && 'entries' in change.value);
6482
- // Handle datagrid value changes (entries updates)
6483
- if (change.id === component.id && change.value && typeof change.value === 'object' && 'entries' in change.value) {
6484
- console.log('[DfFormPreview] datagrid entries update - entries count:', change.value.entries?.length);
6485
- // Update localFormComponents with new entries structure
6486
- const updatedComponents = localFormComponents.map(comp => {
6487
- if (comp.id === component.id) {
6488
- return {
6489
- ...comp,
6490
- ...change.value
6491
- };
6492
- }
6493
- return comp;
6494
- });
6495
- // CRITICAL: Update local state immediately so new entries render without Angular round-trip
6496
- setLocalFormComponents(updatedComponents);
6497
- onFormDataChange?.(updatedComponents);
6498
- // Also update formValues for nested components
6499
- if (change.value.entries && Array.isArray(change.value.entries)) {
6500
- change.value.entries.forEach((entry) => {
6501
- if (entry.components && Array.isArray(entry.components)) {
6502
- entry.components.forEach((nestedComp) => {
6503
- const nestedValue = formValues[nestedComp.id];
6504
- if (nestedValue !== undefined) ;
6505
- else {
6506
- // Initialize with defaultValue if available
6507
- const defaultValue = nestedComp.basic?.defaultValue;
6508
- if (defaultValue !== undefined) {
6509
- setFormValues(prev => ({
6510
- ...prev,
6511
- [nestedComp.id]: defaultValue
6512
- }));
6513
- }
6514
- }
6515
- });
6516
- }
6517
- });
6518
- }
6519
- }
6520
- else {
6521
- // For nested component value changes, use the regular handler
6522
- onFormValueChange(change);
6523
- }
6524
- }, onEntryAdd: () => {
6525
- // CRITICAL: Entry has already been added via onValueChange in DfFormDataGrid
6526
- // Get the updated component from localFormComponents (which should have been updated by onValueChange)
6527
- const currentComponent = localFormComponents.find(comp => comp.id === component.id);
6528
- if (currentComponent && currentComponent.entries) {
6529
- // Entry should already be in the component via onValueChange
6530
- // Just ensure localFormComponents is in sync (no-op if already synced)
6531
- const updatedComponents = localFormComponents.map(comp => {
6532
- if (comp.id === component.id) {
6533
- // Ensure entries are properly structured
6534
- return {
6535
- ...comp,
6536
- entries: comp.entries || []
6537
- };
6538
- }
6539
- return comp;
6540
- });
6541
- onFormDataChange?.(updatedComponents);
6542
- }
6543
- else {
6544
- // Fallback: If component doesn't have entries yet, try to get from formValues
6545
- setTimeout(() => {
6546
- const datagridValue = formValues[component.id];
6547
- if (datagridValue && typeof datagridValue === 'object' && 'entries' in datagridValue) {
6548
- const updatedComponents = localFormComponents.map(comp => {
6549
- if (comp.id === component.id) {
6550
- return {
6551
- ...comp,
6552
- entries: datagridValue.entries
6553
- };
6554
- }
6555
- return comp;
6556
- });
6557
- onFormDataChange?.(updatedComponents);
6558
- }
6559
- }, 100);
6560
- }
6561
- }, onEntryRemove: (entryIndex) => {
6562
- // Handle entry remove - update form components
6563
- const updatedComponents = localFormComponents.map(comp => {
6564
- if (comp.id === component.id && comp.entries) {
6565
- const currentEntries = comp.entries || [];
6566
- const updatedEntries = currentEntries
6567
- .filter((_, index) => index !== entryIndex)
6568
- .map((entry, index) => ({
6569
- ...entry,
6570
- index,
6571
- id: `entry-${comp.id}-${index}`,
6572
- components: entry.components?.map((comp, compIndex) => {
6573
- const templateComp = (comp.templateComponents || [])[compIndex];
6574
- return {
6575
- ...comp,
6576
- id: templateComp ? `${templateComp.id}-entry-${index}-${compIndex}` : comp.id
6577
- };
6578
- }) || []
6579
- }));
6580
- return {
6581
- ...comp,
6582
- entries: updatedEntries
6583
- };
6584
- }
6585
- return comp;
6586
- });
6587
- onFormDataChange?.(updatedComponents);
6588
- }, renderFormComponent: (field) => {
6589
- // Ensure the nested component gets the proper form value
6513
+ }, onValueChange: onFormValueChange, renderFormComponent: (field) => {
6590
6514
  return renderFormComponent(field);
6591
6515
  } }));
6592
6516
  case 'file':