df-ae-forms-package 1.1.1 → 1.1.3

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.esm.js CHANGED
@@ -4241,6 +4241,33 @@ const ComponentSubmissionActions = ({ component }) => {
4241
4241
  return (jsx("div", { className: "component-submission-actions", children: jsxs("div", { className: "actions-content", children: [hasNotes && (jsx("div", { className: "notes-summary-section", children: jsxs("div", { className: "notes-full-text", children: [jsx("span", { className: "notes-label", children: "Notes:" }), " ", notes] }) })), hasAttachments && (jsx("div", { className: "attachments-section", children: jsx(SubmissionAttachmentThumbnails, { attachments: attachments }) }))] }) }));
4242
4242
  };
4243
4243
 
4244
+ // Helper to ensure an ID is a stable string, handling MongoDB-style object IDs
4245
+ // Helper to ensure an ID is a stable string, handling MongoDB-style object IDs
4246
+ function ensureStringId$2(id) {
4247
+ if (!id)
4248
+ return '';
4249
+ if (typeof id === 'string') {
4250
+ if (id.includes('[object Object]'))
4251
+ return '';
4252
+ return id;
4253
+ }
4254
+ if (typeof id === 'object' && id !== null) {
4255
+ if (id.$oid)
4256
+ return String(id.$oid);
4257
+ if (id._id)
4258
+ return ensureStringId$2(id._id);
4259
+ try {
4260
+ const str = id.toString();
4261
+ if (str && str !== '[object Object]')
4262
+ return str;
4263
+ return JSON.stringify(id);
4264
+ }
4265
+ catch (e) {
4266
+ return 'id-error';
4267
+ }
4268
+ }
4269
+ return String(id);
4270
+ }
4244
4271
  const DraggableGridComponent = ({ component, selectedComponent, mode, onComponentSelect, onComponentDelete, onComponentEdit, renderFormComponent, isOverlay = false, formData = {}, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, shouldShowComponent }) => {
4245
4272
  const formValue = formData[component.id];
4246
4273
  const isVisible = shouldShowComponent ? shouldShowComponent(component.id) : true;
@@ -4492,7 +4519,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4492
4519
  let entryComponent = entry.components?.[componentIndex];
4493
4520
  if (!entryComponent) {
4494
4521
  // Use entry.id (which is unique) instead of entryIndex to prevent data collisions
4495
- const uniqueId = `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`;
4522
+ const uniqueId = `${ensureStringId$2(templateComponent.id) || 'comp'}-${ensureStringId$2(entry.id)}-${componentIndex}`;
4496
4523
  entryComponent = {
4497
4524
  ...templateComponent,
4498
4525
  id: uniqueId,
@@ -4505,7 +4532,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4505
4532
  else {
4506
4533
  entryComponent = {
4507
4534
  ...entryComponent,
4508
- id: entryComponent.id || `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`,
4535
+ id: ensureStringId$2(entryComponent.id) || `${ensureStringId$2(templateComponent.id) || 'comp'}-${ensureStringId$2(entry.id)}-${componentIndex}`,
4509
4536
  basic: {
4510
4537
  ...entryComponent.basic,
4511
4538
  showLabel: columnView // Show label in column view
@@ -4564,7 +4591,11 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4564
4591
  position: 'sticky',
4565
4592
  bottom: 0,
4566
4593
  zIndex: 5
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: {
4594
+ }, children: jsxs("button", { onClick: (e) => {
4595
+ e.stopPropagation();
4596
+ console.log('[TableView] Add Entry button clicked (grid view)', 'entries:', dataEntries.length, 'max:', maxEntries);
4597
+ onAddEntry();
4598
+ }, disabled: dataEntries.length >= maxEntries, style: {
4568
4599
  padding: '8px 16px',
4569
4600
  backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
4570
4601
  color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
@@ -4629,7 +4660,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4629
4660
  comp.basic?.label === templateComponent.basic?.label);
4630
4661
  }
4631
4662
  if (!entryComponent) {
4632
- const uniqueId = `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`;
4663
+ const uniqueId = `${ensureStringId$2(templateComponent.id) || 'comp'}-${ensureStringId$2(entry.id)}-${componentIndex}`;
4633
4664
  entryComponent = {
4634
4665
  ...templateComponent,
4635
4666
  id: uniqueId,
@@ -4642,7 +4673,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4642
4673
  else {
4643
4674
  entryComponent = {
4644
4675
  ...entryComponent,
4645
- id: entryComponent.id || `${templateComponent.id || 'comp'}-${entry.id}-${componentIndex}`,
4676
+ id: ensureStringId$2(entryComponent.id) || `${ensureStringId$2(templateComponent.id) || 'comp'}-${ensureStringId$2(entry.id)}-${componentIndex}`,
4646
4677
  basic: {
4647
4678
  ...entryComponent.basic,
4648
4679
  showLabel: columnView // Show label in column view, hide in grid view
@@ -4686,7 +4717,11 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4686
4717
  borderRadius: '8px',
4687
4718
  display: 'flex',
4688
4719
  justifyContent: 'center'
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: {
4720
+ }, children: jsxs("button", { onClick: (e) => {
4721
+ e.stopPropagation();
4722
+ console.log('[TableView] Add Entry button clicked (list view)', 'entries:', dataEntries.length, 'max:', maxEntries);
4723
+ onAddEntry();
4724
+ }, disabled: dataEntries.length >= maxEntries, style: {
4690
4725
  padding: '8px 16px',
4691
4726
  backgroundColor: dataEntries.length >= maxEntries ? '#f3f4f6' : '#10b981',
4692
4727
  color: dataEntries.length >= maxEntries ? '#9ca3af' : '#ffffff',
@@ -4710,10 +4745,11 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4710
4745
  const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueChange, onSelect, isSelected = false, className = '', onDataGridSelect, onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, selectedComponent, renderFormComponent, onEntryAdd, onEntryRemove, formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChange, onAttachmentChange, shouldShowComponent }) => {
4711
4746
  const [isCollapsed, setIsCollapsed] = useState(false);
4712
4747
  const hasInitialized = useRef(false);
4748
+ const gridId = ensureStringId$2(id) || properties._id || (typeof id === 'string' ? id : 'datagrid-id');
4713
4749
  // Get all components in the grid and sanitize them to ensure no data leaks into templates
4714
4750
  let gridComponents = (properties.templateComponents || []).map((comp, index) => ({
4715
4751
  ...comp,
4716
- id: comp.id || `${id}-template-${index}`, // CRITICAL: Ensure template components have stable IDs
4752
+ id: ensureStringId$2(comp.id) || `${gridId}-template-${index}`, // CRITICAL: Ensure template components have stable string IDs
4717
4753
  basic: {
4718
4754
  ...comp.basic,
4719
4755
  // Use an empty string instead of undefined to satisfy component typing
@@ -4727,7 +4763,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4727
4763
  gridComponents = dataEntries[0].components.map((comp, index) => ({
4728
4764
  ...comp,
4729
4765
  // Remove entry suffix for template, use stable ID if missing
4730
- id: comp.id?.replace(/-entry-.*$/, '') || `${id}-template-${index}`,
4766
+ id: ensureStringId$2(comp.id).replace(/-entry-.*$/, '') || `${gridId}-template-${index}`,
4731
4767
  basic: {
4732
4768
  ...comp.basic,
4733
4769
  // Clear any data values while keeping types happy
@@ -4770,7 +4806,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4770
4806
  index: 0,
4771
4807
  components: gridComponents.map((comp, componentIndex) => ({
4772
4808
  ...comp,
4773
- id: `${comp.id}-${newEntryId}-${componentIndex}`,
4809
+ id: `${ensureStringId$2(comp.id)}-${newEntryId}-${componentIndex}`,
4774
4810
  basic: {
4775
4811
  ...comp.basic,
4776
4812
  showLabel: false // Hide label in datagrid cells
@@ -4825,8 +4861,10 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4825
4861
  // Update existing component with new template properties while ensuring unique ID and preserving form values
4826
4862
  const updatedComponent = {
4827
4863
  ...templateComp,
4828
- // Preserve existing ID or generate one if missing
4829
- id: existingComponent.id || `${templateComp.id}-${entry.id}-${componentIndex}`,
4864
+ // Preserve existing ID (ensure it's a string) or generate one if missing
4865
+ id: (existingComponent.id && typeof existingComponent.id === 'string')
4866
+ ? existingComponent.id
4867
+ : `${ensureStringId$2(templateComp.id)}-${ensureStringId$2(entry.id)}-${componentIndex}`,
4830
4868
  basic: {
4831
4869
  ...templateComp.basic,
4832
4870
  showLabel: false, // Hide label in datagrid cells
@@ -4840,7 +4878,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4840
4878
  // Create new component based on template
4841
4879
  // Only for NEW components in existing entries (e.g. column added) do we generate a new ID
4842
4880
  const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
4843
- const newId = `${templateComp.id}-${uniqueSuffix}-${componentIndex}`;
4881
+ const newId = `${ensureStringId$2(templateComp.id)}-${uniqueSuffix}-${componentIndex}`;
4844
4882
  const newComponent = {
4845
4883
  ...templateComp,
4846
4884
  id: newId,
@@ -4935,25 +4973,27 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4935
4973
  // In edit/preview modes, don't handle value changes as components are read-only
4936
4974
  }, [mode, onValueChange]);
4937
4975
  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);
4976
+ console.log('[DfFormDataGrid] handleAddEntry called - Component ID:', id);
4977
+ // Safety check: ensure we have entries array
4978
+ const currentEntries = Array.isArray(properties.entries) ? properties.entries : [];
4979
+ console.log('[DfFormDataGrid] gridComponents:', gridComponents.length);
4980
+ console.log('[DfFormDataGrid] current entries count:', currentEntries.length);
4941
4981
  console.log('[DfFormDataGrid] onValueChange exists:', !!onValueChange);
4942
4982
  // Use timestamp and random string to ensure uniqueness even if entries are deleted
4943
4983
  const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
4944
4984
  const newEntryId = `entry-${uniqueSuffix}`;
4945
4985
  const newEntry = {
4946
4986
  id: newEntryId,
4947
- index: properties.entries.length,
4987
+ index: currentEntries.length,
4948
4988
  components: gridComponents.map((comp, componentIndex) => {
4949
- const componentId = `${comp.id}-${newEntryId}-${componentIndex}`;
4989
+ const componentId = `${ensureStringId$2(comp.id)}-${newEntryId}-${componentIndex}`;
4950
4990
  return {
4951
4991
  ...comp,
4952
4992
  // Use the unique entry ID in the component ID to prevent collisions
4953
4993
  id: componentId,
4954
4994
  basic: {
4955
4995
  ...comp.basic,
4956
- value: '', // Explicitly clear value for new entries
4996
+ value: comp.basic?.defaultValue || '', // Use defaultValue if available, else empty
4957
4997
  showLabel: false // Hide label in datagrid cells
4958
4998
  }
4959
4999
  };
@@ -4961,31 +5001,41 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4961
5001
  styles: {}
4962
5002
  };
4963
5003
  console.log('[DfFormDataGrid] newEntry created:', newEntry.id, 'with', newEntry.components.length, 'components');
4964
- const updatedEntries = [...properties.entries, newEntry];
5004
+ const updatedEntries = [...currentEntries, newEntry];
4965
5005
  console.log('[DfFormDataGrid] updatedEntries count:', updatedEntries.length);
4966
5006
  if (onValueChange) {
4967
- console.log('[DfFormDataGrid] calling onValueChange with updated entries');
5007
+ console.log('[DfFormDataGrid] calling onValueChange with updated datagrid structure - Entries:', updatedEntries.length);
4968
5008
  onValueChange({
4969
- id,
4970
- value: { ...properties, entries: updatedEntries }
5009
+ id: gridId, // Use sanitized gridId
5010
+ value: {
5011
+ ...properties,
5012
+ entries: updatedEntries
5013
+ }
4971
5014
  });
5015
+ // Notify parent if callback provided
5016
+ if (onEntryAdd) {
5017
+ console.log('[DfFormDataGrid] calling onEntryAdd callback');
5018
+ onEntryAdd();
5019
+ }
4972
5020
  }
4973
5021
  else {
4974
- console.log('[DfFormDataGrid] WARNING: onValueChange is not defined!');
5022
+ console.warn('[DfFormDataGrid] Cannot add entry: onValueChange is missing');
4975
5023
  }
4976
- onEntryAdd?.();
4977
- }, [properties, onValueChange, id, onEntryAdd, gridComponents]);
5024
+ }, [onValueChange, properties, gridId, gridComponents, onEntryAdd]);
4978
5025
  const handleRemoveEntry = useCallback((entryIndex) => {
4979
- const updatedEntries = properties.entries
5026
+ // Safety check: ensure we have entries array
5027
+ const currentEntries = Array.isArray(properties.entries) ? properties.entries : [];
5028
+ const updatedEntries = currentEntries
4980
5029
  .filter((_, index) => index !== entryIndex)
4981
5030
  .map((entry, index) => ({ ...entry, index })); // Only update index, preserve unique ID
4982
5031
  if (onValueChange) {
5032
+ console.log('[DfFormDataGrid] Removing entry at index:', entryIndex, 'New count:', updatedEntries.length);
4983
5033
  onValueChange({
4984
- id,
5034
+ id: gridId, // Use sanitized gridId
4985
5035
  value: { ...properties, entries: updatedEntries }
4986
5036
  });
4987
5037
  }
4988
- }, [properties, onValueChange, id, onEntryRemove]);
5038
+ }, [properties.entries, onValueChange, gridId, properties]);
4989
5039
  // Use our own render function to ensure proper onComponentUpdate handling
4990
5040
  const renderComponent = useCallback((field, hideLabel = false) => {
4991
5041
  const formValue = mode === 'test' ? (formData[field.id] || ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '')) : ('defaultValue' in field.basic ? field.basic.defaultValue || '' : '');
@@ -5310,6 +5360,37 @@ const DfFormSection = ({ id, properties, mode = 'edit', formData = {}, onValueCh
5310
5360
 
5311
5361
  // Dynamic imports to avoid circular dependencies
5312
5362
  const DfFormTable$1 = React.lazy(() => Promise.resolve().then(function () { return dfFormTable; }));
5363
+ // Helper to ensure an ID is a stable string, handling MongoDB-style object IDs
5364
+ // This is used globally in this file to prevent [object Object] corruption
5365
+ const ensureStringId$1 = (id) => {
5366
+ if (!id)
5367
+ return '';
5368
+ if (typeof id === 'string') {
5369
+ // If the ID is already corrupted, return empty to trigger regeneration
5370
+ if (id.includes('[object Object]'))
5371
+ return '';
5372
+ return id;
5373
+ }
5374
+ if (typeof id === 'object' && id !== null) {
5375
+ if (id.$oid)
5376
+ return String(id.$oid);
5377
+ if (id._id)
5378
+ return ensureStringId$1(id._id);
5379
+ // Fallback for other object structures that might be IDs
5380
+ try {
5381
+ // If it has a toString that isn't the default, use it
5382
+ const str = id.toString();
5383
+ if (str && str !== '[object Object]')
5384
+ return str;
5385
+ // Otherwise, use a hash or just JSON
5386
+ return JSON.stringify(id);
5387
+ }
5388
+ catch (e) {
5389
+ return 'id-error';
5390
+ }
5391
+ }
5392
+ return String(id);
5393
+ };
5313
5394
  // Normalize a table row from API format (object with numeric keys) into a proper array.
5314
5395
  // The API may return cells as [{"0": {cell}, "1": {cell}}, ...] instead of [[cell, cell], ...].
5315
5396
  const normalizeTableRow = (row) => {
@@ -5355,16 +5436,17 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5355
5436
  // Recursive function to initialize values from nested components
5356
5437
  const initializeComponentValues = (components, values) => {
5357
5438
  components.forEach(component => {
5358
- if (component.id) {
5439
+ const componentId = ensureStringId$1(component.id);
5440
+ if (componentId) {
5359
5441
  // ALWAYS prioritize existing form state (values param) if it exists
5360
- if (values[component.id] !== undefined) ;
5361
- // Then check initialFormData (if we are merging it) - handled by caller usually, but safe to check
5362
- else if (component.basic && 'value' in component.basic && component.basic.value !== undefined && component.basic.value !== '' && component.basic.value !== null) {
5363
- values[component.id] = component.basic.value;
5442
+ if (values[componentId] !== undefined) ;
5443
+ // Then use captured value in basic.value
5444
+ else if (component.basic && 'value' in component.basic && component.basic.value !== undefined) {
5445
+ values[componentId] = component.basic.value;
5364
5446
  }
5365
- // Then check defaultValue
5366
- else if (component.basic && 'defaultValue' in component.basic && component.basic.defaultValue !== undefined && component.basic.defaultValue !== '' && component.basic.defaultValue !== null) {
5367
- values[component.id] = component.basic.defaultValue;
5447
+ // Then use defaultValue
5448
+ else if (component.basic && 'defaultValue' in component.basic && component.basic.defaultValue !== undefined) {
5449
+ values[componentId] = component.basic.defaultValue;
5368
5450
  }
5369
5451
  // Only then default to empty
5370
5452
  else {
@@ -5434,12 +5516,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5434
5516
  // Helper to extract notes and attachments recursively
5435
5517
  const extractNotesAndAttachments = (components) => {
5436
5518
  components.forEach(comp => {
5437
- if (comp.id) {
5519
+ const componentId = ensureStringId$1(comp.id);
5520
+ if (componentId) {
5438
5521
  if (comp.basic?.notes || comp.basic?.comments) {
5439
- initialNotes[comp.id] = comp.basic?.notes || comp.basic?.comments;
5522
+ initialNotes[componentId] = comp.basic?.notes || comp.basic?.comments;
5440
5523
  }
5441
5524
  if (comp.basic?.attachments) {
5442
- initialAttachments[comp.id] = comp.basic.attachments;
5525
+ initialAttachments[componentId] = comp.basic.attachments;
5443
5526
  }
5444
5527
  }
5445
5528
  // Recurse
@@ -5461,27 +5544,43 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5461
5544
  };
5462
5545
  // Validate component IDs for uniqueness without mutating original props
5463
5546
  const getValidatedComponents = (components, parentId) => {
5547
+ const parentIdStr = parentId ? ensureStringId$1(parentId) : '';
5464
5548
  return components.map((component, index) => {
5465
5549
  let validatedComponent = { ...component };
5466
- // Ensure component has an ID
5467
- if (!validatedComponent.id || typeof validatedComponent.id !== 'string' || validatedComponent.id.trim() === '') {
5550
+ // Ensure component has a valid string ID
5551
+ let currentId = validatedComponent.id;
5552
+ // CRITICAL: Reject any ID that is not a string, is empty, or contains "[object Object]"
5553
+ let isIdValid = typeof currentId === 'string' &&
5554
+ currentId.trim() !== '' &&
5555
+ !currentId.includes('[object Object]');
5556
+ if (!isIdValid) {
5557
+ // If it's an object ID (but not a corrupted string), convert it to string
5558
+ if (currentId && typeof currentId === 'object') {
5559
+ const convertedId = ensureStringId$1(currentId);
5560
+ if (convertedId && !convertedId.includes('[object Object]')) {
5561
+ validatedComponent.id = convertedId;
5562
+ isIdValid = true;
5563
+ }
5564
+ }
5565
+ }
5566
+ if (!isIdValid) {
5468
5567
  // Determine a base name for the ID
5469
5568
  const name = validatedComponent.name || 'component';
5470
5569
  const label = validatedComponent.basic?.label || '';
5471
5570
  const safeLabel = label.replace(/[^a-zA-Z0-9]/g, '').toLowerCase().substring(0, 10);
5472
5571
  // Generate a stable unique ID if parentId is provided, otherwise fallback to semi-stable
5473
- if (parentId) {
5474
- validatedComponent.id = `${parentId}-${name}-${index}`;
5572
+ if (parentIdStr) {
5573
+ validatedComponent.id = `${parentIdStr}-${name}-${index}`;
5475
5574
  }
5476
5575
  else {
5477
5576
  // For root components, use label if available for stability
5478
5577
  const labelPart = safeLabel ? `-${safeLabel}` : '';
5479
5578
  validatedComponent.id = `${name}${labelPart}-${index}`;
5480
5579
  }
5481
- console.warn(`[DfFormPreview] Assigned missing ID: ${validatedComponent.id}`);
5580
+ console.warn(`[DfFormPreview] Fixed missing/invalid ID: ${validatedComponent.id}`);
5482
5581
  }
5483
5582
  else {
5484
- // Check for duplicates
5583
+ // ID is a valid string, check for duplicates
5485
5584
  if (seenIds.has(validatedComponent.id)) {
5486
5585
  console.error(`[DfFormPreview] Duplicate component ID detected: ${validatedComponent.id}`);
5487
5586
  // Generate a unique ID for duplicate - using index to keep it somewhat stable
@@ -5501,7 +5600,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5501
5600
  return normalizedRow.map((cell, cellIndex) => {
5502
5601
  if (cell && cell.components && Array.isArray(cell.components)) {
5503
5602
  // Stable ID for table cells: tableId-row-X-cell-Y
5504
- const cellContextId = `${validatedComponent.id}-row-${rowIndex}-cell-${cellIndex}`;
5603
+ const cellContextId = `${ensureStringId$1(validatedComponent.id)}-row-${rowIndex}-cell-${cellIndex}`;
5505
5604
  return {
5506
5605
  ...cell,
5507
5606
  components: getValidatedComponents(cell.components, cellContextId)
@@ -5517,14 +5616,21 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5517
5616
  validatedComponent.entries = validatedComponent.entries.map((entry) => {
5518
5617
  if (entry && entry.components && Array.isArray(entry.components)) {
5519
5618
  // Stable ID for datagrid entries: datagridId-entryId or using entry.id
5619
+ const entryId = entry.id ? ensureStringId$1(entry.id) : `${ensureStringId$1(validatedComponent.id)}-entry-${entry.index}`;
5520
5620
  return {
5521
5621
  ...entry,
5522
- components: getValidatedComponents(entry.components, entry.id || `${validatedComponent.id}-entry-${entry.index}`)
5622
+ id: entryId, // Ensure entry itself has a string ID
5623
+ components: getValidatedComponents(entry.components, entryId)
5523
5624
  };
5524
5625
  }
5525
5626
  return entry;
5526
5627
  });
5527
5628
  }
5629
+ // CRITICAL: Also validate templateComponents for datagrids
5630
+ if (validatedComponent.name === 'datagrid' && validatedComponent.templateComponents && Array.isArray(validatedComponent.templateComponents)) {
5631
+ const datagridId = ensureStringId$1(validatedComponent.id);
5632
+ validatedComponent.templateComponents = getValidatedComponents(validatedComponent.templateComponents, `${datagridId}-template`);
5633
+ }
5528
5634
  return validatedComponent;
5529
5635
  });
5530
5636
  };
@@ -5532,8 +5638,10 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5532
5638
  let validatedFormComponents = localFormComponents;
5533
5639
  if (localFormComponents && localFormComponents.length > 0) {
5534
5640
  validatedFormComponents = getValidatedComponents(localFormComponents);
5535
- // Notifying parent of changed components if they were mutated (IDs added/fixed)
5641
+ // Synchronize local state if components were mutated (IDs added/fixed)
5536
5642
  if (JSON.stringify(validatedFormComponents) !== JSON.stringify(localFormComponents)) {
5643
+ console.log('[DfFormPreview] Synchronizing local components after ID validation');
5644
+ setLocalFormComponents(validatedFormComponents);
5537
5645
  onFormDataChange?.(validatedFormComponents);
5538
5646
  }
5539
5647
  }
@@ -6312,13 +6420,10 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6312
6420
  const componentIdCacheRef = useRef(new Map());
6313
6421
  const componentCounterRef = useRef(0);
6314
6422
  const renderFormComponent = (component) => {
6315
- // CRITICAL: Ensure component has a valid unique ID
6423
+ // CRITICAL: Ensure component has a valid unique string ID
6316
6424
  // NEVER regenerate IDs - always use existing or cached ID
6317
- let finalComponentId;
6318
- if (component.id && typeof component.id === 'string' && component.id.trim() !== '') {
6319
- // Component already has an ID - use it (most common case)
6320
- finalComponentId = component.id;
6321
- }
6425
+ let finalComponentId = ensureStringId$1(component.id);
6426
+ if (finalComponentId && finalComponentId.trim() !== '') ;
6322
6427
  else {
6323
6428
  // Component is missing an ID - need to generate and cache it
6324
6429
  // Create a stable cache key using component properties that don't change
@@ -6448,12 +6553,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6448
6553
  return renderFormComponent(field);
6449
6554
  }, onNotesChange: (componentId, notes) => {
6450
6555
  // Handle notes change for table cell components
6556
+ const targetTableId = ensureStringId$1(component.id);
6451
6557
  const updatedComponents = localFormComponents.map(comp => {
6452
- if (comp.id === component.id && comp.cells) {
6558
+ if (ensureStringId$1(comp.id) === targetTableId && comp.cells) {
6453
6559
  const updatedCells = comp.cells.map((row) => normalizeTableRow(row).map((cell) => {
6454
6560
  if (cell.components) {
6455
6561
  const updatedCellComponents = cell.components.map((cellComp) => {
6456
- if (cellComp.id === componentId) {
6562
+ if (ensureStringId$1(cellComp.id) === componentId) {
6457
6563
  return {
6458
6564
  ...cellComp,
6459
6565
  basic: {
@@ -6475,12 +6581,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6475
6581
  onFormDataChange?.(updatedComponents);
6476
6582
  }, onAttachmentChange: (componentId, attachments) => {
6477
6583
  // Handle attachment change for table cell components
6584
+ const targetTableId = ensureStringId$1(component.id);
6478
6585
  const updatedComponents = localFormComponents.map(comp => {
6479
- if (comp.id === component.id && comp.cells) {
6586
+ if (ensureStringId$1(comp.id) === targetTableId && comp.cells) {
6480
6587
  const updatedCells = comp.cells.map((row) => normalizeTableRow(row).map((cell) => {
6481
6588
  if (cell.components) {
6482
6589
  const updatedCellComponents = cell.components.map((cellComp) => {
6483
- if (cellComp.id === componentId) {
6590
+ if (ensureStringId$1(cellComp.id) === componentId) {
6484
6591
  return {
6485
6592
  ...cellComp,
6486
6593
  basic: {
@@ -6505,12 +6612,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6505
6612
  return (jsx(DfFormDataGrid, { ...commonProps, properties: component, formData: formValues, formTemplateId: formTemplateId, mode: commonProps.mode, onThresholdActionCompletion: handleThresholdActionCompletion, onThresholdIssueRaised: handleThresholdIssueRaised, onNotesChange: (componentId, notes) => {
6506
6613
  handleComponentNotesChange(componentId, notes);
6507
6614
  // Handle notes change for datagrid entry components
6615
+ const targetGridId = ensureStringId$1(component.id);
6508
6616
  const updatedComponents = localFormComponents.map(comp => {
6509
- if (comp.id === component.id && comp.entries) {
6617
+ if (ensureStringId$1(comp.id) === targetGridId && comp.entries) {
6510
6618
  const updatedEntries = comp.entries.map((entry) => {
6511
6619
  if (entry.components) {
6512
6620
  const updatedEntryComponents = entry.components.map((entryComp) => {
6513
- if (entryComp.id === componentId) {
6621
+ if (ensureStringId$1(entryComp.id) === componentId) {
6514
6622
  return {
6515
6623
  ...entryComp,
6516
6624
  basic: {
@@ -6533,12 +6641,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6533
6641
  }, onAttachmentChange: (componentId, attachments) => {
6534
6642
  handleComponentAttachmentChange(componentId, attachments);
6535
6643
  // Handle attachment change for datagrid entry components
6644
+ const targetGridId = ensureStringId$1(component.id);
6536
6645
  const updatedComponents = localFormComponents.map(comp => {
6537
- if (comp.id === component.id && comp.entries) {
6646
+ if (ensureStringId$1(comp.id) === targetGridId && comp.entries) {
6538
6647
  const updatedEntries = comp.entries.map((entry) => {
6539
6648
  if (entry.components) {
6540
6649
  const updatedEntryComponents = entry.components.map((entryComp) => {
6541
- if (entryComp.id === componentId) {
6650
+ if (ensureStringId$1(entryComp.id) === componentId) {
6542
6651
  return {
6543
6652
  ...entryComp,
6544
6653
  basic: {
@@ -6559,13 +6668,16 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6559
6668
  });
6560
6669
  onFormDataChange?.(updatedComponents);
6561
6670
  }, onValueChange: (change) => {
6562
- console.log('[DfFormPreview] datagrid onValueChange received:', change.id, 'hasEntries:', change.value && typeof change.value === 'object' && 'entries' in change.value);
6671
+ const changeId = ensureStringId$1(change.id);
6672
+ const componentId = ensureStringId$1(component.id);
6673
+ console.log(`[DfFormPreview] datagrid onValueChange - Target: ${changeId}, Component: ${componentId}`);
6563
6674
  // Handle datagrid value changes (entries updates)
6564
- if (change.id === component.id && change.value && typeof change.value === 'object' && 'entries' in change.value) {
6675
+ if (changeId === componentId && change.value && typeof change.value === 'object' && 'entries' in change.value) {
6565
6676
  console.log('[DfFormPreview] datagrid entries update - entries count:', change.value.entries?.length);
6566
6677
  // Update localFormComponents with new entries structure
6567
6678
  const updatedComponents = localFormComponents.map(comp => {
6568
- if (comp.id === component.id) {
6679
+ const currentCompId = ensureStringId$1(comp.id);
6680
+ if (currentCompId === componentId) {
6569
6681
  return {
6570
6682
  ...comp,
6571
6683
  ...change.value
@@ -6576,26 +6688,37 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6576
6688
  // CRITICAL: Update local state immediately so new entries render without Angular round-trip
6577
6689
  setLocalFormComponents(updatedComponents);
6578
6690
  onFormDataChange?.(updatedComponents);
6579
- // Also update formValues for nested components
6691
+ // Also update formValues for nested components to prevent undefined values
6580
6692
  if (change.value.entries && Array.isArray(change.value.entries)) {
6693
+ const newValues = { ...formValues };
6694
+ let valuesChanged = false;
6581
6695
  change.value.entries.forEach((entry) => {
6582
6696
  if (entry.components && Array.isArray(entry.components)) {
6583
6697
  entry.components.forEach((nestedComp) => {
6584
- const nestedValue = formValues[nestedComp.id];
6585
- if (nestedValue !== undefined) ;
6586
- else {
6587
- // Initialize with defaultValue if available
6698
+ const nestedId = ensureStringId$1(nestedComp.id);
6699
+ if (!nestedId)
6700
+ return;
6701
+ if (newValues[nestedId] === undefined) {
6702
+ // Initialize with defaultValue if available, otherwise empty string/array
6588
6703
  const defaultValue = nestedComp.basic?.defaultValue;
6589
6704
  if (defaultValue !== undefined) {
6590
- setFormValues(prev => ({
6591
- ...prev,
6592
- [nestedComp.id]: defaultValue
6593
- }));
6705
+ newValues[nestedId] = defaultValue;
6706
+ }
6707
+ else if (nestedComp.name === 'checkbox' || nestedComp.name === 'select') {
6708
+ newValues[nestedId] = [];
6594
6709
  }
6710
+ else {
6711
+ newValues[nestedId] = '';
6712
+ }
6713
+ valuesChanged = true;
6595
6714
  }
6596
6715
  });
6597
6716
  }
6598
6717
  });
6718
+ if (valuesChanged) {
6719
+ console.log('[DfFormPreview] Initializing form values for new datagrid entries');
6720
+ setFormValues(newValues);
6721
+ }
6599
6722
  }
6600
6723
  }
6601
6724
  else {
@@ -6804,6 +6927,32 @@ const DfFormComments = ({ comment = '', onSave, placeholder = 'Enter your reason
6804
6927
  return (jsxs("div", { className: `df-form-comments ${className}`, children: [jsxs("div", { className: "df-form-comments__header", children: [jsx("h3", { className: "df-form-comments__title", children: "Comments" }), jsx("button", { className: "df-form-comments__toggle", type: "button", onClick: toggleComments, "aria-expanded": isExpanded, "aria-label": "Toggle comments section", disabled: disabled, children: isExpanded ? (jsx("span", { className: "df-form-comments__toggle-icon", children: "\u25BC" })) : (jsx("span", { className: "df-form-comments__toggle-icon", children: "\u25B6" })) })] }), jsx("div", { className: `df-form-comments__content ${isExpanded ? 'df-form-comments__content--expanded' : ''}`, children: jsx("div", { className: "df-form-comments__input-container", children: jsx("div", { className: "df-form-comments__input-line", children: jsx("input", { type: "text", id: "comment-input", className: "df-form-comments__input", value: currentComment, onChange: handleInputChange, onBlur: handleInputBlur, onFocus: handleInputFocus, placeholder: placeholder, disabled: disabled }) }) }) })] }));
6805
6928
  };
6806
6929
 
6930
+ // Helper to ensure an ID is a stable string, handling MongoDB-style object IDs
6931
+ const ensureStringId = (id) => {
6932
+ if (!id)
6933
+ return '';
6934
+ if (typeof id === 'string') {
6935
+ if (id.includes('[object Object]'))
6936
+ return '';
6937
+ return id;
6938
+ }
6939
+ if (typeof id === 'object' && id !== null) {
6940
+ if (id.$oid)
6941
+ return String(id.$oid);
6942
+ if (id._id)
6943
+ return ensureStringId(id._id);
6944
+ try {
6945
+ const str = id.toString();
6946
+ if (str && str !== '[object Object]')
6947
+ return str;
6948
+ return JSON.stringify(id);
6949
+ }
6950
+ catch (e) {
6951
+ return 'id-error';
6952
+ }
6953
+ }
6954
+ return String(id);
6955
+ };
6807
6956
  // Normalize a row from the API format (object with numeric keys) into a proper TableCell array.
6808
6957
  // The API may return cells as [{"0": {cell}, "1": {cell}}, ...] instead of [[cell, cell], ...].
6809
6958
  const normalizeRow = (row) => {
@@ -6998,13 +7147,15 @@ const DfFormTable = ({ id, properties, mode = 'edit', formData = {}, validationE
6998
7147
  ? cell.components.map((comp, compIndex) => {
6999
7148
  // CRITICAL: Only generate ID if it's missing - never regenerate existing IDs
7000
7149
  // This prevents component remounting and losing input state
7001
- if (comp.id && typeof comp.id === 'string' && comp.id.trim() !== '') {
7002
- // ID already exists - keep it as is
7003
- return comp;
7150
+ const existingId = ensureStringId(comp.id);
7151
+ if (existingId) {
7152
+ // ID already exists and is valid - keep it but ensure it's a string
7153
+ return { ...comp, id: existingId };
7004
7154
  }
7005
7155
  // Generate unique ID that includes table ID, row, cell, and component index
7006
7156
  // This ensures no conflicts with other components
7007
- const uniqueId = `${comp.name || 'component'}-table-${id}-row-${rowIndex}-cell-${cellIndex}-comp-${compIndex}`;
7157
+ const sanitizedTableId = ensureStringId(id);
7158
+ const uniqueId = `${comp.name || 'component'}-table-${sanitizedTableId}-row-${rowIndex}-cell-${cellIndex}-comp-${compIndex}`;
7008
7159
  return {
7009
7160
  ...comp,
7010
7161
  id: uniqueId