df-ae-forms-package 1.0.99 → 1.1.2

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}-${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,
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
@@ -4629,7 +4656,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4629
4656
  comp.basic?.label === templateComponent.basic?.label);
4630
4657
  }
4631
4658
  if (!entryComponent) {
4632
- const uniqueId = `${templateComponent.id}-${entry.id}-${componentIndex}`;
4659
+ const uniqueId = `${ensureStringId$2(templateComponent.id) || 'comp'}-${ensureStringId$2(entry.id)}-${componentIndex}`;
4633
4660
  entryComponent = {
4634
4661
  ...templateComponent,
4635
4662
  id: uniqueId,
@@ -4642,7 +4669,7 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4642
4669
  else {
4643
4670
  entryComponent = {
4644
4671
  ...entryComponent,
4645
- id: entryComponent.id,
4672
+ id: ensureStringId$2(entryComponent.id) || `${ensureStringId$2(templateComponent.id) || 'comp'}-${ensureStringId$2(entry.id)}-${componentIndex}`,
4646
4673
  basic: {
4647
4674
  ...entryComponent.basic,
4648
4675
  showLabel: columnView // Show label in column view, hide in grid view
@@ -4710,9 +4737,11 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4710
4737
  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
4738
  const [isCollapsed, setIsCollapsed] = useState(false);
4712
4739
  const hasInitialized = useRef(false);
4740
+ const gridId = ensureStringId$2(id);
4713
4741
  // Get all components in the grid and sanitize them to ensure no data leaks into templates
4714
- let gridComponents = (properties.templateComponents || []).map(comp => ({
4742
+ let gridComponents = (properties.templateComponents || []).map((comp, index) => ({
4715
4743
  ...comp,
4744
+ id: ensureStringId$2(comp.id) || `${gridId}-template-${index}`, // CRITICAL: Ensure template components have stable string IDs
4716
4745
  basic: {
4717
4746
  ...comp.basic,
4718
4747
  // Use an empty string instead of undefined to satisfy component typing
@@ -4723,9 +4752,10 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4723
4752
  const dataEntries = properties.entries || [];
4724
4753
  // Fallback: If no template components but we have entries, extract template from first entry
4725
4754
  if (gridComponents.length === 0 && dataEntries.length > 0 && dataEntries[0].components) {
4726
- gridComponents = dataEntries[0].components.map((comp) => ({
4755
+ gridComponents = dataEntries[0].components.map((comp, index) => ({
4727
4756
  ...comp,
4728
- id: comp.id?.replace(/-entry-\d+$/, '') || comp.id, // Remove entry suffix for template
4757
+ // Remove entry suffix for template, use stable ID if missing
4758
+ id: ensureStringId$2(comp.id).replace(/-entry-.*$/, '') || `${gridId}-template-${index}`,
4729
4759
  basic: {
4730
4760
  ...comp.basic,
4731
4761
  // Clear any data values while keeping types happy
@@ -4768,7 +4798,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4768
4798
  index: 0,
4769
4799
  components: gridComponents.map((comp, componentIndex) => ({
4770
4800
  ...comp,
4771
- id: `${comp.id}-${newEntryId}-${componentIndex}`,
4801
+ id: `${ensureStringId$2(comp.id)}-${newEntryId}-${componentIndex}`,
4772
4802
  basic: {
4773
4803
  ...comp.basic,
4774
4804
  showLabel: false // Hide label in datagrid cells
@@ -4793,12 +4823,9 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4793
4823
  // Check if entry is missing any template components or if existing components need updates
4794
4824
  return gridComponents.some((templateComp, componentIndex) => {
4795
4825
  const existingComponent = entry.components?.[componentIndex];
4796
- if (!existingComponent) {
4797
- return true; // Missing component at this index
4826
+ if (!existingComponent || !existingComponent.id) {
4827
+ return true; // Missing component or missing ID at this index
4798
4828
  }
4799
- // We don't check for ID matches here anymore
4800
- // const expectedId = `${templateComp.id}-entry-${entry.index}-${componentIndex}`
4801
- // const hasProperId = existingComponent.id === expectedId
4802
4829
  // Check if existing component needs to be updated with new template properties
4803
4830
  // Compare key properties that should be synced
4804
4831
  const needsPropertyUpdate = JSON.stringify(existingComponent.basic?.options) !== JSON.stringify(templateComp.basic?.options) ||
@@ -4806,7 +4833,6 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4806
4833
  existingComponent.basic?.defaultValue !== templateComp.basic?.defaultValue ||
4807
4834
  existingComponent.basic?.label !== templateComp.basic?.label ||
4808
4835
  existingComponent.validation?.required !== templateComp.validation?.required;
4809
- // We do not check for ID mismatch anymore as we want to preserve unique IDs
4810
4836
  return needsPropertyUpdate;
4811
4837
  });
4812
4838
  });
@@ -4827,7 +4853,10 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4827
4853
  // Update existing component with new template properties while ensuring unique ID and preserving form values
4828
4854
  const updatedComponent = {
4829
4855
  ...templateComp,
4830
- id: existingComponent.id, // Preserve existing ID !!
4856
+ // Preserve existing ID (ensure it's a string) or generate one if missing
4857
+ id: (existingComponent.id && typeof existingComponent.id === 'string')
4858
+ ? existingComponent.id
4859
+ : `${ensureStringId$2(templateComp.id)}-${ensureStringId$2(entry.id)}-${componentIndex}`,
4831
4860
  basic: {
4832
4861
  ...templateComp.basic,
4833
4862
  showLabel: false, // Hide label in datagrid cells
@@ -4841,7 +4870,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4841
4870
  // Create new component based on template
4842
4871
  // Only for NEW components in existing entries (e.g. column added) do we generate a new ID
4843
4872
  const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
4844
- const newId = `${templateComp.id}-${uniqueSuffix}-${componentIndex}`;
4873
+ const newId = `${ensureStringId$2(templateComp.id)}-${uniqueSuffix}-${componentIndex}`;
4845
4874
  const newComponent = {
4846
4875
  ...templateComp,
4847
4876
  id: newId,
@@ -4947,7 +4976,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
4947
4976
  id: newEntryId,
4948
4977
  index: properties.entries.length,
4949
4978
  components: gridComponents.map((comp, componentIndex) => {
4950
- const componentId = `${comp.id}-${newEntryId}-${componentIndex}`;
4979
+ const componentId = `${ensureStringId$2(comp.id)}-${newEntryId}-${componentIndex}`;
4951
4980
  return {
4952
4981
  ...comp,
4953
4982
  // Use the unique entry ID in the component ID to prevent collisions
@@ -5311,6 +5340,37 @@ const DfFormSection = ({ id, properties, mode = 'edit', formData = {}, onValueCh
5311
5340
 
5312
5341
  // Dynamic imports to avoid circular dependencies
5313
5342
  const DfFormTable$1 = React.lazy(() => Promise.resolve().then(function () { return dfFormTable; }));
5343
+ // Helper to ensure an ID is a stable string, handling MongoDB-style object IDs
5344
+ // This is used globally in this file to prevent [object Object] corruption
5345
+ const ensureStringId$1 = (id) => {
5346
+ if (!id)
5347
+ return '';
5348
+ if (typeof id === 'string') {
5349
+ // If the ID is already corrupted, return empty to trigger regeneration
5350
+ if (id.includes('[object Object]'))
5351
+ return '';
5352
+ return id;
5353
+ }
5354
+ if (typeof id === 'object' && id !== null) {
5355
+ if (id.$oid)
5356
+ return String(id.$oid);
5357
+ if (id._id)
5358
+ return ensureStringId$1(id._id);
5359
+ // Fallback for other object structures that might be IDs
5360
+ try {
5361
+ // If it has a toString that isn't the default, use it
5362
+ const str = id.toString();
5363
+ if (str && str !== '[object Object]')
5364
+ return str;
5365
+ // Otherwise, use a hash or just JSON
5366
+ return JSON.stringify(id);
5367
+ }
5368
+ catch (e) {
5369
+ return 'id-error';
5370
+ }
5371
+ }
5372
+ return String(id);
5373
+ };
5314
5374
  // Normalize a table row from API format (object with numeric keys) into a proper array.
5315
5375
  // The API may return cells as [{"0": {cell}, "1": {cell}}, ...] instead of [[cell, cell], ...].
5316
5376
  const normalizeTableRow = (row) => {
@@ -5356,16 +5416,17 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5356
5416
  // Recursive function to initialize values from nested components
5357
5417
  const initializeComponentValues = (components, values) => {
5358
5418
  components.forEach(component => {
5359
- if (component.id) {
5419
+ const componentId = ensureStringId$1(component.id);
5420
+ if (componentId) {
5360
5421
  // ALWAYS prioritize existing form state (values param) if it exists
5361
- if (values[component.id] !== undefined) ;
5362
- // Then check initialFormData (if we are merging it) - handled by caller usually, but safe to check
5363
- else if (component.basic && 'value' in component.basic && component.basic.value !== undefined && component.basic.value !== '' && component.basic.value !== null) {
5364
- values[component.id] = component.basic.value;
5422
+ if (values[componentId] !== undefined) ;
5423
+ // Then use captured value in basic.value
5424
+ else if (component.basic && 'value' in component.basic && component.basic.value !== undefined) {
5425
+ values[componentId] = component.basic.value;
5365
5426
  }
5366
- // Then check defaultValue
5367
- else if (component.basic && 'defaultValue' in component.basic && component.basic.defaultValue !== undefined && component.basic.defaultValue !== '' && component.basic.defaultValue !== null) {
5368
- values[component.id] = component.basic.defaultValue;
5427
+ // Then use defaultValue
5428
+ else if (component.basic && 'defaultValue' in component.basic && component.basic.defaultValue !== undefined) {
5429
+ values[componentId] = component.basic.defaultValue;
5369
5430
  }
5370
5431
  // Only then default to empty
5371
5432
  else {
@@ -5435,12 +5496,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5435
5496
  // Helper to extract notes and attachments recursively
5436
5497
  const extractNotesAndAttachments = (components) => {
5437
5498
  components.forEach(comp => {
5438
- if (comp.id) {
5499
+ const componentId = ensureStringId$1(comp.id);
5500
+ if (componentId) {
5439
5501
  if (comp.basic?.notes || comp.basic?.comments) {
5440
- initialNotes[comp.id] = comp.basic?.notes || comp.basic?.comments;
5502
+ initialNotes[componentId] = comp.basic?.notes || comp.basic?.comments;
5441
5503
  }
5442
5504
  if (comp.basic?.attachments) {
5443
- initialAttachments[comp.id] = comp.basic.attachments;
5505
+ initialAttachments[componentId] = comp.basic.attachments;
5444
5506
  }
5445
5507
  }
5446
5508
  // Recurse
@@ -5461,42 +5523,67 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5461
5523
  });
5462
5524
  };
5463
5525
  // Validate component IDs for uniqueness without mutating original props
5464
- const getValidatedComponents = (components) => {
5526
+ const getValidatedComponents = (components, parentId) => {
5527
+ const parentIdStr = parentId ? ensureStringId$1(parentId) : '';
5465
5528
  return components.map((component, index) => {
5466
5529
  let validatedComponent = { ...component };
5467
- // Ensure component has an ID
5468
- if (!validatedComponent.id || typeof validatedComponent.id !== 'string' || validatedComponent.id.trim() === '') {
5530
+ // Ensure component has a valid string ID
5531
+ let currentId = validatedComponent.id;
5532
+ // CRITICAL: Reject any ID that is not a string, is empty, or contains "[object Object]"
5533
+ let isIdValid = typeof currentId === 'string' &&
5534
+ currentId.trim() !== '' &&
5535
+ !currentId.includes('[object Object]');
5536
+ if (!isIdValid) {
5537
+ // If it's an object ID (but not a corrupted string), convert it to string
5538
+ if (currentId && typeof currentId === 'object') {
5539
+ const convertedId = ensureStringId$1(currentId);
5540
+ if (convertedId && !convertedId.includes('[object Object]')) {
5541
+ validatedComponent.id = convertedId;
5542
+ isIdValid = true;
5543
+ }
5544
+ }
5545
+ }
5546
+ if (!isIdValid) {
5469
5547
  // Determine a base name for the ID
5470
5548
  const name = validatedComponent.name || 'component';
5471
5549
  const label = validatedComponent.basic?.label || '';
5472
5550
  const safeLabel = label.replace(/[^a-zA-Z0-9]/g, '').toLowerCase().substring(0, 10);
5473
- // Generate a unique ID
5474
- validatedComponent.id = `${name}-${safeLabel || 'gen'}-${Date.now()}-${Math.random().toString(36).substr(2, 6)}-${index}`;
5475
- console.warn(`[DfFormPreview] Assigned missing ID: ${validatedComponent.id}`);
5551
+ // Generate a stable unique ID if parentId is provided, otherwise fallback to semi-stable
5552
+ if (parentIdStr) {
5553
+ validatedComponent.id = `${parentIdStr}-${name}-${index}`;
5554
+ }
5555
+ else {
5556
+ // For root components, use label if available for stability
5557
+ const labelPart = safeLabel ? `-${safeLabel}` : '';
5558
+ validatedComponent.id = `${name}${labelPart}-${index}`;
5559
+ }
5560
+ console.warn(`[DfFormPreview] Fixed missing/invalid ID: ${validatedComponent.id}`);
5476
5561
  }
5477
5562
  else {
5478
- // Check for duplicates
5563
+ // ID is a valid string, check for duplicates
5479
5564
  if (seenIds.has(validatedComponent.id)) {
5480
5565
  console.error(`[DfFormPreview] Duplicate component ID detected: ${validatedComponent.id}`);
5481
- // Generate a unique ID for duplicate
5482
- validatedComponent.id = `${validatedComponent.id}-duplicate-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
5566
+ // Generate a unique ID for duplicate - using index to keep it somewhat stable
5567
+ validatedComponent.id = `${validatedComponent.id}-dup-${index}`;
5483
5568
  console.warn(`[DfFormPreview] Generated new unique ID: ${validatedComponent.id}`);
5484
5569
  }
5485
5570
  }
5486
5571
  seenIds.add(validatedComponent.id);
5487
5572
  // Recursively validate nested components
5488
5573
  if (validatedComponent.children && Array.isArray(validatedComponent.children)) {
5489
- validatedComponent.children = getValidatedComponents(validatedComponent.children);
5574
+ validatedComponent.children = getValidatedComponents(validatedComponent.children, validatedComponent.id);
5490
5575
  }
5491
5576
  if (validatedComponent.cells && Array.isArray(validatedComponent.cells)) {
5492
- validatedComponent.cells = validatedComponent.cells.map((row) => {
5577
+ validatedComponent.cells = validatedComponent.cells.map((row, rowIndex) => {
5493
5578
  const normalizedRow = normalizeTableRow(row);
5494
5579
  if (normalizedRow.length > 0) {
5495
- return normalizedRow.map((cell) => {
5580
+ return normalizedRow.map((cell, cellIndex) => {
5496
5581
  if (cell && cell.components && Array.isArray(cell.components)) {
5582
+ // Stable ID for table cells: tableId-row-X-cell-Y
5583
+ const cellContextId = `${ensureStringId$1(validatedComponent.id)}-row-${rowIndex}-cell-${cellIndex}`;
5497
5584
  return {
5498
5585
  ...cell,
5499
- components: getValidatedComponents(cell.components)
5586
+ components: getValidatedComponents(cell.components, cellContextId)
5500
5587
  };
5501
5588
  }
5502
5589
  return cell;
@@ -5508,9 +5595,12 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5508
5595
  if (validatedComponent.entries && Array.isArray(validatedComponent.entries)) {
5509
5596
  validatedComponent.entries = validatedComponent.entries.map((entry) => {
5510
5597
  if (entry && entry.components && Array.isArray(entry.components)) {
5598
+ // Stable ID for datagrid entries: datagridId-entryId or using entry.id
5599
+ const entryId = entry.id ? ensureStringId$1(entry.id) : `${ensureStringId$1(validatedComponent.id)}-entry-${entry.index}`;
5511
5600
  return {
5512
5601
  ...entry,
5513
- components: getValidatedComponents(entry.components)
5602
+ id: entryId, // Ensure entry itself has a string ID
5603
+ components: getValidatedComponents(entry.components, entryId)
5514
5604
  };
5515
5605
  }
5516
5606
  return entry;
@@ -6303,13 +6393,10 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6303
6393
  const componentIdCacheRef = useRef(new Map());
6304
6394
  const componentCounterRef = useRef(0);
6305
6395
  const renderFormComponent = (component) => {
6306
- // CRITICAL: Ensure component has a valid unique ID
6396
+ // CRITICAL: Ensure component has a valid unique string ID
6307
6397
  // NEVER regenerate IDs - always use existing or cached ID
6308
- let finalComponentId;
6309
- if (component.id && typeof component.id === 'string' && component.id.trim() !== '') {
6310
- // Component already has an ID - use it (most common case)
6311
- finalComponentId = component.id;
6312
- }
6398
+ let finalComponentId = ensureStringId$1(component.id);
6399
+ if (finalComponentId && finalComponentId.trim() !== '') ;
6313
6400
  else {
6314
6401
  // Component is missing an ID - need to generate and cache it
6315
6402
  // Create a stable cache key using component properties that don't change
@@ -6795,6 +6882,32 @@ const DfFormComments = ({ comment = '', onSave, placeholder = 'Enter your reason
6795
6882
  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 }) }) }) })] }));
6796
6883
  };
6797
6884
 
6885
+ // Helper to ensure an ID is a stable string, handling MongoDB-style object IDs
6886
+ const ensureStringId = (id) => {
6887
+ if (!id)
6888
+ return '';
6889
+ if (typeof id === 'string') {
6890
+ if (id.includes('[object Object]'))
6891
+ return '';
6892
+ return id;
6893
+ }
6894
+ if (typeof id === 'object' && id !== null) {
6895
+ if (id.$oid)
6896
+ return String(id.$oid);
6897
+ if (id._id)
6898
+ return ensureStringId(id._id);
6899
+ try {
6900
+ const str = id.toString();
6901
+ if (str && str !== '[object Object]')
6902
+ return str;
6903
+ return JSON.stringify(id);
6904
+ }
6905
+ catch (e) {
6906
+ return 'id-error';
6907
+ }
6908
+ }
6909
+ return String(id);
6910
+ };
6798
6911
  // Normalize a row from the API format (object with numeric keys) into a proper TableCell array.
6799
6912
  // The API may return cells as [{"0": {cell}, "1": {cell}}, ...] instead of [[cell, cell], ...].
6800
6913
  const normalizeRow = (row) => {
@@ -6989,13 +7102,15 @@ const DfFormTable = ({ id, properties, mode = 'edit', formData = {}, validationE
6989
7102
  ? cell.components.map((comp, compIndex) => {
6990
7103
  // CRITICAL: Only generate ID if it's missing - never regenerate existing IDs
6991
7104
  // This prevents component remounting and losing input state
6992
- if (comp.id && typeof comp.id === 'string' && comp.id.trim() !== '') {
6993
- // ID already exists - keep it as is
6994
- return comp;
7105
+ const existingId = ensureStringId(comp.id);
7106
+ if (existingId) {
7107
+ // ID already exists and is valid - keep it but ensure it's a string
7108
+ return { ...comp, id: existingId };
6995
7109
  }
6996
7110
  // Generate unique ID that includes table ID, row, cell, and component index
6997
7111
  // This ensures no conflicts with other components
6998
- const uniqueId = `${comp.name || 'component'}-table-${id}-row-${rowIndex}-cell-${cellIndex}-comp-${compIndex}`;
7112
+ const sanitizedTableId = ensureStringId(id);
7113
+ const uniqueId = `${comp.name || 'component'}-table-${sanitizedTableId}-row-${rowIndex}-cell-${cellIndex}-comp-${compIndex}`;
6999
7114
  return {
7000
7115
  ...comp,
7001
7116
  id: uniqueId