df-ae-forms-package 1.1.5 → 1.1.6

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
@@ -820,7 +820,6 @@ const DfFormInput = ({ id, properties, validationErrors = {}, formValue = '', in
820
820
  // Update ref if ID changes (shouldn't happen, but safety check)
821
821
  React.useEffect(() => {
822
822
  if (id !== componentIdRef.current) {
823
- console.warn(`[DfFormInput] Component ID changed from ${componentIdRef.current} to ${id}`);
824
823
  componentIdRef.current = id;
825
824
  }
826
825
  }, [id]);
@@ -2998,8 +2997,6 @@ const DfFormLocation = ({ id, properties, validationErrors = {}, formValue = nul
2998
2997
  }
2999
2998
  }
3000
2999
  catch (error) {
3001
- // Capacitor not properly initialized, fall through to web geolocation
3002
- console.warn('Capacitor Geolocation not available, falling back to web geolocation');
3003
3000
  }
3004
3001
  }
3005
3002
  // Fallback to standard web geolocation API (for browsers)
@@ -3713,7 +3710,6 @@ const AttachmentThumbnails = ({ attachments, onRemove }) => {
3713
3710
  newUrls.set(index, url);
3714
3711
  }
3715
3712
  catch (e) {
3716
- console.warn('Failed to create object URL for attachment:', e);
3717
3713
  }
3718
3714
  }
3719
3715
  else if (file && file.url && file.type && file.type.startsWith('image/')) {
@@ -4624,7 +4620,6 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4624
4620
  zIndex: 5
4625
4621
  }, children: jsxRuntime.jsxs("button", { onClick: (e) => {
4626
4622
  e.stopPropagation();
4627
- console.log('[TableView] Add Entry button clicked (grid view)', 'entries:', dataEntries.length, 'max:', maxEntries);
4628
4623
  onAddEntry();
4629
4624
  }, disabled: dataEntries.length >= maxEntries, style: {
4630
4625
  padding: '8px 16px',
@@ -4750,7 +4745,6 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
4750
4745
  justifyContent: 'center'
4751
4746
  }, children: jsxRuntime.jsxs("button", { onClick: (e) => {
4752
4747
  e.stopPropagation();
4753
- console.log('[TableView] Add Entry button clicked (list view)', 'entries:', dataEntries.length, 'max:', maxEntries);
4754
4748
  onAddEntry();
4755
4749
  }, disabled: dataEntries.length >= maxEntries, style: {
4756
4750
  padding: '8px 16px',
@@ -5003,12 +4997,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
5003
4997
  // In edit/preview modes, don't handle value changes as components are read-only
5004
4998
  }, [mode, onValueChange]);
5005
4999
  const handleAddEntry = React.useCallback(() => {
5006
- console.log('[DfFormDataGrid] handleAddEntry called - Component ID:', id);
5007
5000
  // Safety check: ensure we have entries array
5008
5001
  const currentEntries = Array.isArray(properties.entries) ? properties.entries : [];
5009
- console.log('[DfFormDataGrid] gridComponents:', gridComponents.length);
5010
- console.log('[DfFormDataGrid] current entries count:', currentEntries.length);
5011
- console.log('[DfFormDataGrid] onValueChange exists:', !!onValueChange);
5012
5002
  // Use timestamp and random string to ensure uniqueness even if entries are deleted
5013
5003
  const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
5014
5004
  const newEntryId = `entry-${uniqueSuffix}`;
@@ -5030,11 +5020,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
5030
5020
  }),
5031
5021
  styles: {}
5032
5022
  };
5033
- console.log('[DfFormDataGrid] newEntry created:', newEntry.id, 'with', newEntry.components.length, 'components');
5034
5023
  const updatedEntries = [...currentEntries, newEntry];
5035
- console.log('[DfFormDataGrid] updatedEntries count:', updatedEntries.length);
5036
5024
  if (onValueChange) {
5037
- console.log('[DfFormDataGrid] calling onValueChange with updated datagrid structure - Entries:', updatedEntries.length);
5038
5025
  onValueChange({
5039
5026
  id: gridId, // Use sanitized gridId
5040
5027
  value: {
@@ -5044,13 +5031,9 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
5044
5031
  });
5045
5032
  // Notify parent if callback provided
5046
5033
  if (onEntryAdd) {
5047
- console.log('[DfFormDataGrid] calling onEntryAdd callback');
5048
5034
  onEntryAdd();
5049
5035
  }
5050
5036
  }
5051
- else {
5052
- console.warn('[DfFormDataGrid] Cannot add entry: onValueChange is missing');
5053
- }
5054
5037
  }, [onValueChange, properties, gridId, gridComponents, onEntryAdd]);
5055
5038
  const handleRemoveEntry = React.useCallback((entryIndex) => {
5056
5039
  // Safety check: ensure we have entries array
@@ -5059,7 +5042,6 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
5059
5042
  .filter((_, index) => index !== entryIndex)
5060
5043
  .map((entry, index) => ({ ...entry, index })); // Only update index, preserve unique ID
5061
5044
  if (onValueChange) {
5062
- console.log('[DfFormDataGrid] Removing entry at index:', entryIndex, 'New count:', updatedEntries.length);
5063
5045
  onValueChange({
5064
5046
  id: gridId, // Use sanitized gridId
5065
5047
  value: { ...properties, entries: updatedEntries }
@@ -5447,8 +5429,8 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5447
5429
  const [validationErrors, setValidationErrors] = React.useState({});
5448
5430
  const [formSubmitted, setFormSubmitted] = React.useState(false);
5449
5431
  const [touchedFields, setTouchedFields] = React.useState({});
5450
- // Conditional logic disabled temporarily
5451
- // const [componentVisibility, setComponentVisibility] = useState<Record<string, boolean>>({})
5432
+ // Component visibility state - driven by conditional logic evaluation
5433
+ const [componentVisibility, setComponentVisibility] = React.useState({});
5452
5434
  // Track raised issues for threshold conditions (Set of condition IDs)
5453
5435
  const [raisedThresholdIssues, setRaisedThresholdIssues] = React.useState(new Set());
5454
5436
  // Track threshold action completions: Map<conditionId, { notesCompleted, attachmentsCompleted, emailSent }>
@@ -5546,6 +5528,11 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5546
5528
  if (component.children && Array.isArray(component.children)) {
5547
5529
  initializeComponentValues(component.children, values);
5548
5530
  }
5531
+ // CRITICAL for conditional logic: Also store value under the label key
5532
+ // so condition lookups by label (e.g. "Temperature") work during init
5533
+ if (component.basic?.label && componentId && values[componentId] !== undefined) {
5534
+ values[component.basic.label] = values[componentId];
5535
+ }
5549
5536
  });
5550
5537
  };
5551
5538
  const initializeFormState = React.useCallback(() => {
@@ -5617,15 +5604,12 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5617
5604
  const labelPart = safeLabel ? `-${safeLabel}` : '';
5618
5605
  validatedComponent.id = `${name}${labelPart}-${index}`;
5619
5606
  }
5620
- console.warn(`[DfFormPreview] Fixed missing/invalid ID: ${validatedComponent.id}`);
5621
5607
  }
5622
5608
  else {
5623
5609
  // ID is a valid string, check for duplicates
5624
5610
  if (seenIds.has(validatedComponent.id)) {
5625
- console.error(`[DfFormPreview] Duplicate component ID detected: ${validatedComponent.id}`);
5626
5611
  // Generate a unique ID for duplicate - using index to keep it somewhat stable
5627
5612
  validatedComponent.id = `${validatedComponent.id}-dup-${index}`;
5628
- console.warn(`[DfFormPreview] Generated new unique ID: ${validatedComponent.id}`);
5629
5613
  }
5630
5614
  }
5631
5615
  seenIds.add(validatedComponent.id);
@@ -5680,7 +5664,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5680
5664
  validatedFormComponents = getValidatedComponents(localFormComponents);
5681
5665
  // Synchronize local state if components were mutated (IDs added/fixed)
5682
5666
  if (JSON.stringify(validatedFormComponents) !== JSON.stringify(localFormComponents)) {
5683
- console.log('[DfFormPreview] Synchronizing local components after ID validation');
5684
5667
  setLocalFormComponents(validatedFormComponents);
5685
5668
  onFormDataChange?.(validatedFormComponents);
5686
5669
  }
@@ -5733,13 +5716,229 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5733
5716
  // Initialize notes and attachments state
5734
5717
  setComponentNotes(prev => ({ ...initialNotes, ...prev }));
5735
5718
  setComponentAttachments(prev => ({ ...initialAttachments, ...prev }));
5736
- // Conditional logic evaluation disabled temporarily to fix table issues
5737
- // evaluateConditionalLogic()
5719
+ // Evaluate conditional logic on initialization
5720
+ evaluateConditionalLogic(initialValues);
5738
5721
  }, [initialFormData, localFormComponents]);
5739
- // Conditional logic disabled temporarily - all components are always visible
5740
- const shouldShowComponent = React.useCallback((_componentId) => {
5741
- return true;
5722
+ // ======================== CONDITIONAL LOGIC ENGINE ========================
5723
+ // This is a pure visibility layer. It NEVER modifies component data.
5724
+ // It computes a { [componentId]: boolean } map based on each component's
5725
+ // `logic` property and the current form values.
5726
+ // Helper: Flatten all components (including nested) into a flat list
5727
+ const flattenAllComponents = React.useCallback((components) => {
5728
+ const flat = [];
5729
+ const traverse = (items) => {
5730
+ if (!items || !Array.isArray(items))
5731
+ return;
5732
+ for (const item of items) {
5733
+ flat.push(item);
5734
+ if (item.children && Array.isArray(item.children))
5735
+ traverse(item.children);
5736
+ if (item.cells && Array.isArray(item.cells)) {
5737
+ for (const row of item.cells) {
5738
+ const normalizedRow = normalizeTableRow(row);
5739
+ for (const cell of normalizedRow) {
5740
+ if (cell.components)
5741
+ traverse(cell.components);
5742
+ }
5743
+ }
5744
+ }
5745
+ if (item.entries && Array.isArray(item.entries)) {
5746
+ for (const entry of item.entries) {
5747
+ if (entry.components)
5748
+ traverse(entry.components);
5749
+ }
5750
+ }
5751
+ if (item.templateComponents && Array.isArray(item.templateComponents)) {
5752
+ traverse(item.templateComponents);
5753
+ }
5754
+ }
5755
+ };
5756
+ traverse(components);
5757
+ return flat;
5758
+ }, []);
5759
+ // Helper: Get the value of a component referenced by a condition's `when` field.
5760
+ // The `when` field is typically a component LABEL (e.g. "Temperature"), not an ID.
5761
+ const getConditionComponentValue = React.useCallback((conditionWhen, allComponents, currentFormValues) => {
5762
+ // Try to find the component by label, id, or _id
5763
+ const comp = allComponents.find(c => c.basic?.label === conditionWhen ||
5764
+ ensureStringId$1(c.id) === conditionWhen ||
5765
+ ensureStringId$1(c._id) === conditionWhen);
5766
+ if (!comp)
5767
+ return undefined;
5768
+ const compId = ensureStringId$1(comp.id || comp._id);
5769
+ // Check formValues by ID first
5770
+ if (compId && currentFormValues[compId] !== undefined) {
5771
+ return currentFormValues[compId];
5772
+ }
5773
+ // Also check by label (main website stores values by label too)
5774
+ if (comp.basic?.label && currentFormValues[comp.basic.label] !== undefined) {
5775
+ return currentFormValues[comp.basic.label];
5776
+ }
5777
+ // Fall back to component's basic.value or defaultValue
5778
+ if (comp.basic?.value !== undefined && comp.basic?.value !== null && comp.basic?.value !== '') {
5779
+ return comp.basic.value;
5780
+ }
5781
+ if (comp.basic?.defaultValue !== undefined && comp.basic?.defaultValue !== null && comp.basic?.defaultValue !== '') {
5782
+ return comp.basic.defaultValue;
5783
+ }
5784
+ return undefined;
5742
5785
  }, []);
5786
+ // Helper: Evaluate a single condition
5787
+ const evaluateSingleCondition = React.useCallback((condition, componentValue) => {
5788
+ const { operator, value } = condition;
5789
+ // isEmpty / isNotEmpty
5790
+ if (operator === 'isEmpty') {
5791
+ if (componentValue == null)
5792
+ return true;
5793
+ if (typeof componentValue === 'string')
5794
+ return componentValue.trim() === '';
5795
+ if (Array.isArray(componentValue))
5796
+ return componentValue.length === 0;
5797
+ return false;
5798
+ }
5799
+ if (operator === 'isNotEmpty') {
5800
+ if (componentValue == null)
5801
+ return false;
5802
+ if (typeof componentValue === 'string')
5803
+ return componentValue.trim() !== '';
5804
+ if (Array.isArray(componentValue))
5805
+ return componentValue.length > 0;
5806
+ return true;
5807
+ }
5808
+ // checked / notChecked (for checkboxes)
5809
+ if (operator === 'checked' || operator === 'notChecked') {
5810
+ let isChecked = false;
5811
+ if (typeof componentValue === 'boolean')
5812
+ isChecked = componentValue;
5813
+ else if (typeof componentValue === 'string')
5814
+ isChecked = componentValue.toLowerCase() === 'true' || componentValue === '1' || componentValue.length > 0;
5815
+ else if (Array.isArray(componentValue))
5816
+ isChecked = componentValue.length > 0;
5817
+ else if (typeof componentValue === 'number')
5818
+ isChecked = componentValue > 0;
5819
+ return operator === 'checked' ? isChecked : !isChecked;
5820
+ }
5821
+ // equals / notEquals
5822
+ if (operator === 'equals' || operator === 'notEquals') {
5823
+ let isEqual = false;
5824
+ // Loose comparison
5825
+ if (componentValue == value)
5826
+ isEqual = true;
5827
+ // String comparison (case-insensitive, trimmed)
5828
+ if (!isEqual && componentValue != null && value != null) {
5829
+ const strA = String(componentValue).trim();
5830
+ const strB = String(value).trim();
5831
+ // Numeric comparison within strings
5832
+ if (strA !== '' && strB !== '' && !isNaN(Number(strA)) && !isNaN(Number(strB))) {
5833
+ isEqual = Number(strA) === Number(strB);
5834
+ }
5835
+ if (!isEqual) {
5836
+ isEqual = strA.toLowerCase() === strB.toLowerCase();
5837
+ }
5838
+ }
5839
+ // Array contains check (for multi-select)
5840
+ if (!isEqual && Array.isArray(componentValue)) {
5841
+ isEqual = componentValue.some(v => String(v).trim().toLowerCase() === String(value).trim().toLowerCase());
5842
+ }
5843
+ return operator === 'equals' ? isEqual : !isEqual;
5844
+ }
5845
+ // contains / notContains
5846
+ if (operator === 'contains' || operator === 'notContains') {
5847
+ let doesContain = false;
5848
+ if (typeof componentValue === 'string' && value != null) {
5849
+ doesContain = componentValue.trim().toLowerCase().includes(String(value).trim().toLowerCase());
5850
+ }
5851
+ else if (Array.isArray(componentValue) && value != null) {
5852
+ doesContain = componentValue.some(v => String(v).trim().toLowerCase() === String(value).trim().toLowerCase());
5853
+ }
5854
+ return operator === 'contains' ? doesContain : !doesContain;
5855
+ }
5856
+ // Numeric comparisons
5857
+ if (['greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual'].includes(operator)) {
5858
+ const numA = typeof componentValue === 'number' ? componentValue : parseFloat(String(componentValue));
5859
+ const numB = typeof value === 'number' ? value : parseFloat(String(value));
5860
+ if (isNaN(numA) || isNaN(numB))
5861
+ return false;
5862
+ switch (operator) {
5863
+ case 'greaterThan': return numA > numB;
5864
+ case 'lessThan': return numA < numB;
5865
+ case 'greaterThanOrEqual': return numA >= numB;
5866
+ case 'lessThanOrEqual': return numA <= numB;
5867
+ default: return false;
5868
+ }
5869
+ }
5870
+ return false;
5871
+ }, []);
5872
+ // Main conditional logic evaluation function
5873
+ const evaluateConditionalLogic = React.useCallback((explicitValues) => {
5874
+ const currentComponents = localFormComponents;
5875
+ const currentValues = explicitValues || formValues;
5876
+ if (!currentComponents || currentComponents.length === 0)
5877
+ return;
5878
+ const allComponents = flattenAllComponents(currentComponents);
5879
+ const visibility = {};
5880
+ allComponents.forEach(component => {
5881
+ const compId = ensureStringId$1(component.id || component._id);
5882
+ if (!compId)
5883
+ return;
5884
+ // Container components are ALWAYS visible — their children handle their own logic
5885
+ if (['table', 'datagrid', 'section', 'heading', 'instructions'].includes(component.name)) {
5886
+ visibility[compId] = true;
5887
+ return;
5888
+ }
5889
+ // Get the logic property (API sends it as `logic`, builder uses `conditional`)
5890
+ const logic = component.logic || component.conditional;
5891
+ // No logic or action is 'always' → always show
5892
+ if (!logic || logic.action === 'always' || !logic.conditions || logic.conditions.length === 0) {
5893
+ visibility[compId] = true;
5894
+ return;
5895
+ }
5896
+ // Evaluate each condition
5897
+ const conditionResults = logic.conditions.map((condition) => {
5898
+ const componentValue = getConditionComponentValue(condition.when, allComponents, currentValues);
5899
+ return evaluateSingleCondition(condition, componentValue);
5900
+ });
5901
+ // Determine if conditions are met based on 'when' (all/any)
5902
+ let conditionsMet;
5903
+ if (logic.when === 'any') {
5904
+ conditionsMet = conditionResults.some((r) => r);
5905
+ }
5906
+ else {
5907
+ // Default to 'all'
5908
+ conditionsMet = conditionResults.every((r) => r);
5909
+ }
5910
+ // Apply action
5911
+ if (logic.action === 'show') {
5912
+ visibility[compId] = conditionsMet;
5913
+ }
5914
+ else if (logic.action === 'hide') {
5915
+ visibility[compId] = !conditionsMet;
5916
+ }
5917
+ else {
5918
+ visibility[compId] = true;
5919
+ }
5920
+ });
5921
+ // Only update state if visibility actually changed (prevent re-render loops)
5922
+ setComponentVisibility(prev => {
5923
+ const prevStr = JSON.stringify(prev);
5924
+ const newStr = JSON.stringify(visibility);
5925
+ if (prevStr === newStr)
5926
+ return prev;
5927
+ return visibility;
5928
+ });
5929
+ }, [localFormComponents, formValues, flattenAllComponents, getConditionComponentValue, evaluateSingleCondition]);
5930
+ // Re-evaluate conditional logic whenever form values change
5931
+ React.useEffect(() => {
5932
+ evaluateConditionalLogic();
5933
+ }, [formValues, evaluateConditionalLogic]);
5934
+ // Check if a component should be visible
5935
+ const shouldShowComponent = React.useCallback((componentId) => {
5936
+ // In preview mode (read-only submission view), always show everything
5937
+ if (isPreviewMode)
5938
+ return true;
5939
+ // Default to visible if not explicitly set to false
5940
+ return componentVisibility[componentId] !== false;
5941
+ }, [componentVisibility, isPreviewMode]);
5743
5942
  // Handle form value changes and re-evaluate conditional logic
5744
5943
  const onFormValueChange = React.useCallback((change) => {
5745
5944
  // CRITICAL: Validate that change.id is valid and unique
@@ -5791,6 +5990,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
5791
5990
  // Only update formValues for actual value changes, not structure updates
5792
5991
  if (!isStructureUpdate) {
5793
5992
  newFormValues[change.id] = change.value;
5993
+ // CRITICAL for conditional logic: Also store value under the component's label.
5994
+ // Conditions reference components by label (e.g. "Temperature"), not by ID.
5995
+ const allComponents = flattenAllComponents(localFormComponents);
5996
+ const targetComp = allComponents.find(c => ensureStringId$1(c.id || c._id) === change.id);
5997
+ if (targetComp?.basic?.label) {
5998
+ newFormValues[targetComp.basic.label] = change.value;
5999
+ }
5794
6000
  setFormValues(newFormValues);
5795
6001
  }
5796
6002
  // Clear raised issues for this component when value changes
@@ -6531,7 +6737,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6531
6737
  componentCounterRef.current += 1;
6532
6738
  cachedId = `generated-${name}-${componentCounterRef.current}-${Math.random().toString(36).substr(2, 9)}`;
6533
6739
  componentIdCacheRef.current.set(cacheKey, cachedId);
6534
- console.warn('[DfFormPreview] Generated and cached ID for component:', cachedId, 'key:', cacheKey);
6535
6740
  }
6536
6741
  finalComponentId = cachedId;
6537
6742
  // Create a new component object with the cached ID (don't mutate original)
@@ -6547,9 +6752,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6547
6752
  if (formValue !== undefined) {
6548
6753
  // Check if this value is being used by multiple components
6549
6754
  const componentsWithSameValue = localFormComponents.filter(comp => comp.id !== componentId && formValues[comp.id] === formValue);
6550
- if (componentsWithSameValue.length > 0) {
6551
- console.warn(`[DfFormPreview] Component ${componentId} shares form value with other components:`, componentsWithSameValue.map(c => c.id));
6552
- }
6755
+ if (componentsWithSameValue.length > 0) ;
6553
6756
  }
6554
6757
  const commonProps = {
6555
6758
  id: componentId, // Use the validated component ID
@@ -6762,10 +6965,8 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6762
6965
  }, onValueChange: (change) => {
6763
6966
  const changeId = ensureStringId$1(change.id);
6764
6967
  const componentId = ensureStringId$1(component.id);
6765
- console.log(`[DfFormPreview] datagrid onValueChange - Target: ${changeId}, Component: ${componentId}`);
6766
6968
  // Handle datagrid value changes (entries updates)
6767
6969
  if (changeId === componentId && change.value && typeof change.value === 'object' && 'entries' in change.value) {
6768
- console.log('[DfFormPreview] datagrid entries update - entries count:', change.value.entries?.length);
6769
6970
  // Update localFormComponents with new entries structure
6770
6971
  const updatedComponents = localFormComponents.map(comp => {
6771
6972
  const currentCompId = ensureStringId$1(comp.id);
@@ -6808,7 +7009,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
6808
7009
  }
6809
7010
  });
6810
7011
  if (valuesChanged) {
6811
- console.log('[DfFormPreview] Initializing form values for new datagrid entries');
6812
7012
  setFormValues(newValues);
6813
7013
  }
6814
7014
  }