Optimize Conditional Logic Handling and Introduce Demo Component

- Refactored ConditionalLogicEngine.vue to optimize conditional logic script generation by grouping handlers for watched fields, reducing duplicate event listeners and improving performance.
- Added helper functions for generating condition checks and action codes, enhancing code readability and maintainability.
- Introduced ConditionalLogicTestDemo.vue to demonstrate the benefits of optimization, showcasing before and after examples of conditional logic handling.
- Updated FormBuilderFieldSettingsModal.vue to include notes on optimization when multiple fields watch the same trigger field, improving user awareness of performance enhancements.
- Enhanced ComponentPreview.vue and workflow pages to support preview mode for conditional logic, ensuring consistent behavior across the application.
This commit is contained in:
Afiq 2025-08-06 18:31:56 +08:00
parent 8a6f87ebf1
commit 577128a799
7 changed files with 335 additions and 141 deletions

View File

@ -26,8 +26,8 @@
<FormKit v-else-if="isInputType" :id="`preview-${component.id}`" :type="component.type" :name="component.props.name" <FormKit v-else-if="isInputType" :id="`preview-${component.id}`" :type="component.type" :name="component.props.name"
:label="component.props.label" :help="component.props.help" :placeholder="component.props.placeholder" :label="component.props.label" :help="component.props.help" :placeholder="component.props.placeholder"
:validation="component.props.validation" :validation-visibility="isPreview ? 'live' : 'blur'" :validation="component.props.validation" :validation-visibility="isPreview ? 'live' : 'blur'"
:readonly="component.props.readonly || isPreview" :readonly="component.props.readonly || !isPreview"
:disabled="isPreview || (component.props.readonly && ['select', 'checkbox', 'radio'].includes(component.type))" :disabled="!isPreview || (component.props.readonly && ['select', 'checkbox', 'radio'].includes(component.type))"
:options="component.props.options || undefined" :value="component.props.value || undefined" :options="component.props.options || undefined" :value="component.props.value || undefined"
:accept="component.props.accept || undefined" :max="component.props.max || undefined" :accept="component.props.accept || undefined" :max="component.props.max || undefined"
:mask="component.props.mask || undefined" :digits="component.props.digits || undefined" :mask="component.props.mask || undefined" :digits="component.props.digits || undefined"
@ -144,7 +144,7 @@
</div> </div>
<!-- Default group preview (in edit mode) --> <!-- Default group preview (in edit mode) -->
<div v-if="isPreview" class="repeating-groups space-y-4"> <div v-if="!isPreview" class="repeating-groups space-y-4">
<div class="group-item border border-gray-200 rounded-md p-3 bg-gray-50"> <div class="group-item border border-gray-200 rounded-md p-3 bg-gray-50">
<div class="flex justify-between items-center mb-3"> <div class="flex justify-between items-center mb-3">
<h4 class="text-sm font-medium text-gray-700">Item 1</h4> <h4 class="text-sm font-medium text-gray-700">Item 1</h4>
@ -168,7 +168,7 @@
</button> </button>
</div> </div>
<!-- Functional groups (in form view) --> <!-- Functional groups (in preview mode) -->
<div v-else class="repeating-groups space-y-4"> <div v-else class="repeating-groups space-y-4">
<div v-for="(group, groupIndex) in (safeGetField(component.props.name, previewFormData) || [])" :key="groupIndex" <div v-for="(group, groupIndex) in (safeGetField(component.props.name, previewFormData) || [])" :key="groupIndex"
class="group-item border border-gray-200 rounded-md p-3 bg-gray-50"> class="group-item border border-gray-200 rounded-md p-3 bg-gray-50">
@ -250,7 +250,7 @@
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<div v-if="isPreview" class="list-items space-y-2"> <div v-if="!isPreview" class="list-items space-y-2">
<div v-for="(item, index) in component.props.defaultItems" :key="index" class="flex items-center"> <div v-for="(item, index) in component.props.defaultItems" :key="index" class="flex items-center">
<input :type="component.props.itemType || 'text'" :value="item" :placeholder="component.props.placeholder" <input :type="component.props.itemType || 'text'" :value="item" :placeholder="component.props.placeholder"
disabled disabled
@ -263,7 +263,7 @@
</button> </button>
</div> </div>
<!-- Add button for preview --> <!-- Add button for edit mode -->
<button type="button" <button type="button"
class="inline-flex items-center px-3 py-1.5 border border-blue-600 text-blue-600 bg-white hover:bg-blue-50 rounded text-sm"> class="inline-flex items-center px-3 py-1.5 border border-blue-600 text-blue-600 bg-white hover:bg-blue-50 rounded text-sm">
<Icon name="material-symbols:add-circle-outline" class="w-4 h-4 mr-1" /> <Icon name="material-symbols:add-circle-outline" class="w-4 h-4 mr-1" />
@ -464,7 +464,12 @@
<component-preview :component="childComponent" :is-preview="true" /> <component-preview :component="childComponent" :is-preview="true" />
</template> </template>
</div> </div>
<!-- No placeholder in preview mode when empty --> <!-- Show subtle indication for empty sections in preview mode (optional) -->
<div v-else-if="component.props.showEmptyMessage !== false" class="empty-section-preview">
<p class="text-sm text-gray-400 text-center py-4">
{{ component.props.emptyMessage || 'This section is empty' }}
</p>
</div>
</div> </div>
<!-- In form builder mode, show drop zone --> <!-- In form builder mode, show drop zone -->
@ -728,7 +733,7 @@ watch(() => props.component.props.minItems, (newMinItems, oldMinItems) => {
// Repeating group and dynamic list functionality // Repeating group and dynamic list functionality
const addGroupItem = () => { const addGroupItem = () => {
if (props.isPreview) return; if (!props.isPreview) return;
const groupName = props.component.props.name; const groupName = props.component.props.name;
if (!groupName) return; if (!groupName) return;
@ -758,7 +763,7 @@ const addGroupItem = () => {
}; };
const removeGroupItem = (index) => { const removeGroupItem = (index) => {
if (props.isPreview) return; if (!props.isPreview) return;
const groupName = props.component.props.name; const groupName = props.component.props.name;
if (!groupName) return; if (!groupName) return;
@ -785,7 +790,7 @@ const removeGroupItem = (index) => {
}; };
const addListItem = () => { const addListItem = () => {
if (props.isPreview) return; if (!props.isPreview) return;
const listName = props.component.props.name; const listName = props.component.props.name;
if (!listName) return; if (!listName) return;
@ -802,7 +807,7 @@ const addListItem = () => {
}; };
const removeListItem = (index) => { const removeListItem = (index) => {
if (props.isPreview) return; if (!props.isPreview) return;
const listName = props.component.props.name; const listName = props.component.props.name;
if (!listName) return; if (!listName) return;
@ -1060,7 +1065,7 @@ const showImportModal = (listName) => {
// Handle button click // Handle button click
const handleButtonClick = () => { const handleButtonClick = () => {
if (props.isPreview) return; if (!props.isPreview) return;
// Execute custom onClick code if provided // Execute custom onClick code if provided
if (props.component.props.onClick) { if (props.component.props.onClick) {
@ -1159,12 +1164,7 @@ const handleImageClick = (event) => {
// Compute style based on grid properties // Compute style based on grid properties
const componentStyle = computed(() => { const componentStyle = computed(() => {
// Only apply grid styles in the non-preview mode (actual form) // Apply grid column styling in both preview and edit modes
if (props.isPreview) {
return {}; // Styling is handled by parent in canvas mode
}
// Apply grid column in preview mode
const gridColumn = props.component.props.gridColumn || 'span 12'; const gridColumn = props.component.props.gridColumn || 'span 12';
return { return {
@ -1634,6 +1634,12 @@ const getButtonSizeClass = (size) => {
padding: 1rem; padding: 1rem;
} }
.empty-section-preview {
border: 1px dashed #e5e7eb;
border-radius: 0.375rem;
background-color: #fafafa;
}
.section-drop-zone { .section-drop-zone {
padding: 1rem; padding: 1rem;
min-height: 120px; min-height: 120px;

View File

@ -41,22 +41,11 @@ const getJavaScriptOperator = (operator) => {
// Generate conditional logic script from form components // Generate conditional logic script from form components
const generateConditionalLogicScript = () => { const generateConditionalLogicScript = () => {
const scriptsArray = [] const scriptsArray = []
const fieldWatchers = new Map() // Group by watched field to avoid duplicates
const initialEvaluations = []
// Process each component that has conditional logic enabled // Helper function to generate condition check
props.formComponents.forEach(component => { const generateConditionCheck = (condition) => {
const conditionalLogic = component.props.conditionalLogic
if (!conditionalLogic || !conditionalLogic.enabled || !conditionalLogic.conditions.length) {
return
}
const { conditions, action, operator } = conditionalLogic
const fieldName = component.props.name
if (!fieldName) return
// Generate condition checks
const conditionChecks = conditions.map(condition => {
const { field, operator: condOp, value } = condition const { field, operator: condOp, value } = condition
switch (condOp) { switch (condOp) {
@ -79,49 +68,118 @@ const generateConditionalLogicScript = () => {
default: default:
return `getField('${field}') === '${value}'` return `getField('${field}') === '${value}'`
} }
}).join(` ${getJavaScriptOperator(operator)} `) }
// Helper function to generate action code
const generateActionCode = (action, fieldName) => {
switch (action) {
case 'show': return `showField('${fieldName}')`
case 'hide': return `hideField('${fieldName}')`
case 'enable': return `enableField('${fieldName}')`
case 'disable': return `disableField('${fieldName}')`
default: return `showField('${fieldName}')`
}
}
// Helper function to generate inverse action code
const generateInverseActionCode = (action, fieldName) => {
switch (action) {
case 'show': return `hideField('${fieldName}')`
case 'hide': return `showField('${fieldName}')`
case 'enable': return `disableField('${fieldName}')`
case 'disable': return `enableField('${fieldName}')`
default: return `hideField('${fieldName}')`
}
}
// Process each component that has conditional logic enabled
props.formComponents.forEach(component => {
const conditionalLogic = component.props.conditionalLogic
if (!conditionalLogic || !conditionalLogic.enabled || !conditionalLogic.conditions.length) {
return
}
const { conditions, action, operator } = conditionalLogic
const fieldName = component.props.name
if (!fieldName) return
// Generate condition checks
const conditionChecks = conditions.map(generateConditionCheck).join(` ${getJavaScriptOperator(operator)} `)
// Generate action functions // Generate action functions
const actionCode = action === 'show' ? `showField('${fieldName}')` : const actionCode = generateActionCode(action, fieldName)
action === 'hide' ? `hideField('${fieldName}')` : const inverseActionCode = generateInverseActionCode(action, fieldName)
action === 'enable' ? `enableField('${fieldName}')` :
action === 'disable' ? `disableField('${fieldName}')` :
`showField('${fieldName}')`
const inverseActionCode = action === 'show' ? `hideField('${fieldName}')` : // Get all watched fields for this component
action === 'hide' ? `showField('${fieldName}')` :
action === 'enable' ? `disableField('${fieldName}')` :
action === 'disable' ? `enableField('${fieldName}')` :
`hideField('${fieldName}')`
// Generate field change listeners
const watchedFields = [...new Set(conditions.map(c => c.field).filter(Boolean))] const watchedFields = [...new Set(conditions.map(c => c.field).filter(Boolean))]
// Group logic by watched field to avoid duplicates
watchedFields.forEach(watchField => { watchedFields.forEach(watchField => {
const script = ` if (!fieldWatchers.has(watchField)) {
// Conditional logic for field: ${fieldName} fieldWatchers.set(watchField, {
onFieldChange('${watchField}', function() { fieldName: watchField,
if (${conditionChecks}) { logicBlocks: []
${actionCode}; })
} else {
${inverseActionCode};
} }
// Add this component's logic to the watched field
fieldWatchers.get(watchField).logicBlocks.push({
targetField: fieldName,
conditionChecks,
actionCode,
inverseActionCode,
componentType: component.type
})
})
// Add initial evaluation
initialEvaluations.push({
targetField: fieldName,
conditionChecks,
actionCode,
inverseActionCode,
componentType: component.type
})
})
// Generate optimized field change handlers (one per watched field)
fieldWatchers.forEach((watcher, watchField) => {
const logicBlocks = watcher.logicBlocks.map(block => `
// Logic for ${block.componentType} field: ${block.targetField}
if (${block.conditionChecks}) {
${block.actionCode};
} else {
${block.inverseActionCode};
}`).join('')
const script = `
// Optimized field change handler for: ${watchField}
// Handles ${watcher.logicBlocks.length} dependent field(s): ${watcher.logicBlocks.map(b => b.targetField).join(', ')}
onFieldChange('${watchField}', function() {${logicBlocks}
});` });`
scriptsArray.push(script) scriptsArray.push(script)
}) })
// Also add initial evaluation // Generate initial evaluations
const initialScript = ` if (initialEvaluations.length > 0) {
// Initial evaluation for field: ${fieldName} const initialLogicBlocks = initialEvaluations.map(block => `
(function() { // Initial evaluation for ${block.componentType} field: ${block.targetField}
if (${conditionChecks}) { if (${block.conditionChecks}) {
${actionCode}; ${block.actionCode};
} else { } else {
${inverseActionCode}; ${block.inverseActionCode};
} }`).join('')
const initialScript = `
// Initial conditional logic evaluation
(function() {${initialLogicBlocks}
})();` })();`
scriptsArray.push(initialScript) scriptsArray.push(initialScript)
}) }
return scriptsArray.join('\n\n') return scriptsArray.join('\n\n')
} }

View File

@ -0,0 +1,108 @@
<template>
<div class="conditional-logic-demo p-6 bg-gray-50 rounded-lg">
<h3 class="text-lg font-semibold mb-4">Conditional Logic Optimization Demo</h3>
<!-- Before Optimization Example -->
<div class="mb-6">
<h4 class="text-md font-medium text-red-600 mb-2"> Before Optimization (Duplicate Handlers)</h4>
<div class="bg-red-50 p-4 rounded border border-red-200">
<pre class="text-sm text-red-800 whitespace-pre-wrap">{{ beforeOptimization }}</pre>
</div>
</div>
<!-- After Optimization Example -->
<div class="mb-6">
<h4 class="text-md font-medium text-green-600 mb-2"> After Optimization (Grouped Handlers)</h4>
<div class="bg-green-50 p-4 rounded border border-green-200">
<pre class="text-sm text-green-800 whitespace-pre-wrap">{{ afterOptimization }}</pre>
</div>
</div>
<!-- Benefits -->
<div class="bg-blue-50 p-4 rounded border border-blue-200">
<h4 class="text-md font-medium text-blue-800 mb-2">🚀 Benefits of Optimization</h4>
<ul class="text-sm text-blue-700 space-y-1">
<li> <strong>Reduced Memory Usage:</strong> Only one handler per watched field instead of multiple duplicates</li>
<li> <strong>Better Performance:</strong> Fewer function calls when field values change</li>
<li> <strong>Cleaner Code:</strong> Grouped logic is easier to read and maintain</li>
<li> <strong>No Conflicts:</strong> Eliminates potential conflicts between duplicate handlers</li>
</ul>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
// Example scenario: 3 fields (field2, field3, field4) all depend on field1
const beforeOptimization = computed(() => `// BEFORE: Multiple duplicate handlers for the same field
// This creates performance issues and potential conflicts
// Conditional logic for field: field2
onFieldChange('field1', function() {
if (getField('field1') === 'show') {
showField('field2');
} else {
hideField('field2');
}
});
// Conditional logic for field: field3
onFieldChange('field1', function() {
if (getField('field1') === 'show') {
showField('field3');
} else {
hideField('field3');
}
});
// Conditional logic for field: field4
onFieldChange('field1', function() {
if (getField('field1') === 'show') {
showField('field4');
} else {
hideField('field4');
}
});
// Problem: 3 duplicate handlers listening to the same field!`)
const afterOptimization = computed(() => `// AFTER: Single optimized handler for all dependent fields
// Much more efficient and prevents conflicts
// Optimized field change handler for: field1
// Handles 3 dependent field(s): field2, field3, field4
onFieldChange('field1', function() {
// Logic for text field: field2
if (getField('field1') === 'show') {
showField('field2');
} else {
hideField('field2');
}
// Logic for text field: field3
if (getField('field1') === 'show') {
showField('field3');
} else {
hideField('field3');
}
// Logic for text field: field4
if (getField('field1') === 'show') {
showField('field4');
} else {
hideField('field4');
}
});
// Solution: Only 1 handler managing all dependent fields!`)
</script>
<style scoped>
pre {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 12px;
line-height: 1.4;
overflow-x: auto;
}
</style>

View File

@ -2787,8 +2787,22 @@ const generateConditionalLogicCode = () => {
const { conditions, action, operator } = configModel.value.conditionalLogic const { conditions, action, operator } = configModel.value.conditionalLogic
const fieldName = configModel.value.name || 'this_field' const fieldName = configModel.value.name || 'this_field'
// Generate condition checks // Helper function to map operators to valid JavaScript
const conditionChecks = conditions.map(condition => { const getJavaScriptOperator = (operator) => {
switch (operator) {
case 'and':
case 'AND':
return '&&'
case 'or':
case 'OR':
return '||'
default:
return '&&' // Default to AND if unknown operator
}
}
// Helper function to generate condition check
const generateConditionCheck = (condition) => {
const { field, operator: condOp, value } = condition const { field, operator: condOp, value } = condition
switch (condOp) { switch (condOp) {
@ -2797,9 +2811,9 @@ const generateConditionalLogicCode = () => {
case 'not_equals': case 'not_equals':
return `getField('${field}') !== '${value}'` return `getField('${field}') !== '${value}'`
case 'contains': case 'contains':
return `String(getField('${field}')).includes('${value}')` return `String(getField('${field}') || '').toLowerCase().includes('${value}'.toLowerCase())`
case 'not_contains': case 'not_contains':
return `!String(getField('${field}')).includes('${value}')` return `!String(getField('${field}') || '').toLowerCase().includes('${value}'.toLowerCase())`
case 'is_empty': case 'is_empty':
return `!getField('${field}') || getField('${field}') === ''` return `!getField('${field}') || getField('${field}') === ''`
case 'is_not_empty': case 'is_not_empty':
@ -2811,32 +2825,56 @@ const generateConditionalLogicCode = () => {
default: default:
return `getField('${field}') === '${value}'` return `getField('${field}') === '${value}'`
} }
}).join(` ${operator} `) }
// Generate action // Helper function to generate action code
const actionCode = action === 'show' ? `showField('${fieldName}')` : const generateActionCode = (action, fieldName) => {
action === 'hide' ? `hideField('${fieldName}')` : switch (action) {
action === 'enable' ? `enableField('${fieldName}')` : case 'show': return `showField('${fieldName}')`
action === 'disable' ? `disableField('${fieldName}')` : case 'hide': return `hideField('${fieldName}')`
`showField('${fieldName}')` case 'enable': return `enableField('${fieldName}')`
case 'disable': return `disableField('${fieldName}')`
default: return `showField('${fieldName}')`
}
}
// Generate field change listeners // Helper function to generate inverse action code
const generateInverseActionCode = (action, fieldName) => {
switch (action) {
case 'show': return `hideField('${fieldName}')`
case 'hide': return `showField('${fieldName}')`
case 'enable': return `disableField('${fieldName}')`
case 'disable': return `enableField('${fieldName}')`
default: return `hideField('${fieldName}')`
}
}
// Generate condition checks
const conditionChecks = conditions.map(generateConditionCheck).join(` ${getJavaScriptOperator(operator)} `)
// Generate action functions
const actionCode = generateActionCode(action, fieldName)
const inverseActionCode = generateInverseActionCode(action, fieldName)
// Generate field change listeners - note about optimization
const watchedFields = [...new Set(conditions.map(c => c.field).filter(Boolean))] const watchedFields = [...new Set(conditions.map(c => c.field).filter(Boolean))]
// Add helpful comment about optimization when multiple fields watch the same condition
const optimizationNote = watchedFields.length > 1 ?
`// NOTE: When multiple fields use the same trigger field, the FormBuilder automatically\n// optimizes by grouping these handlers to avoid duplicate onFieldChange listeners.\n\n` :
''
const listenerCode = watchedFields.map(field => const listenerCode = watchedFields.map(field =>
`onFieldChange('${field}', function() { `onFieldChange('${field}', function() {
if (${conditionChecks}) { if (${conditionChecks}) {
${actionCode}; ${actionCode};
} else { } else {
${action === 'show' ? `hideField('${fieldName}')` : ${inverseActionCode};
action === 'hide' ? `showField('${fieldName}')` :
action === 'enable' ? `disableField('${fieldName}')` :
action === 'disable' ? `enableField('${fieldName}')` :
`hideField('${fieldName}')`};
} }
});` });`
).join('\n\n') ).join('\n\n')
return listenerCode || '// Invalid configuration' return optimizationNote + (listenerCode || '// Invalid configuration')
} }
const handleSave = () => { const handleSave = () => {

View File

@ -296,7 +296,7 @@
v-for="(component, index) in formStore.formComponents" v-for="(component, index) in formStore.formComponents"
:key="index" :key="index"
> >
<component-preview :component="component" :is-preview="false" /> <component-preview :component="component" :is-preview="true" />
</template> </template>
</div> </div>
@ -904,6 +904,20 @@
</details> </details>
</div> </div>
<!-- Conditional Logic Optimization -->
<div class="mb-4 p-3 bg-amber-50 rounded border border-amber-200">
<details>
<summary class="text-sm font-medium text-amber-800 cursor-pointer"> Conditional Logic Optimization</summary>
<div class="mt-2 text-xs text-amber-700 space-y-1">
<div><strong>Automatic Optimization:</strong> When multiple fields use <code>onFieldChange</code> with the same trigger field:</div>
<div> The form builder automatically groups them into a single handler</div>
<div> No need to worry about duplicate event listeners</div>
<div> Better performance and memory usage</div>
<div> Use field settings UI for visual condition setup, or write custom logic here</div>
</div>
</details>
</div>
<!-- Utility Functions --> <!-- Utility Functions -->
<div class="mb-4 p-3 bg-purple-50 rounded border border-purple-200"> <div class="mb-4 p-3 bg-purple-50 rounded border border-purple-200">
<details> <details>
@ -980,6 +994,20 @@
</div> </div>
</details> </details>
</div> </div>
<!-- Performance Optimization Notice -->
<div class="mb-4 p-3 bg-blue-50 rounded border border-blue-200">
<details>
<summary class="text-sm font-medium text-blue-800 cursor-pointer"> Performance Optimization</summary>
<div class="mt-2 text-xs text-blue-700 space-y-1">
<div><strong>Smart Field Watching:</strong> The form builder automatically optimizes conditional logic</div>
<div> Groups multiple field conditions that watch the same trigger field</div>
<div> Prevents duplicate <code>onFieldChange</code> handlers</div>
<div> Improves performance and reduces memory usage</div>
<div> Example: If 5 fields depend on "customer_type", only 1 handler is created</div>
</div>
</details>
</div>
</div> </div>
<RsCodeMirror <RsCodeMirror

View File

@ -1730,7 +1730,7 @@ const getWorkflowSubmitButtonStyle = () => {
<template v-for="component in currentForm.formComponents" :key="component.id || component.props.name"> <template v-for="component in currentForm.formComponents" :key="component.id || component.props.name">
<ComponentPreview <ComponentPreview
:component="component" :component="component"
:is-preview="false" :is-preview="true"
:field-states="fieldStates" :field-states="fieldStates"
@form-data-updated="handleFormDataUpdate" @form-data-updated="handleFormDataUpdate"
/> />

View File

@ -83,44 +83,7 @@ export default defineEventHandler(async (event) => {
console.log('Case instance created:', caseInstance.caseUUID); console.log('Case instance created:', caseInstance.caseUUID);
// Get the process definition console.log('Process started successfully - tasks will be created dynamically during workflow execution');
const processDefinition = process.processDefinition || {};
const nodes = processDefinition.nodes || [];
const edges = processDefinition.edges || [];
// Find all form nodes
const formNodes = nodes.filter(node => node.type === 'form');
if (formNodes.length === 0) {
console.log('No form nodes found in process');
return {
success: false,
error: 'Process does not contain any forms'
};
}
console.log(`Found ${formNodes.length} form nodes`);
// Create tasks for all forms
const tasks = [];
for (const formNode of formNodes) {
console.log('Creating task for form:', formNode.data?.label);
const task = await prisma.task.create({
data: {
taskUUID: uuidv4(),
caseID: caseInstance.caseID,
taskName: formNode.data?.label || 'Complete Form',
taskType: 'form',
taskStatus: 'pending',
taskAssignedTo: currentUser.userID,
taskFormID: formNode.data?.formId,
taskCreatedDate: new Date(),
taskModifiedDate: new Date()
}
});
tasks.push(task);
console.log('Task created:', task.taskUUID);
}
// Add to case timeline // Add to case timeline
console.log('Adding to case timeline...'); console.log('Adding to case timeline...');
@ -143,14 +106,7 @@ export default defineEventHandler(async (event) => {
name: caseInstance.caseName, name: caseInstance.caseName,
status: caseInstance.caseStatus, status: caseInstance.caseStatus,
startedAt: caseInstance.caseCreatedDate startedAt: caseInstance.caseCreatedDate
}, }
tasks: tasks.map(task => ({
id: task.taskUUID,
name: task.taskName,
type: task.taskType,
formId: task.taskFormID,
status: task.taskStatus
}))
} }
}; };
} catch (error) { } catch (error) {