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