Add Validation System Test Guide and Enhance Validation Panel Functionality

- Introduced a new `validation-test-guide.md` file detailing the testing process for the node validation system, including test cases and expected outcomes.
- Updated `ProcessFlowCanvas.vue` to integrate a collapsible validation panel, allowing users to toggle visibility and view validation statuses and issues in real-time.
- Enhanced the validation indicators and tooltips in `ValidationIndicator.vue` and `ValidationTooltip.vue` to provide clearer feedback on validation issues with improved styling and severity color coding.
- Removed the deprecated `vue-flow-custom-nodes-migration.md` and `vue-flow-migration-completed-final.md` documentation files to streamline project documentation.
- Adjusted styles in `ValidationTooltip.vue` for better visibility and user experience, ensuring tooltips are informative and visually distinct based on severity.
- Updated `index.vue` to ensure proper text color inheritance for custom nodes, enhancing overall UI consistency.
This commit is contained in:
Md Afiq Iskandar 2025-07-29 09:50:22 +08:00
parent 8f56505af1
commit f4eff35c4b
8 changed files with 355 additions and 728 deletions

View File

@ -15,6 +15,7 @@ import { Controls } from "@vue-flow/controls";
import { MiniMap } from "@vue-flow/minimap";
import InteractiveArrowEdge from "./InteractiveArrowEdge.vue";
import { useNodeValidation } from '~/composables/useNodeValidation';
import { useProcessBuilderStore } from '~/stores/processBuilder';
// Import all file-based custom node components
import StartNode from "~/components/process-flow/custom/StartNode.vue";
import EndNode from "~/components/process-flow/custom/EndNode.vue";
@ -107,6 +108,9 @@ const {
clearValidation
} = useNodeValidation();
// Initialize process store for validation updates
const processStore = useProcessBuilderStore();
// Initialize Vue Flow
const {
nodes,
@ -200,6 +204,14 @@ const toggleHelpGuide = () => {
showHelpGuide.value = !showHelpGuide.value;
};
// Validation panel state
const showValidationPanel = ref(true);
// Toggle validation panel
const toggleValidationPanel = () => {
showValidationPanel.value = !showValidationPanel.value;
};
// State management for preventing recursive updates
const isUpdatingNodes = ref(false);
const isUpdatingEdges = ref(false);
@ -753,11 +765,19 @@ watch(
// Debounce validation to avoid excessive re-computation
clearTimeout(validationTimeout.value);
validationTimeout.value = setTimeout(() => {
validateProcess(currentNodes, currentEdges || []);
const issues = validateProcess(currentNodes, currentEdges || []);
// Update the process store with validation results
processStore.updateValidationResults(
validationResults.value,
validationSummary.value,
overallValidationStatus.value
);
}, 300);
} else {
// Clear validation if no nodes
clearValidation();
processStore.clearValidationResults();
}
},
{ deep: true }
@ -1961,51 +1981,66 @@ function fromObject(flowObject) {
</div>
</Panel>
<!-- Validation Panel -->
<!-- Collapsible Validation Panel -->
<Panel position="top-left" class="validation-panel">
<div class="validation-panel-content">
<div class="flex items-center justify-between mb-2">
<h4 class="validation-title">Process Validation</h4>
<div class="validation-status" :class="overallValidationStatus">
<Icon
:name="overallValidationStatus === 'error' ? 'material-symbols:error' :
overallValidationStatus === 'warning' ? 'material-symbols:warning' :
'material-symbols:check-circle'"
class="w-4 h-4"
/>
<span class="status-text">
{{ overallValidationStatus === 'error' ? 'Errors' :
overallValidationStatus === 'warning' ? 'Warnings' : 'Valid' }}
<button
@click="toggleValidationPanel"
class="validation-toggle-btn"
:class="{ 'expanded': showValidationPanel }"
>
<Icon
:name="processStore.overallValidationStatus === 'error' ? 'material-symbols:error' :
processStore.overallValidationStatus === 'warning' ? 'material-symbols:warning' :
'material-symbols:check-circle'"
class="w-4 h-4"
/>
<span v-if="showValidationPanel">Hide Validation</span>
<span v-else>
Validation
<span v-if="processStore.validationSummary.totalIssues > 0" class="inline-badge">
{{ processStore.validationSummary.totalIssues }}
</span>
</span>
</button>
<div v-if="showValidationPanel" class="validation-content">
<div class="validation-header">
<div class="validation-status" :class="processStore.overallValidationStatus">
<span class="status-text">
{{ processStore.overallValidationStatus === 'error' ? 'Errors Found' :
processStore.overallValidationStatus === 'warning' ? 'Warnings Found' : 'All Valid' }}
</span>
</div>
</div>
</div>
<div v-if="validationSummary.totalIssues > 0" class="validation-summary">
<div class="summary-stats">
<span v-if="validationSummary.errors > 0" class="stat-item error">
{{ validationSummary.errors }} error{{ validationSummary.errors > 1 ? 's' : '' }}
</span>
<span v-if="validationSummary.warnings > 0" class="stat-item warning">
{{ validationSummary.warnings }} warning{{ validationSummary.warnings > 1 ? 's' : '' }}
</span>
<span v-if="validationSummary.infos > 0" class="stat-item info">
{{ validationSummary.infos }} info{{ validationSummary.infos > 1 ? 's' : '' }}
</span>
<div v-if="processStore.validationSummary.totalIssues > 0" class="validation-summary">
<div class="summary-stats">
<span v-if="processStore.validationSummary.errors > 0" class="stat-item error">
{{ processStore.validationSummary.errors }} error{{ processStore.validationSummary.errors > 1 ? 's' : '' }}
</span>
<span v-if="processStore.validationSummary.warnings > 0" class="stat-item warning">
{{ processStore.validationSummary.warnings }} warning{{ processStore.validationSummary.warnings > 1 ? 's' : '' }}
</span>
<span v-if="processStore.validationSummary.infos > 0" class="stat-item info">
{{ processStore.validationSummary.infos }} info{{ processStore.validationSummary.infos > 1 ? 's' : '' }}
</span>
</div>
</div>
<div v-else-if="nodes.length > 0" class="validation-success">
<Icon name="material-symbols:check-circle" class="w-4 h-4 text-green-500" />
<span class="text-green-600 text-sm">Process validation passed</span>
</div>
<div v-else class="validation-empty">
<span class="text-gray-500 text-sm">Add nodes to validate process</span>
</div>
<div v-if="isValidating" class="validation-loading">
<Icon name="material-symbols:refresh" class="w-4 h-4 animate-spin" />
<span class="text-sm text-gray-600">Validating...</span>
</div>
</div>
<div v-else-if="nodes.length > 0" class="validation-success">
<Icon name="material-symbols:check-circle" class="w-4 h-4 text-green-500" />
<span class="text-green-600 text-sm">Process validation passed</span>
</div>
<div v-else class="validation-empty">
<span class="text-gray-500 text-sm">Add nodes to validate process</span>
</div>
<div v-if="isValidating" class="validation-loading">
<Icon name="material-symbols:refresh" class="w-4 h-4 animate-spin" />
<span class="text-sm text-gray-600">Validating...</span>
</div>
</div>
</Panel>
@ -2404,26 +2439,69 @@ function fromObject(flowObject) {
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
padding: 12px;
min-width: 220px;
overflow: hidden;
max-width: 280px;
transition: all 0.3s ease;
}
.validation-title {
.validation-toggle-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: #059669;
color: white;
border: none;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
color: #374151;
margin: 0;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
width: 100%;
}
.validation-toggle-btn:hover {
background: #047857;
transform: translateY(-1px);
}
.validation-toggle-btn.expanded {
background: #dc2626;
}
.validation-toggle-btn.expanded:hover {
background: #b91c1c;
}
.inline-badge {
background: rgba(255, 255, 255, 0.3);
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
margin-left: 4px;
}
.validation-content {
padding: 12px;
background: white;
border-top: 1px solid #e5e7eb;
}
.validation-header {
margin-bottom: 8px;
}
.validation-status {
display: flex;
align-items: center;
gap: 4px;
gap: 6px;
font-size: 11px;
font-weight: 500;
padding: 2px 6px;
padding: 4px 8px;
border-radius: 4px;
background: #f9fafb;
border: 1px solid #e5e7eb;
}
.validation-status.error {

View File

@ -77,50 +77,40 @@ const severityClass = computed(() => {
const severityColorClass = computed(() => {
switch (validationSeverity.value) {
case 'error': return 'text-red-500'
case 'warning': return 'text-yellow-500'
case 'info': return 'text-blue-500'
case 'error': return '!text-red-500'
case 'warning': return '!text-yellow-500'
case 'info': return '!text-blue-500'
default: return ''
}
})
</script>
<template>
<ValidationTooltip
v-if="hasIssues && showTooltip"
:content="validationTooltip"
position="top"
>
<div
class="validation-indicator"
:class="severityClass"
>
<i
:class="[validationIcon, severityColorClass]"
class="validation-icon"
></i>
<!-- Badge with issue count -->
<span
v-if="validationIssues.length > 1"
class="validation-badge"
:class="severityClass"
>
{{ validationIssues.length }}
</span>
</div>
</ValidationTooltip>
<!-- Fallback without tooltip -->
<div
v-else-if="hasIssues"
v-if="hasIssues"
class="validation-indicator"
:class="severityClass"
>
<i
:class="[validationIcon, severityColorClass]"
<ValidationTooltip
v-if="showTooltip"
:content="validationTooltip"
:severity="validationSeverity"
position="top"
>
<Icon
:name="validationIcon"
:class="severityColorClass"
class="validation-icon"
></Icon>
</ValidationTooltip>
<!-- Fallback without tooltip -->
<Icon
v-else
:name="validationIcon"
:class="severityColorClass"
class="validation-icon"
></i>
></Icon>
<!-- Badge with issue count -->
<span

View File

@ -9,6 +9,10 @@ const props = defineProps({
position: {
type: String,
default: 'top' // top, bottom, left, right
},
severity: {
type: String,
default: 'info' // error, warning, info
}
})
@ -19,6 +23,15 @@ const positionClass = computed(() => {
return `tooltip-${props.position}`
})
const severityClass = computed(() => {
return `tooltip-${props.severity}`
})
// Format content with colored text for different sections
const formattedContent = computed(() => {
return props.content
})
const onMouseEnter = () => {
showTooltip.value = true
}
@ -40,10 +53,10 @@ const onMouseLeave = () => {
v-if="showTooltip && content"
ref="tooltipRef"
class="tooltip"
:class="positionClass"
:class="[positionClass, severityClass]"
>
<div class="tooltip-content">
<pre class="tooltip-text">{{ content }}</pre>
<pre class="tooltip-text">{{ formattedContent }}</pre>
</div>
<div class="tooltip-arrow"></div>
</div>
@ -53,20 +66,23 @@ const onMouseLeave = () => {
<style scoped>
.tooltip-container {
position: relative;
display: inline-block;
display: inline-flex;
align-items: center;
justify-content: center;
}
.tooltip {
position: absolute;
z-index: 9999;
background: #1f2937;
color: white;
border-radius: 6px;
padding: 8px 12px;
background: white;
color: #374151;
border: 1px solid #d1d5db;
border-radius: 8px;
padding: 10px 12px;
font-size: 12px;
white-space: nowrap;
max-width: 300px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
max-width: 280px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
pointer-events: none;
}
@ -75,15 +91,16 @@ const onMouseLeave = () => {
white-space: pre-line;
font-family: inherit;
font-size: 11px;
line-height: 1.4;
line-height: 1.5;
color: #374151;
font-weight: 500;
}
/* Position variants */
.tooltip-top {
bottom: 100%;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
margin-bottom: 8px;
}
.tooltip-bottom {
@ -114,13 +131,30 @@ const onMouseLeave = () => {
height: 0;
}
.tooltip-arrow::before {
content: '';
position: absolute;
width: 0;
height: 0;
}
.tooltip-top .tooltip-arrow {
top: 100%;
top: calc(100% - 1px);
left: 50%;
transform: translateX(-50%);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #1f2937;
border-top: 6px solid white;
z-index: 2;
}
.tooltip-top .tooltip-arrow::before {
top: -7px;
left: -6px;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #d1d5db;
z-index: 1;
}
.tooltip-bottom .tooltip-arrow {
@ -129,7 +163,7 @@ const onMouseLeave = () => {
transform: translateX(-50%);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #1f2937;
border-bottom: 6px solid white;
}
.tooltip-left .tooltip-arrow {
@ -138,7 +172,7 @@ const onMouseLeave = () => {
transform: translateY(-50%);
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 6px solid #1f2937;
border-left: 6px solid white;
}
.tooltip-right .tooltip-arrow {
@ -147,7 +181,33 @@ const onMouseLeave = () => {
transform: translateY(-50%);
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 6px solid #1f2937;
border-right: 6px solid white;
}
/* Severity styling */
.tooltip-error {
border-left: 4px solid #ef4444;
}
.tooltip-warning {
border-left: 4px solid #f59e0b;
}
.tooltip-info {
border-left: 4px solid #3b82f6;
}
/* Content color styling */
.tooltip-error .tooltip-text {
color: #b91c1c;
}
.tooltip-warning .tooltip-text {
color: #d97706;
}
.tooltip-info .tooltip-text {
color: #1e40af;
}
/* Responsive adjustments */

View File

@ -1,304 +0,0 @@
# Vue Flow Custom Nodes Migration Guide
## 🎯 Problem Solved
Custom nodes defined inline (using template strings or object definitions) **do not load in production** with Vue Flow. The solution is to use **separate .vue component files** that are properly imported.
## ✅ Production-Safe Approach: File-Based Custom Nodes
### 1. Create Separate .vue Files for Each Node Type
Instead of defining nodes inline in `composables/processFlowNodes.js`, create individual `.vue` files:
```
components/process-flow/custom/
├── StartNode.vue
├── FormNode.vue
├── EndNode.vue
├── ConditionalNode.vue
├── ScriptNode.vue
├── ApiNode.vue
├── NotificationNode.vue
└── ... (other node types)
```
### 2. Standard Vue Component Structure
Each node component should follow this pattern:
```vue
<script setup>
import { Handle, Position } from '@vue-flow/core'
// Define props that Vue Flow passes to custom nodes
const props = defineProps([
'id', // Node ID
'type', // Node type
'label', // Node label
'selected', // Selection state
'data' // Custom data object
])
</script>
<template>
<div class="custom-node" :class="{ selected }">
<!-- Input handle (if needed) -->
<Handle
type="target"
:position="Position.Left"
class="node-handle"
/>
<!-- Node content -->
<div class="node-content">
<div class="node-icon">🚀</div>
<div class="node-label">{{ data?.label || label || 'Default Label' }}</div>
</div>
<!-- Output handle (if needed) -->
<Handle
type="source"
:position="Position.Right"
class="node-handle"
/>
</div>
</template>
<style scoped>
/* Component-specific styles */
.custom-node {
border-radius: 8px;
padding: 12px;
background: linear-gradient(135deg, #10b981, #059669);
color: white;
border: 2px solid #047857;
min-width: 120px;
}
.custom-node.selected {
border-color: #fbbf24;
box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.3);
}
</style>
```
### 3. Import Components in Your Main File
In `pages/process-builder/index.vue`:
```javascript
// Import all custom node components
import StartNode from "~/components/process-flow/custom/StartNode.vue"
import FormNode from "~/components/process-flow/custom/FormNode.vue"
import EndNode from "~/components/process-flow/custom/EndNode.vue"
import ConditionalNode from "~/components/process-flow/custom/ConditionalNode.vue"
import ScriptNode from "~/components/process-flow/custom/ScriptNode.vue"
import ApiNode from "~/components/process-flow/custom/ApiNode.vue"
import NotificationNode from "~/components/process-flow/custom/NotificationNode.vue"
// ... import other nodes
```
### 4. Create nodeTypes Object
```javascript
import { markRaw } from 'vue'
// Map node types to components (use markRaw to prevent reactivity issues)
const nodeTypes = {
'start': markRaw(StartNode),
'form': markRaw(FormNode),
'end': markRaw(EndNode),
'conditional': markRaw(ConditionalNode),
'script': markRaw(ScriptNode),
'api': markRaw(ApiNode),
'notification': markRaw(NotificationNode),
// ... other node types
}
```
### 5. Use nodeTypes in VueFlow Component
```vue
<template>
<VueFlow
v-model:nodes="nodes"
v-model:edges="edges"
:node-types="nodeTypes"
@error="handleVueFlowError"
>
<Background />
<Controls />
</VueFlow>
</template>
```
## 🚫 What NOT to Use (Breaks in Production)
### ❌ Template Slots Approach
```vue
<!-- This breaks in production -->
<VueFlow v-model:nodes="nodes" v-model:edges="edges">
<template #node-start="{ data, label }">
<div class="start-node">{{ label }}</div>
</template>
</VueFlow>
```
### ❌ Inline Object Definitions
```javascript
// This breaks in production
const nodeTypes = {
'start': {
props: ['id', 'data'],
template: `<div>{{ data.label }}</div>`
}
}
```
## 📋 Migration Checklist
### Current State Analysis
- [ ] Review `composables/processFlowNodes.js` for all node type definitions
- [ ] Identify unique node types used in your process builder
- [ ] Note any complex computed properties or methods in existing nodes
### Migration Steps
1. [ ] Create individual `.vue` files for each node type in `components/process-flow/custom/`
2. [ ] Convert existing node logic from `processFlowNodes.js` to Vue component format
3. [ ] Import all node components in main process builder file
4. [ ] Create `nodeTypes` object mapping types to components with `markRaw`
5. [ ] Update VueFlow component to use `:node-types` prop instead of template slots
6. [ ] Remove old `processFlowNodes.js` file
7. [ ] Test all node types render correctly
8. [ ] Test in production environment
### Key Node Types to Migrate
Based on your `processFlowNodes.js`, you have these node types:
- `start` - Start nodes
- `form` - Form nodes
- `end` - End nodes
- `conditional` - Conditional/gateway nodes
- `script` - Script execution nodes
- `api` - API call nodes
- `notification` - Notification nodes
- `subprocess` - Subprocess nodes
- Various shape nodes (rectangle, circle, diamond, etc.)
## 🔧 Common Patterns for Migration
### Computed Properties
Convert computed properties from options API to composition API:
```javascript
// OLD (in processFlowNodes.js)
computed: {
nodeLabel() {
return this.data?.label || 'Default'
}
}
// NEW (in .vue component)
const nodeLabel = computed(() => {
return props.data?.label || 'Default'
})
```
### Event Handlers
```javascript
// OLD
methods: {
onClick() {
this.$emit('node-click', this.id)
}
}
// NEW
const emit = defineEmits(['node-click'])
const onClick = () => {
emit('node-click', props.id)
}
```
### Complex Node Logic
For nodes with complex logic (like ConditionalNode), maintain the same patterns but convert to composition API:
```vue
<script setup>
import { computed } from 'vue'
import { Handle, Position } from '@vue-flow/core'
const props = defineProps(['id', 'type', 'data', 'selected'])
const totalConditions = computed(() => {
return props.data?.conditions?.length || 0
})
const conditionSummary = computed(() => {
if (totalConditions.value === 0) return 'No conditions'
return `${totalConditions.value} condition${totalConditions.value > 1 ? 's' : ''}`
})
</script>
```
## 🚀 Benefits of File-Based Approach
1. **Production Compatibility** - Works reliably in all environments
2. **Better Performance** - Proper component compilation and tree-shaking
3. **Developer Experience** - Full IDE support, syntax highlighting, linting
4. **Maintainability** - Separate files are easier to manage and debug
5. **Reusability** - Components can be imported and used elsewhere
6. **Scoped Styles** - No CSS conflicts between node types
## 🐛 Debugging Tips
### Error Handling
Add error handling to catch Vue Flow issues:
```javascript
const handleVueFlowError = (error) => {
console.error('Vue Flow Error:', error)
if (isErrorOfType(error, ErrorCode.MISSING_VIEWPORT_DIMENSIONS)) {
console.error('Container needs explicit width/height')
}
}
```
### Node ID Conflicts
Ensure all node IDs are unique across your application to prevent rendering conflicts.
## 📁 File Structure After Migration
```
components/process-flow/custom/
├── StartNode.vue
├── FormNode.vue
├── EndNode.vue
├── ConditionalNode.vue
├── ScriptNode.vue
├── ApiNode.vue
├── NotificationNode.vue
├── SubprocessNode.vue
├── RectangleShapeNode.vue
├── CircleShapeNode.vue
├── DiamondShapeNode.vue
├── TriangleShapeNode.vue
├── PentagonShapeNode.vue
├── HexagonShapeNode.vue
└── OctagonShapeNode.vue
pages/process-builder/
└── index.vue (updated to import and use file-based nodes)
# Remove after migration:
composables/processFlowNodes.js ❌
```
---
## 🎯 Ready to Migrate?
When you're ready to start the migration, provide:
1. Your current `pages/process-builder/index.vue` file
2. Your current `composables/processFlowNodes.js` file
3. Any specific node types you want to prioritize
The assistant will help you convert each node type to the production-safe file-based approach! 🚀

View File

@ -1,188 +0,0 @@
# Vue Flow Custom Nodes Migration - COMPLETED ✅
## Migration Summary
The Vue Flow custom nodes migration has been **successfully completed**. All inline node definitions have been converted to file-based Vue components for production compatibility.
## What Was Accomplished
### 1. **Created File-Based Node Components**
All node types have been migrated from inline definitions to individual `.vue` files:
**Process Nodes:**
- `components/process-flow/custom/StartNode.vue`
- `components/process-flow/custom/EndNode.vue`
- `components/process-flow/custom/FormNode.vue`
- `components/process-flow/custom/ApiNode.vue`
- `components/process-flow/custom/GatewayNode.vue`
- `components/process-flow/custom/ScriptNode.vue`
- `components/process-flow/custom/BusinessRuleNode.vue`
- `components/process-flow/custom/NotificationNode.vue`
- `components/process-flow/custom/HtmlNode.vue`
- `components/process-flow/custom/SubprocessNode.vue`
**Shape Nodes:**
- `components/process-flow/custom/HexagonShape.vue`
- `components/process-flow/custom/TrapezoidShape.vue`
- `components/process-flow/custom/RectangleShape.vue`
- `components/process-flow/custom/SwimlaneHorizontal.vue`
- `components/process-flow/custom/SwimlaneVertical.vue`
- `components/process-flow/custom/TextAnnotation.vue`
- `components/process-flow/custom/ProcessGroup.vue`
### 2. **Updated Vue Flow Canvas**
- Modified `components/process-flow/ProcessFlowCanvas.vue` to import all new components
- Created `customNodeTypes` object with `markRaw` wrappers for production safety
- Removed dependency on old `composables/processFlowNodes.js`
### 3. **Extracted Styles**
- Created `composables/nodeStyles.js` for shared node styling
- Updated `plugins/process-flow-styles.client.js` to use new styles location
- Maintained all existing visual styling and behavior
### 4. **Clean Migration**
- **Removed** old `composables/processFlowNodes.js` file
- **Updated** all references to use new file structure
- **Verified** no remaining dependencies on old composable
## Technical Implementation
### Node Component Structure
Each node component follows this pattern:
```vue
<script setup>
import { Handle, Position } from '@vue-flow/core'
import { computed } from 'vue'
const props = defineProps(['id', 'type', 'label', 'selected', 'data'])
const emit = defineEmits(['node-click'])
const nodeLabel = computed(() => props.label || props.data?.label || 'Default Label')
const onClick = () => emit('node-click', props.id)
</script>
<template>
<div :class="['custom-node', 'node-{type}', { selected }]" @click="onClick">
<Handle type="target" :position="Position.Top" ... />
<Handle type="source" :position="Position.Right" ... />
<!-- Node content -->
</div>
</template>
<style scoped>
/* Component-specific styles */
</style>
```
### Vue Flow Integration
```javascript
// In ProcessFlowCanvas.vue
import { markRaw } from 'vue'
import StartNode from '~/components/process-flow/custom/StartNode.vue'
// ... other imports
const customNodeTypes = {
start: markRaw(StartNode),
end: markRaw(EndNode),
// ... other node types
}
```
## Production Benefits
### ✅ **Fixed Production Issues**
- **Eliminated** template compilation errors in production builds
- **Resolved** SSR/hydration mismatches
- **Improved** component loading and bundling efficiency
### ✅ **Enhanced Maintainability**
- **Separated** concerns: each node type in its own file
- **Improved** code organization and readability
- **Easier** debugging and testing of individual node types
### ✅ **Better Developer Experience**
- **Full** IDE support for Vue SFC features
- **Proper** component hot-reloading during development
- **Type safety** with TypeScript support
## Verification Checklist
### ✅ **Migration Completed**
- [x] All 17 node types converted to Vue components
- [x] ProcessFlowCanvas updated to use file-based components
- [x] Styles extracted to separate composable
- [x] Plugin updated to use new styles location
- [x] Old processFlowNodes.js file removed
- [x] All references updated
### ✅ **Production Ready**
- [x] Using `markRaw` to prevent reactivity issues
- [x] Proper component imports and registration
- [x] No remaining inline node definitions
- [x] Compatible with Nuxt production builds
## File Structure After Migration
```
components/process-flow/custom/
├── StartNode.vue
├── EndNode.vue
├── FormNode.vue
├── ApiNode.vue
├── GatewayNode.vue
├── ScriptNode.vue
├── BusinessRuleNode.vue
├── NotificationNode.vue
├── HtmlNode.vue
├── SubprocessNode.vue
├── HexagonShape.vue
├── TrapezoidShape.vue
├── RectangleShape.vue
├── SwimlaneHorizontal.vue
├── SwimlaneVertical.vue
├── TextAnnotation.vue
└── ProcessGroup.vue
composables/
└── nodeStyles.js (extracted from old processFlowNodes.js)
plugins/
└── process-flow-styles.client.js (updated import)
```
## Next Steps
### Immediate Actions
1. **Test the process builder** in development to verify all nodes render correctly
2. **Test node connections** and ensure handles work properly
3. **Verify configuration modals** open correctly for each node type
### Production Deployment
1. **Build the application** for production (`npm run build`)
2. **Test production build** functionality
3. **Deploy with confidence** - production issues are resolved
### Optional Enhancements
1. **Add TypeScript types** for better development experience
2. **Create unit tests** for individual node components
3. **Document node configuration** options for each component
## Troubleshooting
If you encounter any issues:
1. **Check imports** - Ensure all new component paths are correct
2. **Verify markRaw usage** - All components should be wrapped with `markRaw()`
3. **Review console errors** - Look for missing components or import issues
4. **Test in development first** - Verify everything works before production build
## Migration Success ✅
The Vue Flow custom nodes migration is **100% complete** and **production-ready**. The application now uses a modern, maintainable component architecture that will work reliably in all deployment environments.
---
**Migration completed on:** December 2024
**Files migrated:** 17 node components + 1 styles file
**Production compatibility:** ✅ Verified
**Backward compatibility:** ✅ Maintained

View File

@ -1,136 +0,0 @@
# Vue Flow Custom Nodes Migration - COMPLETED
## 🎯 Migration Summary
The Vue Flow custom nodes have been successfully migrated from inline definitions to production-safe file-based components as outlined in the migration guide.
## ✅ Completed Tasks
### 1. Directory Structure ✅
Created: `components/process-flow/custom/`
### 2. Core Process Node Components ✅
- `StartNode.vue` - Process start point with output handles
- `EndNode.vue` - Process end point with input handles
- `FormNode.vue` - Form task with full configuration display
- `ApiNode.vue` - API call with method/URL display
- `GatewayNode.vue` - Decision point with diamond shape and condition display
- `ScriptNode.vue` - Script execution with language display
- `BusinessRuleNode.vue` - Business logic with condition/action summary
- `NotificationNode.vue` - Notification with type and recipient display
- `HtmlNode.vue` - Custom HTML content display
- `SubprocessNode.vue` - Sub-process execution display
### 3. Shape Components ✅
- `HexagonShape.vue` - Hexagon design element with CSS clip-path
- `TrapezoidShape.vue` - Trapezoid design element with CSS clip-path
- `RectangleShape.vue` - Rectangle design element
- `SwimlaneHorizontal.vue` - Horizontal swimlane for process grouping
- `SwimlaneVertical.vue` - Vertical swimlane for process grouping
### 4. Vue Flow Integration ✅
- Updated `ProcessFlowCanvas.vue` to import all file-based components
- Created `customNodeTypes` object with `markRaw()` wrappers
- Removed dependency on old `composables/processFlowNodes.js`
## 🔧 Key Migration Benefits
1. **Production Compatibility** - File-based components work reliably in production builds
2. **Better Performance** - Proper component compilation and tree-shaking
3. **Developer Experience** - Full IDE support, syntax highlighting, and linting
4. **Maintainability** - Separate files are easier to manage and debug
5. **Reusability** - Components can be imported and used elsewhere
6. **Scoped Styles** - No CSS conflicts between node types
## 📁 Final File Structure
```
components/process-flow/custom/
├── StartNode.vue
├── EndNode.vue
├── FormNode.vue
├── ApiNode.vue
├── GatewayNode.vue
├── ScriptNode.vue
├── BusinessRuleNode.vue
├── NotificationNode.vue
├── HtmlNode.vue
├── SubprocessNode.vue
├── HexagonShape.vue
├── TrapezoidShape.vue
├── RectangleShape.vue
├── SwimlaneHorizontal.vue
└── SwimlaneVertical.vue
```
## 🧪 Testing Requirements
The following should be tested to verify the migration:
### Node Rendering Tests
- [ ] All node types render correctly in the process builder
- [ ] Node colors and styling work as expected
- [ ] Node labels and descriptions display properly
- [ ] Handle positioning and connections work correctly
### Functionality Tests
- [ ] Node selection works
- [ ] Node dragging works
- [ ] Node configuration modals open correctly
- [ ] Node deletion works
- [ ] Edge connections between nodes work
- [ ] Save/load process functionality works
### Shape Tests
- [ ] Shape nodes render without connection handles
- [ ] Shape layering (z-index) works correctly
- [ ] Shape resizing works
- [ ] Shape selection and styling works
### Production Build Test
- [ ] `npm run build` completes successfully
- [ ] Production build loads and works correctly
- [ ] All node types work in production environment
## 🗑️ Cleanup Tasks
After successful testing:
- [ ] Remove `composables/processFlowNodes.js`
- [ ] Remove any remaining references to the old composable
- [ ] Update any documentation that references the old approach
## 🚨 Rollback Plan
If issues are discovered, the rollback process is:
1. Restore `composables/processFlowNodes.js` from git history
2. Revert changes to `ProcessFlowCanvas.vue`
3. Remove the `components/process-flow/custom/` directory
4. Restart development server
## 📋 Node Type Mapping
| Node Type | File Component | Status |
|-----------|---------------|--------|
| `start` | `StartNode.vue` | ✅ |
| `end` | `EndNode.vue` | ✅ |
| `form` | `FormNode.vue` | ✅ |
| `api` | `ApiNode.vue` | ✅ |
| `gateway` | `GatewayNode.vue` | ✅ |
| `script` | `ScriptNode.vue` | ✅ |
| `business-rule` | `BusinessRuleNode.vue` | ✅ |
| `notification` | `NotificationNode.vue` | ✅ |
| `html` | `HtmlNode.vue` | ✅ |
| `subprocess` | `SubprocessNode.vue` | ✅ |
| `hexagon-shape` | `HexagonShape.vue` | ✅ |
| `trapezoid-shape` | `TrapezoidShape.vue` | ✅ |
| `rectangle-shape` | `RectangleShape.vue` | ✅ |
| `swimlane-horizontal` | `SwimlaneHorizontal.vue` | ✅ |
| `swimlane-vertical` | `SwimlaneVertical.vue` | ✅ |
---
**Migration completed successfully!** 🚀
The Vue Flow custom nodes are now using the production-safe file-based approach and ready for testing.

View File

@ -3973,7 +3973,7 @@ const sendToBack = () => {
}
/* Ensure all text elements in nodes inherit the custom text color */
:deep(.custom-node *) {
:deep(.custom-node *:not(.icon)) {
color: inherit !important;
}

127
validation-test-guide.md Normal file
View File

@ -0,0 +1,127 @@
# Validation System Test Guide
## How to Test the Node Validation System
### 1. Start the Development Server
```bash
yarn dev
```
### 2. Navigate to Process Builder
- Go to `/process-builder/` in your browser
- Create a new process or open an existing one
### 3. Test Cases to Verify
#### **Case 1: Missing Start Node (Should show error)**
- Create a process with only form and end nodes
- You should see an error in the validation panel: "Process has no start node"
#### **Case 2: Form Node Without Form Selected (Should show warning)**
- Add a Form node to the canvas
- Don't configure any form for it
- You should see a warning icon on the node with tooltip: "Form node has no form selected"
#### **Case 3: API Node Without URL (Should show error)**
- Add an API node to the canvas
- Don't configure any URL for it
- You should see an error icon on the node with tooltip: "API node has no URL configured"
#### **Case 4: Gateway Node Without Conditions (Should show warning)**
- Add a Gateway node to the canvas
- Don't configure any conditions for it
- You should see a warning icon on the node with tooltip: "Gateway node has no conditions defined"
#### **Case 5: Script Node Without Code (Should show warning)**
- Add a Script node to the canvas
- Don't add any code content
- You should see a warning icon on the node with tooltip: "Script node has no code defined"
### 4. Expected Visual Elements
#### **Validation Panel (Top-Left)**
- Shows overall validation status (Valid/Warnings/Errors)
- Displays counts for each severity level
- Updates in real-time as you add/modify nodes
#### **Node Validation Indicators**
- **Red circle with error icon**: Critical errors that prevent execution
- **Yellow circle with warning icon**: Warnings that should be addressed
- **Blue circle with info icon**: Informational suggestions
- **Number badge**: Shows count when multiple issues exist
#### **Tooltips**
- **Light, readable design**: White background with dark text for better visibility
- **Positioned above icons**: Tooltips appear above the validation icons with arrows pointing down
- **Color-coded by severity**:
- Red text and border for errors
- Yellow/orange text and border for warnings
- Blue text and border for info
- **Organized content**: Messages organized by severity (ERRORS, WARNINGS, INFO)
- **Clear descriptions**: Detailed explanations of what needs to be fixed
### 5. How to Fix Validation Issues
#### **Form Node Warnings:**
- Click on the form node to open configuration
- Select a form from the dropdown
- Warning should disappear
#### **API Node Errors:**
- Click on the API node to open configuration
- Enter a valid URL (e.g., https://api.example.com)
- Select HTTP method
- Error should disappear
#### **Gateway Node Warnings:**
- Click on the gateway node to open configuration
- Add at least one condition with variable, operator, and value
- Warning should disappear
#### **Script Node Warnings:**
- Click on the script node to open configuration
- Add JavaScript code in the code editor
- Warning should disappear
### 6. Real-time Validation
- Validation happens automatically as you:
- Add new nodes
- Remove nodes
- Modify node configurations
- Connect/disconnect nodes
- No manual refresh needed
### 7. Performance Features
- Validation is debounced (300ms delay) to prevent excessive computation
- Only validates when nodes are present
- Clears validation when canvas is empty
## Troubleshooting
### If tooltips don't appear:
- Make sure you're hovering directly over the validation icon
- Check browser console for any JavaScript errors
- Verify the validation icon is clickable (cursor should change to help)
### If validation panel is empty:
- Add some nodes to trigger validation
- Check that nodes have configuration issues
- Look for any console errors
### If validation icons don't appear:
- Verify nodes have actual validation issues
- Check that the ValidationIndicator component is properly loaded
- Ensure the process store is connected
## Success Criteria
✅ **The validation system is working correctly if:**
1. Validation panel shows in top-left corner
2. Validation icons appear on misconfigured nodes
3. Tooltips display detailed error messages on hover
4. Validation updates in real-time as you modify the process
5. Different severity levels show different colored icons
6. Multiple issues show number badges
7. Fixing configuration issues removes validation warnings
This validation system helps users build robust business processes by providing immediate feedback on configuration issues and process structure problems.