From 8f84b00a9e61c8f841115a94da749af81b0d7d23 Mon Sep 17 00:00:00 2001 From: Afiq Date: Mon, 4 Aug 2025 16:25:56 +0800 Subject: [PATCH] Enhance Form Builder Submit Button Configuration and Variable Handling - Introduced a new submit button configuration in the form builder, allowing users to enable/disable the default submit button and customize its label, category, and color. - Updated VariableBrowser.vue to support object property path input for variables, including validation and error handling for property paths. - Enhanced ApiNodeConfiguration.vue to prevent object path creation for output and error variables. - Improved workflow page to respect form builder submit button settings, ensuring consistent behavior across the application. - Added helper functions for managing submit button styles and variants, enhancing the overall user experience. --- .../process-flow/ApiNodeConfiguration.vue | 2 + components/process-flow/VariableBrowser.vue | 155 +++++++++++++++++- pages/form-builder/index.vue | 155 +++++++++++++++++- pages/workflow/[id].vue | 47 +++++- prisma/json/json-schema.json | 20 +++ prisma/schema.prisma | 2 + server/api/forms/[id].put.js | 5 + server/api/forms/create.post.js | 3 +- stores/formBuilder.js | 15 ++ 9 files changed, 391 insertions(+), 13 deletions(-) diff --git a/components/process-flow/ApiNodeConfiguration.vue b/components/process-flow/ApiNodeConfiguration.vue index c5214a8..4de51a3 100644 --- a/components/process-flow/ApiNodeConfiguration.vue +++ b/components/process-flow/ApiNodeConfiguration.vue @@ -172,6 +172,7 @@ v-model="localNodeData.outputVariable" :availableVariables="availableVariables" :allowCreate="true" + :allowObjectPath="false" @change="saveChanges" /> @@ -188,6 +189,7 @@ v-model="localNodeData.errorVariable" :availableVariables="availableVariables" :allowCreate="true" + :allowObjectPath="false" @change="saveChanges" /> diff --git a/components/process-flow/VariableBrowser.vue b/components/process-flow/VariableBrowser.vue index 41d9aa3..0903148 100644 --- a/components/process-flow/VariableBrowser.vue +++ b/components/process-flow/VariableBrowser.vue @@ -4,7 +4,7 @@
+
+
+ + + + +
+
+ Examples +
+
data.user.name - Access nested object property
+
items[0].title - Access first array item property
+
results.users[2].email - Complex nested access
+
response.data.attributes.value - Deep nesting
+
+
+
+ + +
+ + {{ propertyPathError }} +
+ + +
+
Full Variable Path:
+ {{ finalVariablePath }} +
+ +
@@ -224,6 +281,10 @@ const props = defineProps({ required: { type: Boolean, default: false + }, + allowObjectPath: { + type: Boolean, + default: true // Enable object property path input } }); @@ -239,14 +300,32 @@ const newVariableDefaultValue = ref(''); const newVariableDescription = ref(''); const nameValidationError = ref(''); +// Object property path state +const propertyPath = ref(''); +const propertyPathError = ref(''); + // Computed properties +const baseVariableName = computed(() => { + // Extract base variable name from modelValue (strip property path) + const value = props.modelValue || ''; + const dotIndex = value.indexOf('.'); + return dotIndex > -1 ? value.substring(0, dotIndex) : value; +}); + const selectedVariable = computed(() => { - return props.availableVariables.find(v => v.name === props.modelValue); + return props.availableVariables.find(v => v.name === baseVariableName.value); +}); + +const finalVariablePath = computed(() => { + if (!baseVariableName.value) return ''; + if (!propertyPath.value) return baseVariableName.value; + return `${baseVariableName.value}.${propertyPath.value}`; }); const hasError = computed(() => { if (!props.required && !props.modelValue) return false; if (props.modelValue && !selectedVariable.value) return true; + if (propertyPathError.value) return true; return false; }); @@ -255,7 +334,10 @@ const errorMessage = computed(() => { return 'Variable selection is required'; } if (props.modelValue && !selectedVariable.value) { - return `Variable "${props.modelValue}" not found`; + return `Variable "${baseVariableName.value}" not found`; + } + if (propertyPathError.value) { + return propertyPathError.value; } return ''; }); @@ -305,9 +387,74 @@ const canCreateVariable = computed(() => { // Methods const handleVariableSelect = (event) => { - emit('update:modelValue', event.target.value); + const selectedVar = event.target.value; + propertyPath.value = ''; // Reset property path when changing variable + propertyPathError.value = ''; + emit('update:modelValue', selectedVar); }; +const handlePropertyPathChange = () => { + propertyPathError.value = ''; // Clear error on input + emit('update:modelValue', finalVariablePath.value); +}; + +const validatePropertyPath = () => { + if (!propertyPath.value) { + propertyPathError.value = ''; + return; + } + + // Basic validation for property path syntax + const path = propertyPath.value.trim(); + + // Check for invalid characters or patterns + if (path.includes('..') || path.startsWith('.') || path.endsWith('.')) { + propertyPathError.value = 'Invalid property path format'; + return; + } + + // Check for balanced brackets + const openBrackets = (path.match(/\[/g) || []).length; + const closeBrackets = (path.match(/\]/g) || []).length; + if (openBrackets !== closeBrackets) { + propertyPathError.value = 'Unmatched brackets in property path'; + return; + } + + propertyPathError.value = ''; +}; + +const clearPropertyPath = () => { + propertyPath.value = ''; + propertyPathError.value = ''; + emit('update:modelValue', baseVariableName.value); +}; + +const isObjectType = (type) => { + return ['object', 'array', 'map', 'set'].includes(type); +}; + +// Watch for external changes to modelValue to sync property path +watch(() => props.modelValue, (newValue) => { + if (!newValue) { + propertyPath.value = ''; + propertyPathError.value = ''; + return; + } + + const dotIndex = newValue.indexOf('.'); + if (dotIndex > -1) { + // modelValue contains a property path + const baseName = newValue.substring(0, dotIndex); + const path = newValue.substring(dotIndex + 1); + propertyPath.value = path; + } else { + // No property path in modelValue + propertyPath.value = ''; + } + propertyPathError.value = ''; +}, { immediate: true }); + const openCreateVariable = () => { showCreateVariable.value = true; resetCreateForm(); diff --git a/pages/form-builder/index.vue b/pages/form-builder/index.vue index 11a3cf1..6565a6e 100644 --- a/pages/form-builder/index.vue +++ b/pages/form-builder/index.vue @@ -295,16 +295,20 @@
- +
@@ -576,6 +580,102 @@ + + +