Enhance Component Preview and Workflow Loading Logic
- Updated ComponentPreview.vue to improve handling of select fields, ensuring the first option value is returned as the default if no form data exists. - Refactored handleFieldInput to support asynchronous updates for select fields, enhancing reactivity and ensuring the latest value is used from the form store. - Introduced retry logic with exponential backoff in loadProcess and loadFormData functions within [id].vue, improving resilience against temporary fetch failures. - Enhanced error handling and logging for loading processes and forms, providing clearer feedback during failures and retries. - Updated form data initialization to set default values for select fields when loading forms, ensuring a smoother user experience.
This commit is contained in:
parent
649a956063
commit
b7d6f42e76
@ -1590,15 +1590,43 @@ const getFieldValue = (component) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For select fields, return first option value as default if no form data exists
|
||||||
|
if (component.type === 'select' && component.props.options && component.props.options.length > 0) {
|
||||||
|
return component.props.options[0].value || '';
|
||||||
|
}
|
||||||
|
|
||||||
// Return component default value if no form data exists
|
// Return component default value if no form data exists
|
||||||
return component.props.value || '';
|
return component.props.value || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFieldInput = (component, event) => {
|
const handleFieldInput = async (component, event) => {
|
||||||
if (!props.isPreview) return;
|
if (!props.isPreview) return;
|
||||||
|
|
||||||
const fieldName = component.props.name;
|
const fieldName = component.props.name;
|
||||||
const newValue = event;
|
|
||||||
|
// Extract the actual value from the event based on field type
|
||||||
|
let newValue;
|
||||||
|
if (component.type === 'select') {
|
||||||
|
// For select fields, get the value from the event target or use the event itself if it's already a value
|
||||||
|
newValue = (event && event.target) ? event.target.value : event;
|
||||||
|
console.log(`[ComponentPreview] Select field '${fieldName}' input - raw event:`, event);
|
||||||
|
console.log(`[ComponentPreview] Select field '${fieldName}' extracted value:`, newValue);
|
||||||
|
|
||||||
|
// Wait for Vue's reactivity cycle to complete
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
// Try to get the current value from the form store after nextTick
|
||||||
|
const currentStoreValue = formStore.previewFormData[fieldName];
|
||||||
|
console.log(`[ComponentPreview] Select field '${fieldName}' store value after nextTick:`, currentStoreValue);
|
||||||
|
|
||||||
|
// Use the most recent value (either extracted or from store)
|
||||||
|
if (currentStoreValue && currentStoreValue !== newValue) {
|
||||||
|
console.log(`[ComponentPreview] Using store value instead:`, currentStoreValue);
|
||||||
|
newValue = currentStoreValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newValue = event;
|
||||||
|
}
|
||||||
|
|
||||||
// Simple update for regular fields only
|
// Simple update for regular fields only
|
||||||
const updatedData = { ...formStore.previewFormData, [fieldName]: newValue };
|
const updatedData = { ...formStore.previewFormData, [fieldName]: newValue };
|
||||||
@ -1611,30 +1639,34 @@ const handleFieldChange = (component, event) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to safely get field values for repeating groups without warnings
|
// Helper function to safely get field values for repeating groups without warnings
|
||||||
const getRepeatingGroupFieldValue = (group, fieldName, fieldType) => {
|
const getRepeatingGroupFieldValue = (group, fieldName, fieldType, component = null) => {
|
||||||
if (!group || typeof group !== 'object') {
|
if (!group || typeof group !== 'object') {
|
||||||
return getDefaultValueForType(fieldType);
|
return getDefaultValueForType(fieldType, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.hasOwnProperty(fieldName)) {
|
if (group.hasOwnProperty(fieldName)) {
|
||||||
const value = group[fieldName];
|
const value = group[fieldName];
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return getDefaultValueForType(fieldType);
|
return getDefaultValueForType(fieldType, component);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getDefaultValueForType(fieldType);
|
return getDefaultValueForType(fieldType, component);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to get default values based on field type
|
// Helper function to get default values based on field type
|
||||||
const getDefaultValueForType = (fieldType) => {
|
const getDefaultValueForType = (fieldType, component = null) => {
|
||||||
switch (fieldType) {
|
switch (fieldType) {
|
||||||
case 'number':
|
case 'number':
|
||||||
return 0;
|
return 0;
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return [];
|
return [];
|
||||||
case 'select':
|
case 'select':
|
||||||
|
// If component is provided and has options, use first option value
|
||||||
|
if (component && component.props && component.props.options && component.props.options.length > 0) {
|
||||||
|
return component.props.options[0].value || '';
|
||||||
|
}
|
||||||
return '';
|
return '';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
|
@ -163,15 +163,15 @@ const captureUrlParameters = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Load process data
|
// Load process data
|
||||||
const loadProcess = async () => {
|
const loadProcess = async (retryCount = 0) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
|
||||||
console.log('[Workflow] Loading process definition...');
|
console.log(`[Workflow] Loading process definition... (attempt ${retryCount + 1})`);
|
||||||
const response = await $fetch(`/api/process/${processId.value}`);
|
const response = await $fetch(`/api/process/${processId.value}`);
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success && response.process) {
|
||||||
process.value = response.process; // includes processDefinition
|
process.value = response.process; // includes processDefinition
|
||||||
|
|
||||||
// Check if process is published
|
// Check if process is published
|
||||||
@ -203,15 +203,31 @@ const loadProcess = async () => {
|
|||||||
await startProcessExecution();
|
await startProcessExecution();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
error.value = response.message || 'Failed to load process';
|
throw new Error(response.message || 'Invalid process response');
|
||||||
notifyParentOfError(error.value);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (fetchError) {
|
||||||
console.error('[Workflow] Error loading process:', err);
|
console.error(`[Workflow] Error loading process (attempt ${retryCount + 1}):`, fetchError);
|
||||||
error.value = 'Failed to load process data';
|
|
||||||
|
// Retry logic with exponential backoff
|
||||||
|
if (retryCount < 2) {
|
||||||
|
const delay = Math.pow(2, retryCount) * 1500; // 1.5s, 3s
|
||||||
|
console.log(`[Workflow] Retrying process load in ${delay}ms (attempt ${retryCount + 2}/3)`);
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
await loadProcess(retryCount + 1);
|
||||||
|
}, delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After all retries failed
|
||||||
|
error.value = `Failed to load process after ${retryCount + 1} attempts: ${fetchError.message || 'Network error'}`;
|
||||||
notifyParentOfError(error.value);
|
notifyParentOfError(error.value);
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
} finally {
|
||||||
|
// Only set loading to false if we're not retrying
|
||||||
|
if (retryCount >= 2 || process.value) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,6 +291,7 @@ function getNextNodeIdForDecision(currentNodeId) {
|
|||||||
const { conditions = [], defaultPath } = currentNodeObj.data || {};
|
const { conditions = [], defaultPath } = currentNodeObj.data || {};
|
||||||
console.log('[Gateway Debug] Conditions:', conditions);
|
console.log('[Gateway Debug] Conditions:', conditions);
|
||||||
console.log('[Gateway Debug] Default path:', defaultPath);
|
console.log('[Gateway Debug] Default path:', defaultPath);
|
||||||
|
console.log('[Gateway Debug] Current process variables:', processVariables.value);
|
||||||
|
|
||||||
// Evaluate condition groups (each group represents a path)
|
// Evaluate condition groups (each group represents a path)
|
||||||
for (const conditionGroup of conditions) {
|
for (const conditionGroup of conditions) {
|
||||||
@ -341,25 +358,46 @@ function evaluateConditionGroup(conditionGroup, variables) {
|
|||||||
const condition = conditionGroup.conditions[i];
|
const condition = conditionGroup.conditions[i];
|
||||||
const conditionResult = evaluateCondition(condition, variables);
|
const conditionResult = evaluateCondition(condition, variables);
|
||||||
const operator = condition.logicalOperator || 'and';
|
const operator = condition.logicalOperator || 'and';
|
||||||
|
|
||||||
|
console.log(`[Gateway Debug] Group '${conditionGroup.output}': evaluating condition ${i}, operator='${operator}', previous result=${result}, current condition result=${conditionResult}`);
|
||||||
|
|
||||||
if (operator === 'and') {
|
if (operator === 'and') {
|
||||||
result = result && conditionResult;
|
result = result && conditionResult;
|
||||||
} else if (operator === 'or') {
|
} else if (operator === 'or') {
|
||||||
result = result || conditionResult;
|
result = result || conditionResult;
|
||||||
}
|
}
|
||||||
console.log(`[Gateway Debug] Group '${conditionGroup.output}': after condition ${i}, operator='${operator}', conditionResult=`, conditionResult, ', groupResult=', result);
|
console.log(`[Gateway Debug] Group '${conditionGroup.output}': after condition ${i}, final result=`, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[Gateway Debug] Group '${conditionGroup.output}': FINAL GROUP RESULT=`, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Evaluate a single condition
|
// Helper: Evaluate a single condition
|
||||||
function evaluateCondition(condition, variables) {
|
function evaluateCondition(condition, variables) {
|
||||||
const { variable, operator, value, valueType, minValue, maxValue } = condition;
|
const { variable, operator, value, minValue, maxValue } = condition;
|
||||||
const variableValue = variables[variable];
|
const variableValue = variables[variable];
|
||||||
|
|
||||||
|
console.log(`[Gateway Debug] Evaluating condition: variable='${variable}', operator='${operator}', value='${value}', variableValue='${variableValue}'`);
|
||||||
|
|
||||||
|
// Handle undefined/null variables
|
||||||
|
if (variableValue === undefined || variableValue === null) {
|
||||||
|
console.warn(`[Gateway Debug] Variable '${variable}' is undefined/null for condition evaluation`);
|
||||||
|
|
||||||
|
// For empty/not_empty checks, handle undefined/null explicitly
|
||||||
|
if (operator === 'empty' || operator === 'is_empty') {
|
||||||
|
return true;
|
||||||
|
} else if (operator === 'not_empty' || operator === 'is_not_empty') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other operators, undefined/null values should generally fail comparisons
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle boolean type conversions
|
// Handle boolean type conversions
|
||||||
let compareValue = value;
|
let compareValue = value;
|
||||||
if (valueType === 'boolean') {
|
if (condition.valueType === 'boolean') {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
compareValue = value.toLowerCase() === 'true';
|
compareValue = value.toLowerCase() === 'true';
|
||||||
} else {
|
} else {
|
||||||
@ -372,12 +410,13 @@ function evaluateCondition(condition, variables) {
|
|||||||
case 'eq':
|
case 'eq':
|
||||||
case 'equals':
|
case 'equals':
|
||||||
case '==':
|
case '==':
|
||||||
result = variableValue == compareValue;
|
// Use strict equality for better type safety
|
||||||
|
result = String(variableValue) === String(compareValue);
|
||||||
break;
|
break;
|
||||||
case 'neq':
|
case 'neq':
|
||||||
case 'not_equals':
|
case 'not_equals':
|
||||||
case '!=':
|
case '!=':
|
||||||
result = variableValue != compareValue;
|
result = String(variableValue) !== String(compareValue);
|
||||||
break;
|
break;
|
||||||
case 'gt':
|
case 'gt':
|
||||||
case 'greater_than':
|
case 'greater_than':
|
||||||
@ -445,7 +484,16 @@ function evaluateCondition(condition, variables) {
|
|||||||
break;
|
break;
|
||||||
case 'not_empty':
|
case 'not_empty':
|
||||||
case 'is_not_empty':
|
case 'is_not_empty':
|
||||||
result = variableValue && variableValue !== '' && variableValue !== null && variableValue !== undefined;
|
// Check for meaningful values - handle strings, arrays, objects
|
||||||
|
if (typeof variableValue === 'string') {
|
||||||
|
result = variableValue.trim() !== '';
|
||||||
|
} else if (Array.isArray(variableValue)) {
|
||||||
|
result = variableValue.length > 0;
|
||||||
|
} else if (typeof variableValue === 'object' && variableValue !== null) {
|
||||||
|
result = Object.keys(variableValue).length > 0;
|
||||||
|
} else {
|
||||||
|
result = variableValue !== null && variableValue !== undefined && variableValue !== '';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'is_true':
|
case 'is_true':
|
||||||
result = Boolean(variableValue) === true;
|
result = Boolean(variableValue) === true;
|
||||||
@ -497,7 +545,7 @@ function evaluateCondition(condition, variables) {
|
|||||||
console.warn('[Workflow] Unknown condition operator:', operator);
|
console.warn('[Workflow] Unknown condition operator:', operator);
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
console.log(`[Gateway Debug] Condition: variable='${variable}', operator='${operator}', value=`, value, ', minValue=', minValue, ', maxValue=', maxValue, '| variableValue=', variableValue, '| result=', result);
|
console.log(`[Gateway Debug] Condition DETAILED: variable='${variable}', operator='${operator}', expectedValue='${value}', actualVariableValue='${variableValue}', types: expected=${typeof value}, actual=${typeof variableValue}, result=${result}`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,14 +692,50 @@ function applyInputMappings(nodeData, processVars, formData) {
|
|||||||
function applyOutputMappings(nodeData, formData, processVars) {
|
function applyOutputMappings(nodeData, formData, processVars) {
|
||||||
const { outputMappings = [] } = nodeData;
|
const { outputMappings = [] } = nodeData;
|
||||||
console.log('[Workflow] Applying output mappings:', outputMappings);
|
console.log('[Workflow] Applying output mappings:', outputMappings);
|
||||||
|
console.log('[Workflow] Form data available for mapping:', Object.keys(formData));
|
||||||
|
|
||||||
outputMappings.forEach(mapping => {
|
outputMappings.forEach(mapping => {
|
||||||
const { formField, processVariable } = mapping;
|
const { formField, processVariable } = mapping;
|
||||||
if (formData[formField] !== undefined) {
|
|
||||||
processVars[processVariable] = formData[formField];
|
// Validate that the form field exists in the form data
|
||||||
console.log(`[Workflow] Mapped ${formField} -> ${processVariable}:`, formData[formField]);
|
if (!(formField in formData)) {
|
||||||
|
console.warn(`[Workflow] Form field '${formField}' not found in form data for mapping to '${processVariable}'`);
|
||||||
|
console.warn(`[Workflow] Available form fields:`, Object.keys(formData));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fieldValue = formData[formField];
|
||||||
|
|
||||||
|
// Handle undefined, null, and empty values
|
||||||
|
if (fieldValue === undefined) {
|
||||||
|
console.warn(`[Workflow] Form field '${formField}' is undefined, skipping mapping to '${processVariable}'`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the value with proper type handling
|
||||||
|
let mappedValue = fieldValue;
|
||||||
|
|
||||||
|
// Special handling for common field types
|
||||||
|
if (typeof fieldValue === 'string') {
|
||||||
|
mappedValue = fieldValue.trim(); // Remove whitespace
|
||||||
|
} else if (Array.isArray(fieldValue)) {
|
||||||
|
// For multi-select fields, join values or take first value based on expected variable type
|
||||||
|
const variableSource = process.value?.variables || process.value?.processVariables;
|
||||||
|
const variableConfig = variableSource?.[processVariable];
|
||||||
|
|
||||||
|
if (variableConfig?.type === 'string' && fieldValue.length > 0) {
|
||||||
|
mappedValue = fieldValue[0]; // Take first selected value for string variables
|
||||||
|
} else {
|
||||||
|
mappedValue = fieldValue; // Keep as array for other types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the process variable
|
||||||
|
processVars[processVariable] = mappedValue;
|
||||||
|
console.log(`[Workflow] Successfully mapped '${formField}' (${typeof fieldValue}) -> '${processVariable}':`, mappedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('[Workflow] Process variables after output mapping:', processVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply button mappings to assign button click data to process variables
|
// Apply button mappings to assign button click data to process variables
|
||||||
@ -676,6 +760,7 @@ function applyButtonMappings(nodeData, buttonEvent, processVars) {
|
|||||||
break;
|
break;
|
||||||
case 'custom':
|
case 'custom':
|
||||||
valueToAssign = customValue || '';
|
valueToAssign = customValue || '';
|
||||||
|
console.log(`[Workflow] Button custom value assignment: customValue=${customValue} (type: ${typeof customValue}), valueToAssign=${valueToAssign} (type: ${typeof valueToAssign})`);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
valueToAssign = true;
|
valueToAssign = true;
|
||||||
@ -1067,17 +1152,38 @@ const getStepInfo = (node) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Load form data from database
|
// Load form data from database
|
||||||
const loadFormData = async (formId) => {
|
const loadFormData = async (formId, retryCount = 0) => {
|
||||||
try {
|
try {
|
||||||
if (!formId) return null;
|
if (!formId) {
|
||||||
|
console.warn('[Workflow] No form ID provided to loadFormData');
|
||||||
const response = await $fetch(`/api/forms/${formId}`);
|
return null;
|
||||||
if (response.success) {
|
|
||||||
return response.form;
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
} catch (error) {
|
console.log(`[Workflow] Loading form data for ID: ${formId} (attempt ${retryCount + 1})`);
|
||||||
console.error('Error loading form:', error);
|
const response = await $fetch(`/api/forms/${formId}`);
|
||||||
|
|
||||||
|
if (response.success && response.form) {
|
||||||
|
console.log(`[Workflow] Form data loaded successfully for ID: ${formId}`);
|
||||||
|
return response.form;
|
||||||
|
} else {
|
||||||
|
console.warn(`[Workflow] Form load failed - invalid response for ID: ${formId}`, response);
|
||||||
|
throw new Error(`Invalid form response: ${response.message || 'Unknown error'}`);
|
||||||
|
}
|
||||||
|
} catch (fetchError) {
|
||||||
|
console.error(`[Workflow] Error loading form ${formId}:`, fetchError);
|
||||||
|
|
||||||
|
// Retry logic with exponential backoff
|
||||||
|
if (retryCount < 3) {
|
||||||
|
const delay = Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s
|
||||||
|
console.log(`[Workflow] Retrying form load in ${delay}ms (attempt ${retryCount + 2}/4)`);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
return loadFormData(formId, retryCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// After all retries failed, set error state and return null
|
||||||
|
error.value = `Failed to load form after ${retryCount + 1} attempts: ${fetchError.message || 'Network error'}`;
|
||||||
|
console.error(`[Workflow] Form loading completely failed for ID: ${formId} after ${retryCount + 1} attempts`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1101,10 +1207,18 @@ const handleScriptFieldChange = ({ fieldName, value }) => {
|
|||||||
|
|
||||||
// Handle form data updates from ComponentPreview
|
// Handle form data updates from ComponentPreview
|
||||||
const handleFormDataUpdate = (updatedData) => {
|
const handleFormDataUpdate = (updatedData) => {
|
||||||
// Update the form data with the new data
|
console.log('[Workflow] Form data update from ComponentPreview:', updatedData);
|
||||||
|
|
||||||
|
// Log the current formData state before update
|
||||||
|
console.log('[Workflow] Current formData before update:', formData.value);
|
||||||
|
|
||||||
|
// Update the form data with the new data (simple merge)
|
||||||
formData.value = { ...formData.value, ...updatedData };
|
formData.value = { ...formData.value, ...updatedData };
|
||||||
// Also update form store to keep them in sync
|
|
||||||
formStore.updatePreviewFormData(formData.value);
|
// Log the new formData state after update
|
||||||
|
console.log('[Workflow] FormData after update:', formData.value);
|
||||||
|
|
||||||
|
// Don't update form store here - let the watcher handle it to avoid double updates
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle button click events and apply button mappings
|
// Handle button click events and apply button mappings
|
||||||
@ -1114,8 +1228,16 @@ const handleButtonClick = (buttonEvent) => {
|
|||||||
// Apply button mappings if configured
|
// Apply button mappings if configured
|
||||||
if (currentNode.value?.data?.buttonMappings?.length > 0) {
|
if (currentNode.value?.data?.buttonMappings?.length > 0) {
|
||||||
applyButtonMappings(currentNode.value.data, buttonEvent, processVariables.value);
|
applyButtonMappings(currentNode.value.data, buttonEvent, processVariables.value);
|
||||||
|
}
|
||||||
// Check if any button mapping has navigation action
|
|
||||||
|
// Apply output mappings to capture current form data before navigation
|
||||||
|
if (currentNode.value?.data?.outputMappings?.length > 0) {
|
||||||
|
console.log('[Workflow] Applying output mappings before button navigation');
|
||||||
|
applyOutputMappings(currentNode.value.data, formData.value, processVariables.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any button mapping has navigation action
|
||||||
|
if (currentNode.value?.data?.buttonMappings?.length > 0) {
|
||||||
const buttonMapping = currentNode.value.data.buttonMappings.find(
|
const buttonMapping = currentNode.value.data.buttonMappings.find(
|
||||||
mapping => mapping.formButton === buttonEvent.buttonName && mapping.navigationAction === 'continue'
|
mapping => mapping.formButton === buttonEvent.buttonName && mapping.navigationAction === 'continue'
|
||||||
);
|
);
|
||||||
@ -1306,6 +1428,13 @@ watch(currentStep, async (newStep) => {
|
|||||||
const formId = currentNode.value.data?.formId;
|
const formId = currentNode.value.data?.formId;
|
||||||
if (formId) {
|
if (formId) {
|
||||||
currentForm.value = await loadFormData(formId);
|
currentForm.value = await loadFormData(formId);
|
||||||
|
|
||||||
|
// Check if form loading failed
|
||||||
|
if (!currentForm.value) {
|
||||||
|
console.error(`[Workflow] Form loading failed for ID: ${formId}, showing error state`);
|
||||||
|
stepLoading.value = false;
|
||||||
|
return; // Don't proceed with form setup if loading failed
|
||||||
|
}
|
||||||
// Apply input mappings to pre-fill form
|
// Apply input mappings to pre-fill form
|
||||||
if (currentNode.value.data?.inputMappings) {
|
if (currentNode.value.data?.inputMappings) {
|
||||||
applyInputMappings(
|
applyInputMappings(
|
||||||
@ -1327,6 +1456,19 @@ watch(currentStep, async (newStep) => {
|
|||||||
|
|
||||||
// Initialize repeating groups and tables in form data
|
// Initialize repeating groups and tables in form data
|
||||||
const updatedFormData = { ...formData.value };
|
const updatedFormData = { ...formData.value };
|
||||||
|
|
||||||
|
// Initialize all form fields with proper default values
|
||||||
|
currentForm.value.formComponents.forEach(component => {
|
||||||
|
const fieldName = component.props?.name;
|
||||||
|
|
||||||
|
// Initialize select fields with first option if not already set
|
||||||
|
if (component.type === 'select' && fieldName && !updatedFormData.hasOwnProperty(fieldName)) {
|
||||||
|
if (component.props.options && component.props.options.length > 0) {
|
||||||
|
updatedFormData[fieldName] = component.props.options[0].value || '';
|
||||||
|
console.log(`[Workflow] Initialized select field '${fieldName}' with default value:`, updatedFormData[fieldName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
currentForm.value.formComponents.forEach(component => {
|
currentForm.value.formComponents.forEach(component => {
|
||||||
if (component.type === 'repeating-group' && component.props?.name) {
|
if (component.type === 'repeating-group' && component.props?.name) {
|
||||||
const groupName = component.props.name;
|
const groupName = component.props.name;
|
||||||
@ -1448,6 +1590,7 @@ const onFormKitSubmit = () => {
|
|||||||
handleFormSubmit();
|
handleFormSubmit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// New: validate and submit handler for button
|
// New: validate and submit handler for button
|
||||||
const validateAndSubmit = () => {
|
const validateAndSubmit = () => {
|
||||||
if (formRef.value && formRef.value.node && typeof formRef.value.node.submit === 'function') {
|
if (formRef.value && formRef.value.node && typeof formRef.value.node.submit === 'function') {
|
||||||
@ -1861,12 +2004,18 @@ const getWorkflowSubmitButtonStyle = () => {
|
|||||||
<!-- Error State -->
|
<!-- Error State -->
|
||||||
<div v-else-if="error" :class="isIframeMode ? 'p-6 text-center' : 'bg-white rounded-xl shadow-sm border border-gray-200 p-12 text-center'">
|
<div v-else-if="error" :class="isIframeMode ? 'p-6 text-center' : 'bg-white rounded-xl shadow-sm border border-gray-200 p-12 text-center'">
|
||||||
<Icon name="material-symbols:error-outline" class="w-16 h-16 text-red-400 mx-auto mb-4" />
|
<Icon name="material-symbols:error-outline" class="w-16 h-16 text-red-400 mx-auto mb-4" />
|
||||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Error</h3>
|
<h3 class="text-lg font-medium text-gray-900 mb-2">Process Error</h3>
|
||||||
<p class="text-gray-600 mb-6">{{ error }}</p>
|
<p class="text-gray-600 mb-6">{{ error }}</p>
|
||||||
<RsButton v-if="!isIframeMode" @click="goHome" variant="primary">
|
<div class="flex justify-center gap-3">
|
||||||
<Icon name="material-symbols:home" class="mr-2" />
|
<RsButton @click="() => loadProcess(0)" variant="secondary">
|
||||||
Go Home
|
<Icon name="material-symbols:refresh" class="mr-2" />
|
||||||
</RsButton>
|
Try Again
|
||||||
|
</RsButton>
|
||||||
|
<RsButton v-if="!isIframeMode" @click="goHome" variant="primary">
|
||||||
|
<Icon name="material-symbols:home" class="mr-2" />
|
||||||
|
Go Home
|
||||||
|
</RsButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Process Complete - Hidden when hideComplete=true -->
|
<!-- Process Complete - Hidden when hideComplete=true -->
|
||||||
@ -1877,7 +2026,7 @@ const getWorkflowSubmitButtonStyle = () => {
|
|||||||
The workflow "{{ process.processName }}" has been completed successfully.
|
The workflow "{{ process.processName }}" has been completed successfully.
|
||||||
</p>
|
</p>
|
||||||
<div v-if="!isIframeMode" class="flex justify-center gap-3">
|
<div v-if="!isIframeMode" class="flex justify-center gap-3">
|
||||||
<RsButton @click="loadProcess" variant="secondary">
|
<RsButton @click="() => loadProcess(0)" variant="secondary">
|
||||||
<Icon name="material-symbols:refresh" class="mr-2" />
|
<Icon name="material-symbols:refresh" class="mr-2" />
|
||||||
Run Again
|
Run Again
|
||||||
</RsButton>
|
</RsButton>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user