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