Add Workflow Execution Page and Update Process Management
- Introduced a new page for executing business process workflows, allowing users to run and interact with processes directly. - Added functionality to navigate to the workflow execution page from the process management interface. - Enhanced the user experience with loading states and dynamic step execution feedback. - Updated the process management page to include a button for running workflows, improving accessibility to process execution features. - Ensured integration with existing form and conditional logic components for seamless workflow execution.
This commit is contained in:
parent
2f1d3964c8
commit
eadf3443de
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(mkdir:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
Definisi Aktiviti BF-NAS-PRF-AS-QS-03 Analisa Data (Had Kifayah)
|
||||
DEFINISI AKTIVITI FUNGSI BISNES
|
||||
Rujukan Fungsi BF-NAS-PRF-AS_QS
|
||||
Nama Fungsi Pendaftaran Pantas Perseorangan
|
||||
Rujukan Aktiviti BF-NAS-PRF-AS-QS-03
|
||||
Nama Aktiviti Analisa Data (Had Kifayah)
|
||||
Keterangan Aktiviti Proses pengiraan had kifayah asnaf menggunakan maklumat konfigurasi had kifayah dan juga maklumat yang telah diisi oleh pemohon/pendaftar
|
||||
Aktor NAS
|
||||
|
||||
Aktiviti Sebelum -
|
||||
|
||||
Aktiviti Selepas/ Aktiviti Lain BF-NAS-PRF-AS-QS-04
|
||||
Syor Status Keluarga Asnaf
|
||||
|
||||
Kaedah/Operasi (Bagaimana):
|
||||
1. Sebagai contoh, proses pengiraan had kifayah menggunakan maklumat-maklumat didalam konfigurasi had kifayah. Untuk kes pendaftaran pantas perseorangan, hanya minimum maklumat yang dimasukkan. Oleh itu proses pengiraan had kifayah akan menggunakan maklumat-maklumat yang dimasukkan oleh pemohon.
|
||||
• No Pengenalan
|
||||
• Pendapatan
|
||||
• Bilangan tanggungan
|
||||
2. Sistem akan membuat pengiraan had kifayah menggunakan maklumat yang dimasukkan satu persatu mengikut formula had kifayah yang telah dikonfigurasi.
|
||||
3. Pengesyoran profil akan menggunakan maklumat %had kifayah, contohnya:
|
||||
• 0%-49.9% - Fakir
|
||||
• 50 – 100% - Miskin
|
||||
• >100% - Non-FM
|
||||
4. Pengiraan had kifayah ini adalah dinamik. Sistem akan membuat pengiraan mengikut formula terkini.
|
||||
5. Data had kifayah keluarga akan disimpan ke dalam pangkalan data
|
||||
6. Sistem akan menjana ID rekod dan masa tindakan.
|
||||
7. Rekod Log
|
||||
o Tindakan Admin direkodkan ke dalam Audit Trail, termasuk:
|
||||
- Nama pengguna
|
||||
- Tarikh dan Masa
|
||||
- Jenis perubahan (Tambah/Kemaskini)
|
||||
- Butiran data
|
||||
8. Pemberitahuan Sistem (Automatik) – ‘Pengiraan had kifayah selesai’
|
||||
Penggunaan Maklumat:
|
||||
1. No Pengenalan Ketua Keluarga(CRU)
|
||||
2. No Pengenalan Tanggungan (CRU)
|
||||
3. Jumlah had kifayah mengikut formula (CRU)
|
||||
4. Kira Jumlah Had Kifayah Keluarga (CRU)
|
||||
5. Kira %Had Kifayah (CRU)
|
||||
6. Kategori Keluarga (CRU)
|
||||
Polisi dan Dasar Berkaitan
|
||||
1. Dasar Keselamatan ICT LZS
|
||||
• Polisi Pengurusan Data ICT Kuatkuasa 1 Jan 2020 (3)
|
||||
• Polisi Perlindungan Data Peribadi LZS
|
||||
2. Garis Panduan dan Syarat Kelayakan Agihan Zakat
|
||||
• Menetapkan prinsip asas mengenai pemberian zakat termasuk had keperluan asas dan mekanisme kelayakan penerima.
|
||||
• Kelulusan kadar kifayah mesti selari dengan keutamaan dan strategi agihan semasa.
|
||||
Kaedah Alternatif
|
||||
Tiada
|
||||
Ciri-ciri Kualiti (Keperluan Bukan Fungsian)
|
||||
1. Audit Trail
|
||||
Catatan Tambahan:
|
||||
Aktiviti akan dipanggil setiap kali membuat pendaftaran pantas asnaf.
|
||||
|
||||
Jadual 28: Definisi Aktiviti BF-NAS-PRF-AS-QS-03 Analisa Data (Had Kifayah)
|
||||
|
||||
Definisi Aktiviti BF-NAS-PRF-AS-QS-04 Syor Status Keluarga Asnaf
|
||||
DEFINISI AKTIVITI FUNGSI BISNES
|
||||
Rujukan Fungsi BF-NAS-PRF-AS-QS
|
||||
Nama Fungsi Pendaftaran Pantas Perseorangan
|
||||
Rujukan Aktiviti BF-NAS-PRF-AS-QS-04
|
||||
Nama Aktiviti Syor Status Keluarga Asnaf
|
||||
Keterangan Aktiviti Sistem akan mengesyorkan status keluarga asnaf hasil dari pengiraan had kifayah.
|
||||
Aktor NAS
|
||||
|
||||
Aktiviti Sebelum -
|
||||
|
||||
Aktiviti Selepas/ Aktiviti Lain BF-NAS-PRF-AS-QS-05
|
||||
Pengesahan
|
||||
|
||||
Kaedah/Operasi (Bagaimana):
|
||||
1. Pengesyoran profil akan menggunakan maklumat %had kifayah, contohnya:
|
||||
a. 0%-49.9% - Fakir
|
||||
b. 50 – 100% - Miskin
|
||||
c. >100% - Non-FM
|
||||
2. Pengiraan had kifayah ini adalah dinamik. Sistem akan membuat pengiraan mengikut formula terkini.
|
||||
3. Notifikasi berkenaan maklumat ID ketua keluarga akan dihantar kepada pegawai yang akan membuat pengesahan.
|
||||
4. Pengesahan akan dilakukan oleh pegawai yang telah dikonfigurasi untuk mengesahkan profil ini.
|
||||
5. Jika tarikh masuk islam diisi, status individu tersebut akan menjadi ‘Muallaf’ dengan syarat tempoh tarikh masuk islam dengan tarikh semasa kurang dari 5 tahun.
|
||||
6. Status keluarga akan ditentukan oleh proses pengiraan had kifayah.
|
||||
7. Status individu tanggungan akan menggunakan tetapan status keluarga tersebut. (Fakir, Miskin, Muallaf, Non-FM)
|
||||
8. Simpan maklumat
|
||||
a. %Had kifayah kepada %had kifayah syor
|
||||
b. status keluarga kepada kategori keluarga asnaf syor
|
||||
c. status asnaf = status keluarga kepada kategori asnaf syor
|
||||
9. Ringkasan maklumat asnaf yang digenerate oleh AI perlu disimpan di dalam pangkalan data (syor pengesahan) berserta tarikh janaan laporan (tarikh pengesyoran) dan akan dipaparkan kemudian pada proses pengesahan.
|
||||
Penggunaan Maklumat:
|
||||
Maklumat Had Kifayah
|
||||
1. No Pengenalan Ketua Keluarga(R)
|
||||
2. Nama (R)
|
||||
3. Alamat (R)
|
||||
4. %Had Kifayah syor (C)
|
||||
5. Kategori Keluarga Asnaf syor (C)
|
||||
6. Kategori Asnaf syor (C)
|
||||
7. Kategori Tanggungan syor (C)
|
||||
8. Syor Pengesahan (C)
|
||||
9. Tarikh Pengesyoran (C)
|
||||
Polisi dan Dasar Berkaitan
|
||||
1. Dasar Keselamatan ICT LZS
|
||||
• Polisi Pengurusan Data ICT Kuatkuasa 1 Jan 2020 (3)
|
||||
• Polisi Perlindungan Data Peribadi LZS
|
||||
2. Garis Panduan dan Syarat Kelayakan Agihan Zakat
|
||||
• Menetapkan prinsip asas mengenai pemberian zakat termasuk had keperluan asas dan mekanisme kelayakan penerima.
|
||||
• Kelulusan kadar kifayah mesti selari dengan keutamaan dan strategi agihan semasa.
|
||||
Kaedah Alternatif
|
||||
Tiada
|
||||
Ciri-ciri Kualiti (Keperluan Bukan Fungsian)
|
||||
Tiada
|
||||
Catatan Tambahan:
|
||||
Aktiviti akan dipanggil setiap kali membuat pendaftaran pantas asnaf.
|
@ -1,303 +0,0 @@
|
||||
# Form Builder Development Changelog
|
||||
|
||||
This document tracks specific development changes, bug fixes, and feature implementations for the Form Builder module.
|
||||
|
||||
## December 2024 Development Session
|
||||
|
||||
### 🔍 **Critical Discovery: Dual Configuration System Issue**
|
||||
|
||||
**Date**: December 2024
|
||||
**Severity**: High
|
||||
**Status**: Partially Resolved
|
||||
|
||||
#### Issue Description
|
||||
Discovered two separate configuration systems for form components causing enhanced settings to not appear in the form builder interface:
|
||||
|
||||
1. **FormBuilderConfiguration.vue** - Enhanced with new features but not used by the form builder
|
||||
2. **FormBuilderFieldSettingsModal.vue** - Actually used by the form builder but limited
|
||||
|
||||
#### Root Cause Analysis
|
||||
The form builder interface was loading configuration settings from `FormBuilderFieldSettingsModal.vue`, while development enhancements were being added to `FormBuilderConfiguration.vue`, causing a disconnect between enhanced features and the user interface.
|
||||
|
||||
#### Resolution Actions Taken
|
||||
- ✅ **Analysis Complete**: Identified the dual system architecture
|
||||
- ✅ **Migration Started**: Began migrating dynamic-list configuration to the active system
|
||||
- ✅ **Field Configurations**: Added dynamic-list to basic field settings (label, name, placeholder, help, width)
|
||||
- ✅ **Settings Integration**: Added dynamic-list to hasSpecificSettings array
|
||||
- ✅ **UI Elements**: Added icon, type name, and description for dynamic-list
|
||||
- 🟡 **Template Section**: Started but incomplete - template section for dynamic-list specific settings
|
||||
|
||||
#### Remaining Work
|
||||
- [ ] Complete dynamic-list template section in FormBuilderFieldSettingsModal.vue
|
||||
- [ ] Test full configuration panel functionality
|
||||
- [ ] Migrate other enhanced components to the active configuration system
|
||||
- [ ] Consider long-term unification of configuration systems
|
||||
|
||||
---
|
||||
|
||||
### 🚀 **Dynamic List Component Enhancement**
|
||||
|
||||
**Date**: December 2024
|
||||
**Status**: ✅ Completed (Component Logic) / 🟡 Pending (Configuration Panel)
|
||||
**Impact**: High - Transforms basic list into professional-grade data collection tool
|
||||
|
||||
#### Features Implemented
|
||||
|
||||
##### 1. **Item Validation System**
|
||||
```javascript
|
||||
// Added to FormBuilderComponents.vue
|
||||
itemValidation: {
|
||||
required: false,
|
||||
minLength: 0,
|
||||
maxLength: 100,
|
||||
pattern: null
|
||||
}
|
||||
```
|
||||
- Individual item validation with customizable rules
|
||||
- Support for required fields, length limits, and pattern matching
|
||||
- Real-time validation feedback with error messages
|
||||
|
||||
##### 2. **Uniqueness Validation**
|
||||
```javascript
|
||||
allowDuplicates: false // Prevents duplicate entries
|
||||
```
|
||||
- Real-time duplicate detection as user types
|
||||
- Visual feedback when duplicates are detected
|
||||
- Option to allow or prevent duplicate entries
|
||||
|
||||
##### 3. **Item Type Support**
|
||||
```javascript
|
||||
itemType: 'text' // Options: text, number, email, url
|
||||
```
|
||||
- Type-specific validation (email format, URL format, numeric values)
|
||||
- Appropriate input styling and behavior per type
|
||||
- Enhanced user experience with proper input methods
|
||||
|
||||
##### 4. **Search and Filter System**
|
||||
```javascript
|
||||
enableSearch: true
|
||||
```
|
||||
- Real-time search through list items
|
||||
- Case-insensitive filtering
|
||||
- Search query highlighting
|
||||
- Clear search functionality
|
||||
|
||||
##### 5. **Bulk Operations**
|
||||
```javascript
|
||||
bulkOperations: true
|
||||
```
|
||||
- Select all / select none functionality
|
||||
- Bulk delete selected items
|
||||
- Visual selection indicators with checkboxes
|
||||
- Confirmation dialogs for bulk actions
|
||||
|
||||
##### 6. **Import/Export Functionality**
|
||||
```javascript
|
||||
exportFormat: 'json', // Options: json, csv, txt
|
||||
importEnabled: true
|
||||
```
|
||||
- Export data in multiple formats (JSON, CSV, TXT)
|
||||
- Import functionality with format detection
|
||||
- Data validation during import process
|
||||
- Error handling for malformed import data
|
||||
|
||||
##### 7. **Enhanced UI Features**
|
||||
- Item counter display (current/max items)
|
||||
- Delete confirmation dialogs
|
||||
- Visual sorting indicators (drag handles)
|
||||
- Professional styling with loading states
|
||||
- Error state management with user feedback
|
||||
|
||||
#### Technical Implementation
|
||||
|
||||
**Files Modified**:
|
||||
- ✅ `components/FormBuilderComponents.vue` - Added 10 new properties to dynamic-list component definition
|
||||
- ✅ `components/FormBuilderConfiguration.vue` - Created comprehensive configuration interface (not in use)
|
||||
- ✅ `components/ComponentPreview.vue` - Implemented full validation engine and UI enhancements
|
||||
|
||||
**Code Architecture**:
|
||||
```javascript
|
||||
// Validation Engine
|
||||
const validateItem = (item, index) => {
|
||||
const errors = []
|
||||
|
||||
// Type validation
|
||||
if (itemType.value === 'email' && !isValidEmail(item)) {
|
||||
errors.push('Invalid email format')
|
||||
}
|
||||
|
||||
// Uniqueness check
|
||||
if (!allowDuplicates.value && isDuplicate(item, index)) {
|
||||
errors.push('Duplicate items not allowed')
|
||||
}
|
||||
|
||||
// Custom validation rules
|
||||
if (itemValidation.value?.required && !item?.trim()) {
|
||||
errors.push('This field is required')
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// Search/Filter System
|
||||
const filteredItems = computed(() => {
|
||||
if (!searchQuery.value) return modelValue.value || []
|
||||
return (modelValue.value || []).filter(item =>
|
||||
item.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
// Import/Export System
|
||||
const exportData = (format) => {
|
||||
const data = modelValue.value || []
|
||||
switch (format) {
|
||||
case 'json': return JSON.stringify(data, null, 2)
|
||||
case 'csv': return data.join('\n')
|
||||
case 'txt': return data.join('\n')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Performance Considerations
|
||||
- Implemented debounced search to prevent excessive filtering
|
||||
- Lazy validation to avoid blocking UI during large list operations
|
||||
- Efficient duplicate detection using Set data structures
|
||||
- Minimal re-renders through computed properties and proper reactivity
|
||||
|
||||
#### Testing Status
|
||||
- ✅ Manual testing completed for all new features
|
||||
- ✅ Validation engine tested with various input types
|
||||
- ✅ Search functionality verified with large datasets
|
||||
- ✅ Import/export tested with different data formats
|
||||
- ❌ **Missing**: Configuration panel testing (pending migration completion)
|
||||
|
||||
---
|
||||
|
||||
### 🔧 **Configuration System Migration Status**
|
||||
|
||||
#### Progress Tracking
|
||||
|
||||
**Phase 1: Analysis and Discovery** ✅ Completed
|
||||
- [x] Identified dual configuration system issue
|
||||
- [x] Analyzed both configuration systems
|
||||
- [x] Determined root cause of missing settings
|
||||
|
||||
**Phase 2: Basic Integration** ✅ Completed
|
||||
- [x] Added dynamic-list to field configurations array
|
||||
- [x] Added dynamic-list to hasSpecificSettings array
|
||||
- [x] Added icon, type name, and description
|
||||
- [x] Basic field settings (label, name, placeholder, help, width)
|
||||
|
||||
**Phase 3: Advanced Settings Migration** 🟡 In Progress
|
||||
- [x] Identified template structure in FormBuilderFieldSettingsModal.vue
|
||||
- [x] Located correct insertion point for dynamic-list template
|
||||
- [ ] **IN PROGRESS**: Complete template section with all 10 enhanced properties
|
||||
- [ ] **PENDING**: Test configuration panel functionality
|
||||
- [ ] **PENDING**: Verify all settings are properly bound to component
|
||||
|
||||
**Phase 4: Complete Integration** ❌ Not Started
|
||||
- [ ] Full end-to-end testing of configuration panel
|
||||
- [ ] Migration of other enhanced components
|
||||
- [ ] Documentation updates for configuration system
|
||||
- [ ] Performance testing with enhanced components
|
||||
|
||||
#### Current Configuration Template Structure
|
||||
```javascript
|
||||
// FormBuilderFieldSettingsModal.vue - Template Section
|
||||
<template v-else-if="selectedComponent.type === 'dynamic-list'">
|
||||
<!-- Basic Settings (✅ Completed) -->
|
||||
<div class="space-y-4">
|
||||
<!-- Label, Name, Placeholder, Help, Width -->
|
||||
|
||||
<!-- Enhanced Settings (🟡 In Progress) -->
|
||||
<!-- Item Validation Settings -->
|
||||
<!-- Uniqueness and Type Settings -->
|
||||
<!-- Search and Bulk Operations -->
|
||||
<!-- Import/Export Configuration -->
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 📊 **Development Metrics**
|
||||
|
||||
#### Code Changes Summary
|
||||
- **Files Modified**: 4 primary files
|
||||
- **Lines Added**: ~800 lines of new functionality
|
||||
- **Components Enhanced**: 1 (Dynamic List)
|
||||
- **New Properties Added**: 10 essential settings
|
||||
- **Features Implemented**: 7 major feature categories
|
||||
|
||||
#### Time Investment
|
||||
- **Analysis and Discovery**: ~2 hours
|
||||
- **Feature Implementation**: ~4 hours
|
||||
- **Configuration Migration**: ~2 hours (ongoing)
|
||||
- **Documentation**: ~1 hour
|
||||
- **Total Session Time**: ~9 hours
|
||||
|
||||
#### Quality Assurance
|
||||
- **Manual Testing**: ✅ Comprehensive
|
||||
- **Code Review**: ✅ Self-reviewed
|
||||
- **Documentation**: ✅ Comprehensive
|
||||
- **Integration Testing**: 🟡 Partial (pending configuration panel completion)
|
||||
|
||||
---
|
||||
|
||||
### 🎯 **Next Development Session Priorities**
|
||||
|
||||
#### Immediate Tasks (Next Session)
|
||||
1. **Complete Dynamic List Configuration Integration**
|
||||
- [ ] Finish template section in FormBuilderFieldSettingsModal.vue
|
||||
- [ ] Test all 10 enhanced properties in configuration panel
|
||||
- [ ] Verify proper data binding and reactivity
|
||||
|
||||
2. **Configuration System Unification Planning**
|
||||
- [ ] Evaluate long-term strategy for dual configuration system
|
||||
- [ ] Plan migration approach for other enhanced components
|
||||
- [ ] Document recommended architecture going forward
|
||||
|
||||
#### Short-term Development (1-2 weeks)
|
||||
1. **Enhanced Validation System**
|
||||
- [ ] Implement pattern matching validation
|
||||
- [ ] Add real-time validation feedback
|
||||
- [ ] Create custom error message templates
|
||||
|
||||
2. **Accessibility Foundation**
|
||||
- [ ] Add ARIA labels to all components
|
||||
- [ ] Implement keyboard navigation
|
||||
- [ ] Create accessibility validation tool
|
||||
|
||||
#### Medium-term Goals (1-3 months)
|
||||
1. **Enhanced Select Component** - Searchable dropdown, multi-select
|
||||
2. **File Upload Improvements** - Progress indicators, previews
|
||||
3. **Rich Text Components** - WYSIWYG editor integration
|
||||
|
||||
---
|
||||
|
||||
### 🏆 **Session Achievements**
|
||||
|
||||
✅ **Major Accomplishments**:
|
||||
- Identified and analyzed critical architectural issue
|
||||
- Enhanced Dynamic List Component with 10 professional-grade features
|
||||
- Created comprehensive validation and UI system
|
||||
- Started migration to resolve configuration panel issue
|
||||
- Updated all project documentation with detailed technical information
|
||||
|
||||
✅ **Code Quality**:
|
||||
- Maintained existing code patterns and conventions
|
||||
- Implemented robust error handling and validation
|
||||
- Added comprehensive inline documentation
|
||||
- Followed Vue 3 Composition API best practices
|
||||
|
||||
✅ **User Experience**:
|
||||
- Transformed basic list into professional data collection tool
|
||||
- Added intuitive bulk operations and search functionality
|
||||
- Implemented real-time validation with clear error messaging
|
||||
- Created import/export functionality for data management
|
||||
|
||||
**Impact**: This development session significantly enhanced the Form Builder's capabilities, particularly for data collection workflows, while also identifying and beginning to resolve a critical architectural issue that was preventing feature enhancements from reaching users.
|
||||
|
||||
---
|
||||
|
||||
*This changelog will be updated with each development session to track continued progress and improvements.*
|
@ -1,134 +0,0 @@
|
||||
# Form History & Versioning System
|
||||
|
||||
## Overview
|
||||
|
||||
The form history system provides automatic versioning for all forms, allowing users to view previous versions and restore to any point in time. Every time a user saves a form, the previous version is automatically archived in the `formHistory` table.
|
||||
|
||||
## Database Schema
|
||||
|
||||
### formHistory Table
|
||||
- `historyID` - Primary key for the history entry
|
||||
- `formID` - Foreign key linking to the main form
|
||||
- `formUUID` - UUID of the form for easy reference
|
||||
- `formName` - Name of the form at the time of save
|
||||
- `formDescription` - Description at the time of save
|
||||
- `formComponents` - JSON of all form components at the time of save
|
||||
- `formStatus` - Status (active/inactive) at the time of save
|
||||
- `customCSS` - Custom CSS code at the time of save
|
||||
- `customScript` - Custom JavaScript code at the time of save
|
||||
- `formEvents` - Form events configuration at the time of save
|
||||
- `scriptMode` - Script execution mode (safe/advanced) at the time of save
|
||||
- `versionNumber` - Sequential version number (1, 2, 3...)
|
||||
- `changeDescription` - Optional description of changes made
|
||||
- `savedBy` - User ID who saved this version
|
||||
- `savedDate` - Timestamp when this version was saved
|
||||
|
||||
## How It Works
|
||||
|
||||
### Automatic Versioning
|
||||
1. When a user clicks "Save" on a form
|
||||
2. The system first retrieves the current form data
|
||||
3. The current form data is saved to `formHistory` with the next sequential version number
|
||||
4. The form is then updated with the new data
|
||||
5. Each save creates a complete snapshot of the form state
|
||||
|
||||
### Version Management
|
||||
- **Version Numbers**: Sequential integers starting from 1
|
||||
- **Change Descriptions**: Optional descriptions that can be added when saving
|
||||
- **User Tracking**: Each version records who made the changes
|
||||
- **Complete Snapshots**: Each version contains all form data, not just changes
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Get Form History
|
||||
```
|
||||
GET /api/forms/{formId}/history
|
||||
```
|
||||
Returns all versions of a form with metadata.
|
||||
|
||||
### Get Specific Version
|
||||
```
|
||||
GET /api/forms/{formId}/version/{versionId}
|
||||
```
|
||||
Returns details of a specific version. `versionId` can be either the `historyID` or `versionNumber`.
|
||||
|
||||
### Restore Version
|
||||
```
|
||||
POST /api/forms/{formId}/restore
|
||||
```
|
||||
Body:
|
||||
```json
|
||||
{
|
||||
"versionNumber": 5,
|
||||
"restoredBy": 1,
|
||||
"changeDescription": "Restored to working version"
|
||||
}
|
||||
```
|
||||
|
||||
## Frontend Features
|
||||
|
||||
### History Modal
|
||||
- **Version List**: Shows all versions with timestamps and change descriptions
|
||||
- **Preview**: Users can preview any version before restoring
|
||||
- **Restore**: One-click restore to any previous version
|
||||
- **User Information**: Shows who made each change
|
||||
|
||||
### History Button
|
||||
- Appears in the form builder header next to "Templates"
|
||||
- Only visible for saved forms (not new forms)
|
||||
- Opens the history modal for the current form
|
||||
|
||||
## Store Functions
|
||||
|
||||
### Form Builder Store Methods
|
||||
- `getFormHistory(formId)` - Get all versions of a form
|
||||
- `getFormVersion(formId, versionId)` - Get specific version details
|
||||
- `restoreFormVersion(formId, versionData)` - Restore to a specific version
|
||||
- `loadFormVersionPreview(formId, versionId)` - Load version for preview only
|
||||
- `setChangeDescription(description)` - Set description for next save
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Save with Description
|
||||
```javascript
|
||||
// Set optional change description
|
||||
formStore.setChangeDescription("Added validation to email field");
|
||||
|
||||
// Save form (automatically creates history entry)
|
||||
await formStore.saveForm();
|
||||
```
|
||||
|
||||
### View Form History
|
||||
```javascript
|
||||
const history = await formStore.getFormHistory(formId);
|
||||
console.log(`Form has ${history.totalVersions} versions`);
|
||||
```
|
||||
|
||||
### Restore to Previous Version
|
||||
```javascript
|
||||
const versionToRestore = history.history[2]; // Version 3
|
||||
await formStore.restoreFormVersion(formId, versionToRestore, "Reverted problematic changes");
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Never Lose Data**: Every form state is preserved
|
||||
2. **Easy Rollback**: One-click restore to any previous version
|
||||
3. **Change Tracking**: See who made changes and when
|
||||
4. **Preview Before Restore**: View any version before making it current
|
||||
5. **Audit Trail**: Complete history of all form modifications
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- **Storage Efficiency**: Each version stores complete form data for simplicity and reliability
|
||||
- **Cascade Deletion**: When a form is deleted, all its history is automatically removed
|
||||
- **Foreign Key Constraints**: Ensures data integrity between forms and history
|
||||
- **Indexing**: Optimized for fast lookups by form, date, and UUID
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- **Diff View**: Show differences between versions
|
||||
- **Branch/Merge**: Allow creating branches of forms
|
||||
- **Bulk Operations**: Restore multiple forms to specific dates
|
||||
- **Export History**: Download form history as JSON/CSV
|
||||
- **Retention Policy**: Automatic cleanup of old versions
|
@ -1,767 +0,0 @@
|
||||
# Form Builder JavaScript API Reference
|
||||
|
||||
## Overview
|
||||
|
||||
The Form Builder JavaScript API enables developers to create dynamic, interactive forms with real-time calculations, conditional logic, and automated field updates. This API is powered by the FormScriptEngine component that executes JavaScript code safely within form contexts.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Form Script Structure
|
||||
|
||||
```javascript
|
||||
// Form initialization - runs once when form loads
|
||||
onLoad: function() {
|
||||
console.log('Form is loading...');
|
||||
|
||||
// Set default values
|
||||
setField('status', 'new');
|
||||
setField('date_created', new Date().toISOString().split('T')[0]);
|
||||
}
|
||||
|
||||
// Field change handler - runs when any field value changes
|
||||
onFieldChange: function(fieldName, value) {
|
||||
console.log('Field changed:', fieldName, '=', value);
|
||||
|
||||
// Handle specific field changes
|
||||
if (fieldName === 'quantity' || fieldName === 'price') {
|
||||
updateTotal();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions (optional)
|
||||
function updateTotal() {
|
||||
const quantity = getField('quantity') || 0;
|
||||
const price = getField('price') || 0;
|
||||
const total = quantity * price;
|
||||
|
||||
setField('total', total.toFixed(2));
|
||||
}
|
||||
```
|
||||
|
||||
## Core Functions
|
||||
|
||||
### setField(fieldName, value)
|
||||
|
||||
Updates a form field value and triggers UI refresh.
|
||||
|
||||
**Parameters:**
|
||||
- `fieldName` (string): The name/ID of the form field to update
|
||||
- `value` (any): The new value to set for the field
|
||||
|
||||
**Returns:** `void`
|
||||
|
||||
**Examples:**
|
||||
```javascript
|
||||
// Set text field
|
||||
setField('user_name', 'John Doe');
|
||||
|
||||
// Set number field
|
||||
setField('age', 25);
|
||||
|
||||
// Set date field
|
||||
setField('birth_date', '1999-01-15');
|
||||
|
||||
// Set boolean field
|
||||
setField('is_active', true);
|
||||
|
||||
// Set calculated field
|
||||
setField('total_amount', 149.99);
|
||||
|
||||
// Clear field
|
||||
setField('notes', '');
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Field names are case-sensitive and must match the form field's `name` attribute exactly
|
||||
- Setting a field value will trigger the `onFieldChange` handler for that field
|
||||
- Values are automatically converted to appropriate types based on field type
|
||||
|
||||
### getField(fieldName)
|
||||
|
||||
Retrieves the current value of a form field.
|
||||
|
||||
**Parameters:**
|
||||
- `fieldName` (string): The name/ID of the form field to retrieve
|
||||
|
||||
**Returns:** The current field value (type depends on field type)
|
||||
|
||||
**Examples:**
|
||||
```javascript
|
||||
// Get text field value
|
||||
const userName = getField('user_name'); // Returns string
|
||||
|
||||
// Get number field value with default
|
||||
const quantity = getField('quantity') || 0; // Returns number or 0
|
||||
|
||||
// Get date field value
|
||||
const birthDate = getField('birth_date'); // Returns string in YYYY-MM-DD format
|
||||
|
||||
// Get boolean field value
|
||||
const isActive = getField('is_active'); // Returns boolean
|
||||
|
||||
// Check if field exists and has value
|
||||
const email = getField('email');
|
||||
if (email) {
|
||||
console.log('Email provided:', email);
|
||||
}
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Returns `undefined` if field doesn't exist
|
||||
- Returns empty string `''` for empty text fields
|
||||
- Returns `false` for unchecked boolean fields
|
||||
- Use logical OR `||` to provide default values for empty fields
|
||||
|
||||
## Event Handlers
|
||||
|
||||
### onLoad
|
||||
|
||||
Executes once when the form initially loads. Use for initialization, setting default values, and performing initial calculations.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Initialization code here
|
||||
}
|
||||
```
|
||||
|
||||
**Common Use Cases:**
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Set default values
|
||||
setField('country', 'United States');
|
||||
setField('currency', 'USD');
|
||||
setField('created_date', new Date().toISOString().split('T')[0]);
|
||||
|
||||
// Initialize calculated fields
|
||||
setField('subtotal', 0);
|
||||
setField('tax_amount', 0);
|
||||
setField('total', 0);
|
||||
|
||||
// Set up initial state
|
||||
const userRole = getField('user_role');
|
||||
if (userRole === 'admin') {
|
||||
setField('permissions', 'full_access');
|
||||
}
|
||||
|
||||
// Perform initial calculations
|
||||
calculateTotals();
|
||||
}
|
||||
```
|
||||
|
||||
### onFieldChange
|
||||
|
||||
Executes whenever a user changes any field value. Receives the field name and new value as parameters.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
// Handle field changes here
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `fieldName` (string): Name of the field that changed
|
||||
- `value` (any): New value of the field
|
||||
|
||||
**Common Use Cases:**
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
console.log('Field updated:', fieldName, '=', value);
|
||||
|
||||
// Handle specific fields
|
||||
switch(fieldName) {
|
||||
case 'quantity':
|
||||
case 'unit_price':
|
||||
calculateLineTotal();
|
||||
break;
|
||||
|
||||
case 'country':
|
||||
updateTaxRate(value);
|
||||
updateShippingOptions(value);
|
||||
break;
|
||||
|
||||
case 'subscription_type':
|
||||
updateFeatures(value);
|
||||
break;
|
||||
|
||||
case 'birth_date':
|
||||
calculateAge(value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle multiple related fields
|
||||
if (['first_name', 'last_name'].includes(fieldName)) {
|
||||
updateFullName();
|
||||
}
|
||||
|
||||
// Conditional logic
|
||||
if (fieldName === 'has_insurance' && value === true) {
|
||||
setField('insurance_provider', '');
|
||||
setField('policy_number', '');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Practical Examples
|
||||
|
||||
### Invoice Calculator
|
||||
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Initialize invoice
|
||||
setField('invoice_number', 'INV-' + Date.now());
|
||||
setField('invoice_date', new Date().toISOString().split('T')[0]);
|
||||
setField('tax_rate', 8.5); // 8.5% default tax
|
||||
|
||||
// Initialize line items
|
||||
setField('quantity', 1);
|
||||
setField('unit_price', 0);
|
||||
setField('subtotal', 0);
|
||||
setField('tax_amount', 0);
|
||||
setField('total', 0);
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
// Recalculate when quantity, price, or tax rate changes
|
||||
if (['quantity', 'unit_price', 'tax_rate'].includes(fieldName)) {
|
||||
const quantity = parseFloat(getField('quantity')) || 0;
|
||||
const unitPrice = parseFloat(getField('unit_price')) || 0;
|
||||
const taxRate = parseFloat(getField('tax_rate')) || 0;
|
||||
|
||||
const subtotal = quantity * unitPrice;
|
||||
const taxAmount = (subtotal * taxRate) / 100;
|
||||
const total = subtotal + taxAmount;
|
||||
|
||||
setField('subtotal', subtotal.toFixed(2));
|
||||
setField('tax_amount', taxAmount.toFixed(2));
|
||||
setField('total', total.toFixed(2));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### User Registration with Validation
|
||||
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
setField('registration_date', new Date().toISOString().split('T')[0]);
|
||||
setField('account_status', 'pending');
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
// Email validation
|
||||
if (fieldName === 'email') {
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const isValid = emailPattern.test(value);
|
||||
|
||||
setField('email_valid', isValid);
|
||||
setField('email_status', isValid ? 'Valid' : 'Invalid email format');
|
||||
}
|
||||
|
||||
// Password strength
|
||||
if (fieldName === 'password') {
|
||||
let strength = 'Weak';
|
||||
|
||||
if (value.length >= 8) {
|
||||
if (/[A-Z]/.test(value) && /[a-z]/.test(value) && /\d/.test(value)) {
|
||||
strength = 'Strong';
|
||||
} else {
|
||||
strength = 'Medium';
|
||||
}
|
||||
}
|
||||
|
||||
setField('password_strength', strength);
|
||||
}
|
||||
|
||||
// Full name combination
|
||||
if (['first_name', 'last_name'].includes(fieldName)) {
|
||||
const firstName = getField('first_name') || '';
|
||||
const lastName = getField('last_name') || '';
|
||||
const fullName = (firstName + ' ' + lastName).trim();
|
||||
|
||||
setField('full_name', fullName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### E-commerce Order Form
|
||||
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
setField('order_date', new Date().toISOString().split('T')[0]);
|
||||
setField('shipping_method', 'standard');
|
||||
setField('gift_wrap', false);
|
||||
|
||||
// Initialize pricing
|
||||
updateShipping();
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
// Product selection
|
||||
if (fieldName === 'product') {
|
||||
const prices = {
|
||||
'laptop': 999.99,
|
||||
'mouse': 29.99,
|
||||
'keyboard': 79.99,
|
||||
'monitor': 299.99
|
||||
};
|
||||
|
||||
const price = prices[value] || 0;
|
||||
setField('unit_price', price);
|
||||
}
|
||||
|
||||
// Quantity and pricing updates
|
||||
if (['quantity', 'unit_price'].includes(fieldName)) {
|
||||
const quantity = parseFloat(getField('quantity')) || 0;
|
||||
const unitPrice = parseFloat(getField('unit_price')) || 0;
|
||||
const itemTotal = quantity * unitPrice;
|
||||
|
||||
setField('item_total', itemTotal.toFixed(2));
|
||||
updateOrderTotal();
|
||||
}
|
||||
|
||||
// Shipping calculation
|
||||
if (['shipping_method', 'country'].includes(fieldName)) {
|
||||
updateShipping();
|
||||
}
|
||||
|
||||
// Gift wrap fee
|
||||
if (fieldName === 'gift_wrap') {
|
||||
const giftWrapFee = value ? 9.99 : 0;
|
||||
setField('gift_wrap_fee', giftWrapFee.toFixed(2));
|
||||
updateOrderTotal();
|
||||
}
|
||||
}
|
||||
|
||||
function updateShipping() {
|
||||
const method = getField('shipping_method');
|
||||
const country = getField('country');
|
||||
|
||||
let shippingCost = 0;
|
||||
|
||||
if (country === 'US') {
|
||||
switch(method) {
|
||||
case 'standard': shippingCost = 5.99; break;
|
||||
case 'express': shippingCost = 14.99; break;
|
||||
case 'overnight': shippingCost = 24.99; break;
|
||||
}
|
||||
} else {
|
||||
switch(method) {
|
||||
case 'standard': shippingCost = 19.99; break;
|
||||
case 'express': shippingCost = 39.99; break;
|
||||
}
|
||||
}
|
||||
|
||||
setField('shipping_cost', shippingCost.toFixed(2));
|
||||
updateOrderTotal();
|
||||
}
|
||||
|
||||
function updateOrderTotal() {
|
||||
const itemTotal = parseFloat(getField('item_total')) || 0;
|
||||
const shippingCost = parseFloat(getField('shipping_cost')) || 0;
|
||||
const giftWrapFee = parseFloat(getField('gift_wrap_fee')) || 0;
|
||||
|
||||
const orderTotal = itemTotal + shippingCost + giftWrapFee;
|
||||
setField('order_total', orderTotal.toFixed(2));
|
||||
}
|
||||
```
|
||||
|
||||
### Loan Calculator
|
||||
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Set default loan parameters
|
||||
setField('loan_amount', 100000);
|
||||
setField('interest_rate', 4.5);
|
||||
setField('loan_term_years', 30);
|
||||
|
||||
// Calculate initial payment
|
||||
calculateLoanPayment();
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (['loan_amount', 'interest_rate', 'loan_term_years'].includes(fieldName)) {
|
||||
calculateLoanPayment();
|
||||
}
|
||||
}
|
||||
|
||||
function calculateLoanPayment() {
|
||||
const principal = parseFloat(getField('loan_amount')) || 0;
|
||||
const annualRate = parseFloat(getField('interest_rate')) || 0;
|
||||
const years = parseFloat(getField('loan_term_years')) || 0;
|
||||
|
||||
if (principal > 0 && annualRate > 0 && years > 0) {
|
||||
const monthlyRate = (annualRate / 100) / 12;
|
||||
const numberOfPayments = years * 12;
|
||||
|
||||
const monthlyPayment = principal *
|
||||
(monthlyRate * Math.pow(1 + monthlyRate, numberOfPayments)) /
|
||||
(Math.pow(1 + monthlyRate, numberOfPayments) - 1);
|
||||
|
||||
const totalPayments = monthlyPayment * numberOfPayments;
|
||||
const totalInterest = totalPayments - principal;
|
||||
|
||||
setField('monthly_payment', monthlyPayment.toFixed(2));
|
||||
setField('total_payments', totalPayments.toFixed(2));
|
||||
setField('total_interest', totalInterest.toFixed(2));
|
||||
} else {
|
||||
setField('monthly_payment', '0.00');
|
||||
setField('total_payments', '0.00');
|
||||
setField('total_interest', '0.00');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Global Objects
|
||||
|
||||
### Math Object
|
||||
Full JavaScript Math object with all methods:
|
||||
|
||||
```javascript
|
||||
// Common math operations
|
||||
const rounded = Math.round(getField('price') * 1.1);
|
||||
const randomId = Math.floor(Math.random() * 1000000);
|
||||
const maxValue = Math.max(getField('value1'), getField('value2'));
|
||||
|
||||
// Advanced math
|
||||
const power = Math.pow(getField('base'), getField('exponent'));
|
||||
const squareRoot = Math.sqrt(getField('area'));
|
||||
```
|
||||
|
||||
### Date Object
|
||||
Full JavaScript Date object for date/time operations:
|
||||
|
||||
```javascript
|
||||
// Current date/time
|
||||
const now = new Date();
|
||||
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
|
||||
|
||||
// Date calculations
|
||||
const birthDate = new Date(getField('birth_date'));
|
||||
const age = Math.floor((Date.now() - birthDate.getTime()) / (365.25 * 24 * 60 * 60 * 1000));
|
||||
|
||||
// Date formatting
|
||||
const formatted = new Date().toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
```
|
||||
|
||||
### String Methods
|
||||
Standard JavaScript string methods:
|
||||
|
||||
```javascript
|
||||
// String manipulation
|
||||
const name = getField('user_name').toUpperCase();
|
||||
const initials = getField('first_name').charAt(0) + getField('last_name').charAt(0);
|
||||
const slug = getField('title').toLowerCase().replace(/\s+/g, '-');
|
||||
```
|
||||
|
||||
### Number Parsing
|
||||
Standard parsing functions:
|
||||
|
||||
```javascript
|
||||
// Parse numbers
|
||||
const quantity = parseInt(getField('quantity_str')) || 0;
|
||||
const price = parseFloat(getField('price_str')) || 0.0;
|
||||
|
||||
// Validation
|
||||
const isValidNumber = !isNaN(parseFloat(getField('amount')));
|
||||
```
|
||||
|
||||
### Console Logging
|
||||
Console object for debugging:
|
||||
|
||||
```javascript
|
||||
// Debug logging
|
||||
console.log('Processing field:', fieldName, 'with value:', value);
|
||||
console.warn('Invalid input detected:', value);
|
||||
console.error('Calculation failed:', error);
|
||||
|
||||
// Grouped logging
|
||||
console.group('Order Calculation');
|
||||
console.log('Subtotal:', subtotal);
|
||||
console.log('Tax:', tax);
|
||||
console.log('Total:', total);
|
||||
console.groupEnd();
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Error Handling
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
try {
|
||||
if (fieldName === 'price') {
|
||||
const price = parseFloat(value);
|
||||
|
||||
if (isNaN(price)) {
|
||||
console.warn('Invalid price value:', value);
|
||||
setField('price_error', 'Please enter a valid number');
|
||||
return;
|
||||
}
|
||||
|
||||
if (price < 0) {
|
||||
console.warn('Negative price not allowed:', price);
|
||||
setField('price_error', 'Price cannot be negative');
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear error and proceed
|
||||
setField('price_error', '');
|
||||
calculateTotal(price);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error processing field change:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Optimization
|
||||
```javascript
|
||||
// Cache calculations
|
||||
let cachedTaxRate = null;
|
||||
|
||||
function getTaxRate(country) {
|
||||
if (cachedTaxRate === null) {
|
||||
const rates = {
|
||||
'US': 8.5,
|
||||
'CA': 12.0,
|
||||
'UK': 20.0
|
||||
};
|
||||
cachedTaxRate = rates[country] || 0;
|
||||
}
|
||||
return cachedTaxRate;
|
||||
}
|
||||
|
||||
// Minimize calculations
|
||||
onFieldChange: function(fieldName, value) {
|
||||
// Only calculate when relevant fields change
|
||||
if (['quantity', 'price', 'tax_rate'].includes(fieldName)) {
|
||||
// Batch related calculations
|
||||
updateAllTotals();
|
||||
}
|
||||
|
||||
// Avoid recalculating if value hasn't actually changed
|
||||
const currentTotal = getField('total');
|
||||
const newTotal = calculateNewTotal();
|
||||
|
||||
if (currentTotal !== newTotal) {
|
||||
setField('total', newTotal);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Data Validation
|
||||
```javascript
|
||||
// Validation helper functions
|
||||
function isValidEmail(email) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
}
|
||||
|
||||
function isValidPhone(phone) {
|
||||
return /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/.test(phone);
|
||||
}
|
||||
|
||||
function validateCreditCard(number) {
|
||||
// Luhn algorithm
|
||||
const digits = number.replace(/\D/g, '');
|
||||
let sum = 0;
|
||||
let isEven = false;
|
||||
|
||||
for (let i = digits.length - 1; i >= 0; i--) {
|
||||
let digit = parseInt(digits[i]);
|
||||
|
||||
if (isEven) {
|
||||
digit *= 2;
|
||||
if (digit > 9) digit -= 9;
|
||||
}
|
||||
|
||||
sum += digit;
|
||||
isEven = !isEven;
|
||||
}
|
||||
|
||||
return sum % 10 === 0;
|
||||
}
|
||||
|
||||
// Use in field change handler
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (fieldName === 'email') {
|
||||
const valid = isValidEmail(value);
|
||||
setField('email_valid', valid);
|
||||
setField('email_message', valid ? 'Valid email' : 'Invalid email format');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Conditional Field Display
|
||||
```javascript
|
||||
// Simulate showing/hiding fields by setting values
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (fieldName === 'account_type') {
|
||||
if (value === 'business') {
|
||||
setField('company_name', '');
|
||||
setField('tax_id', '');
|
||||
setField('show_business_fields', 'true');
|
||||
} else {
|
||||
setField('show_business_fields', 'false');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-step Calculations
|
||||
```javascript
|
||||
// Break complex calculations into steps
|
||||
function calculateShippingCost() {
|
||||
const weight = parseFloat(getField('package_weight')) || 0;
|
||||
const distance = parseFloat(getField('shipping_distance')) || 0;
|
||||
const method = getField('shipping_method');
|
||||
|
||||
// Step 1: Base cost by weight
|
||||
let baseCost = 0;
|
||||
if (weight <= 1) baseCost = 5.99;
|
||||
else if (weight <= 5) baseCost = 12.99;
|
||||
else baseCost = 5.99 + (weight - 1) * 2.50;
|
||||
|
||||
// Step 2: Distance multiplier
|
||||
let distanceMultiplier = 1;
|
||||
if (distance > 500) distanceMultiplier = 1.5;
|
||||
else if (distance > 100) distanceMultiplier = 1.2;
|
||||
|
||||
// Step 3: Method adjustment
|
||||
let methodMultiplier = 1;
|
||||
switch(method) {
|
||||
case 'express': methodMultiplier = 2; break;
|
||||
case 'overnight': methodMultiplier = 3; break;
|
||||
}
|
||||
|
||||
// Final calculation
|
||||
const finalCost = baseCost * distanceMultiplier * methodMultiplier;
|
||||
|
||||
setField('shipping_cost', finalCost.toFixed(2));
|
||||
setField('shipping_breakdown', `Base: $${baseCost} × Distance: ${distanceMultiplier} × Method: ${methodMultiplier}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Form State Management
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Initialize form state
|
||||
setField('form_state', 'initialized');
|
||||
setField('validation_errors', '');
|
||||
setField('completion_percentage', 0);
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
// Update completion percentage
|
||||
updateCompletionPercentage();
|
||||
|
||||
// Track form state
|
||||
setField('form_state', 'editing');
|
||||
setField('last_modified', new Date().toISOString());
|
||||
}
|
||||
|
||||
function updateCompletionPercentage() {
|
||||
const requiredFields = ['first_name', 'last_name', 'email', 'phone'];
|
||||
let completedFields = 0;
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
if (getField(field) && getField(field).trim() !== '') {
|
||||
completedFields++;
|
||||
}
|
||||
});
|
||||
|
||||
const percentage = Math.round((completedFields / requiredFields.length) * 100);
|
||||
setField('completion_percentage', percentage);
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging and Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**1. Field names don't match**
|
||||
```javascript
|
||||
// ❌ Wrong - case sensitive
|
||||
setField('User_Name', 'John'); // Field is actually 'user_name'
|
||||
|
||||
// ✅ Correct
|
||||
setField('user_name', 'John');
|
||||
```
|
||||
|
||||
**2. Missing error handling**
|
||||
```javascript
|
||||
// ❌ Without error handling
|
||||
const price = parseFloat(getField('price'));
|
||||
const total = price * quantity; // Will fail if price is invalid
|
||||
|
||||
// ✅ With error handling
|
||||
const price = parseFloat(getField('price')) || 0;
|
||||
const quantity = parseFloat(getField('quantity')) || 0;
|
||||
const total = price * quantity;
|
||||
```
|
||||
|
||||
**3. Infinite loops in onFieldChange**
|
||||
```javascript
|
||||
// ❌ Can cause infinite loop
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (fieldName === 'total') {
|
||||
setField('total', value * 1.1); // This will trigger onFieldChange again!
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Avoid triggering changes for calculated fields
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (fieldName === 'quantity' || fieldName === 'price') {
|
||||
const total = getField('quantity') * getField('price');
|
||||
setField('total', total); // Only update when input fields change
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Debug Techniques
|
||||
|
||||
```javascript
|
||||
// Enable detailed logging
|
||||
onFieldChange: function(fieldName, value) {
|
||||
console.group(`Field Change: ${fieldName}`);
|
||||
console.log('New Value:', value);
|
||||
console.log('Current Form Data:', {
|
||||
quantity: getField('quantity'),
|
||||
price: getField('price'),
|
||||
total: getField('total')
|
||||
});
|
||||
|
||||
// Your calculation logic here
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
// Add validation checkpoints
|
||||
function calculateTotal() {
|
||||
const quantity = getField('quantity');
|
||||
const price = getField('price');
|
||||
|
||||
console.log('Calculation inputs:', { quantity, price });
|
||||
|
||||
if (isNaN(quantity) || isNaN(price)) {
|
||||
console.error('Invalid inputs for calculation');
|
||||
return;
|
||||
}
|
||||
|
||||
const total = quantity * price;
|
||||
console.log('Calculated total:', total);
|
||||
|
||||
setField('total', total.toFixed(2));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*This API reference covers all available JavaScript functionality in the Form Builder. For implementation examples and integration details, see the [Technical Guide](TECHNICAL_GUIDE.md) and [User Guide](USER_GUIDE.md).*
|
||||
|
||||
Last updated: December 2024
|
@ -1,405 +0,0 @@
|
||||
# Form Builder Development Roadmap
|
||||
|
||||
This document outlines the planned improvements and missing essential features for the Corrad ProcessMaker Form Builder. It serves as a development checklist to track progress on enhancing form building capabilities.
|
||||
|
||||
## Current Status
|
||||
|
||||
**Last Updated**: December 2024
|
||||
|
||||
**Current Version**: v2.0 with JavaScript API and dynamic calculations
|
||||
|
||||
### Recently Completed Features ✅
|
||||
- JavaScript Execution Engine (`FormScriptEngine`) for real-time calculations
|
||||
- Dynamic field updates with `onFieldChange` and `onLoad` event handlers
|
||||
- Enhanced debugging with comprehensive logging
|
||||
- API functions: `setField()`, `getField()` for programmatic form control
|
||||
- Grid system for responsive layouts
|
||||
- Form versioning and management
|
||||
- Security improvements with sandboxed JavaScript execution
|
||||
- **NEW**: Dynamic List Component comprehensive enhancements with validation, search, and bulk operations
|
||||
- **NEW**: Configuration panel issue identified and partially resolved
|
||||
|
||||
---
|
||||
|
||||
## Recent Development Session Highlights (December 2024)
|
||||
|
||||
### 🔍 **Critical Discovery: Dual Configuration System Issue**
|
||||
**Status**: 🟡 Partially Resolved
|
||||
**Issue**: Two separate configuration systems discovered:
|
||||
1. `FormBuilderConfiguration.vue` - Enhanced but not actively used
|
||||
2. `FormBuilderFieldSettingsModal.vue` - Actually used by form builder
|
||||
|
||||
**Work Completed**:
|
||||
- ✅ Identified root cause of missing configuration panels
|
||||
- ✅ Enhanced FormBuilderConfiguration.vue (not in use)
|
||||
- ✅ Started migration to FormBuilderFieldSettingsModal.vue
|
||||
- 🟡 Added dynamic-list to field configurations (label, name, placeholder, help, width)
|
||||
- 🟡 Added dynamic-list to hasSpecificSettings types
|
||||
- 🟡 Added icon, type name, and description for dynamic-list
|
||||
- ❌ **IN PROGRESS**: Adding template section for dynamic-list specific settings
|
||||
|
||||
**Next Steps**:
|
||||
- [ ] Complete dynamic-list template section in FormBuilderFieldSettingsModal.vue
|
||||
- [ ] Migrate other enhanced components from FormBuilderConfiguration.vue
|
||||
- [ ] Consider unifying the dual configuration system
|
||||
|
||||
---
|
||||
|
||||
## Missing Essential Settings Analysis
|
||||
|
||||
### 🔴 Critical Priority (Must Have)
|
||||
|
||||
#### **1. Conditional Logic System**
|
||||
**Status**: ✅ Completed December 2024
|
||||
**Components Affected**: All form components
|
||||
**Description**: Essential for dynamic forms that show/hide fields based on user input
|
||||
|
||||
**Implemented Features**:
|
||||
- ✅ Field visibility rules (show/hide based on other field values)
|
||||
- ✅ Field enabling/disabling based on conditions
|
||||
- ✅ Multi-field conditional logic (AND/OR operators)
|
||||
- ✅ Visual condition builder interface
|
||||
- ✅ Field dependency chains
|
||||
- ✅ JavaScript code generation and execution
|
||||
- ✅ Integration with existing FormScriptEngine
|
||||
- ✅ Real-time condition evaluation
|
||||
- ✅ Automatic script generation and injection
|
||||
|
||||
**Files Modified**:
|
||||
- `components/FormBuilderComponents.vue` - Added conditional logic properties
|
||||
- `components/FormBuilderFieldSettingsModal.vue` - Added condition builder interface
|
||||
- `components/ConditionalLogicEngine.vue` - New component for script generation
|
||||
- `pages/form-builder/index.vue` - Integrated conditional logic engine
|
||||
|
||||
**Impact**: High - Critical for advanced form workflows
|
||||
|
||||
---
|
||||
|
||||
#### **2. Enhanced Validation System**
|
||||
**Status**: 🟡 Partially Implemented
|
||||
**Current**: Basic required, email, URL, min/max validations
|
||||
**Components Affected**: All input components
|
||||
|
||||
**Missing Features**:
|
||||
- [ ] Pattern matching validation (regex support)
|
||||
- [ ] Custom validation functions
|
||||
- [ ] Real-time validation feedback
|
||||
- [ ] Cross-field validation (confirm password, date ranges)
|
||||
- [ ] Conditional validation rules
|
||||
- [ ] Custom error message templates
|
||||
- [ ] Validation groups and schemas
|
||||
|
||||
**Impact**: High - Essential for data quality and user experience
|
||||
|
||||
---
|
||||
|
||||
#### **3. Accessibility Features**
|
||||
**Status**: ❌ Not Implemented
|
||||
**Components Affected**: All components
|
||||
**Description**: WCAG compliance and screen reader support
|
||||
|
||||
**Missing Features**:
|
||||
- [ ] ARIA labels and descriptions
|
||||
- [ ] Keyboard navigation support
|
||||
- [ ] Screen reader announcements
|
||||
- [ ] High contrast mode support
|
||||
- [ ] Focus management and indicators
|
||||
- [ ] Alternative text for visual elements
|
||||
- [ ] Accessibility validation tool
|
||||
|
||||
**Impact**: High - Required for compliance and inclusivity
|
||||
|
||||
---
|
||||
|
||||
### 🟡 High Priority (Should Have)
|
||||
|
||||
#### **4. Dynamic List Component Enhancements**
|
||||
**Status**: ✅ Completed December 2024
|
||||
**Current Settings**: `['label', 'name', 'help', 'placeholder', 'buttonText', 'minItems', 'maxItems', 'defaultItems', 'width']`
|
||||
|
||||
**✅ COMPLETED: Essential Settings Implementation**:
|
||||
- [x] **Item validation** - Individual list item validation with type checking
|
||||
- [x] **Uniqueness validation** - Prevent duplicate items with real-time checking
|
||||
- [x] **Item type support** - Support for text, number, email, url types
|
||||
- [x] **Search/filter functionality** - Real-time search within list items
|
||||
- [x] **Bulk operations** - Select all, delete selected items
|
||||
- [x] **Import/Export functionality** - JSON, CSV, TXT format support
|
||||
- [x] **Item counter display** - Visual count of items
|
||||
- [x] **Delete confirmation** - Confirm before deleting items
|
||||
- [x] **Visual sorting indicators** - Drag handles for reordering
|
||||
- [x] **Enhanced UI with validation feedback** - Real-time error display
|
||||
|
||||
**Files Modified**:
|
||||
- ✅ `components/FormBuilderComponents.vue` - Added 10 new properties
|
||||
- ✅ `components/FormBuilderConfiguration.vue` - Added comprehensive configuration interface
|
||||
- ✅ `components/ComponentPreview.vue` - Full implementation with validation engine
|
||||
- 🟡 `components/FormBuilderFieldSettingsModal.vue` - Partial integration (IN PROGRESS)
|
||||
|
||||
**Missing Features** (moved to future roadmap):
|
||||
- [ ] Drag & drop reordering functionality (requires vue-draggable integration)
|
||||
- [ ] Custom item templates (rich formatting)
|
||||
|
||||
**Configuration Panel Issue**: ❌ Settings not showing in form builder due to dual configuration system
|
||||
**Solution**: 🟡 Migration to FormBuilderFieldSettingsModal.vue in progress
|
||||
|
||||
**Impact**: High - Critical for data collection workflows
|
||||
|
||||
---
|
||||
|
||||
#### **5. Enhanced Select Component**
|
||||
**Status**: 🟡 Partially Implemented
|
||||
**Current Settings**: `['label', 'name', 'placeholder', 'help', 'options', 'width']`
|
||||
|
||||
**Missing Essential Settings**:
|
||||
- [ ] Searchable/filterable dropdown
|
||||
- [ ] Multi-select with chips display
|
||||
- [ ] Option groups/categories (optgroups)
|
||||
- [ ] Dynamic options loading from API endpoints
|
||||
- [ ] Custom option templates (with icons, descriptions)
|
||||
- [ ] Maximum selections limit for multi-select
|
||||
- [ ] Async option loading with loading states
|
||||
|
||||
**Impact**: Medium-High - Common requirement for complex forms
|
||||
|
||||
---
|
||||
|
||||
#### **6. Rich Text and Enhanced Text Components**
|
||||
**Status**: ❌ Not Implemented
|
||||
**Components Affected**: textarea, text components
|
||||
|
||||
**Missing Features**:
|
||||
- [ ] WYSIWYG rich text editor integration
|
||||
- [ ] Character counter display
|
||||
- [ ] Auto-resize for textarea
|
||||
- [ ] Auto-complete/suggestions
|
||||
- [ ] Advanced input masks for specific formats
|
||||
- [ ] Markdown support and preview
|
||||
|
||||
**Impact**: Medium - Useful for content-heavy forms
|
||||
|
||||
---
|
||||
|
||||
#### **7. File Upload Enhancements**
|
||||
**Status**: ❌ Not Implemented
|
||||
**Current Settings**: Basic file upload with size/type restrictions
|
||||
|
||||
**Missing Features**:
|
||||
- [ ] Upload progress indicators
|
||||
- [ ] File preview thumbnails (images, documents)
|
||||
- [ ] Drag and drop file upload
|
||||
- [ ] Multiple file upload with management
|
||||
- [ ] File validation (beyond size/type)
|
||||
- [ ] Drag to reorder uploaded files
|
||||
- [ ] Cloud storage integration options
|
||||
|
||||
**Impact**: Medium-High - Essential for document workflows
|
||||
|
||||
---
|
||||
|
||||
### 🟢 Medium Priority (Nice to Have)
|
||||
|
||||
#### **8. Date/Time Component Improvements**
|
||||
**Status**: 🟡 Partially Implemented
|
||||
**Current**: Basic date picker
|
||||
|
||||
**Missing Features**:
|
||||
- [ ] Date range picker
|
||||
- [ ] Time picker with seconds
|
||||
- [ ] DateTime picker combined
|
||||
- [ ] Timezone support
|
||||
- [ ] Calendar view with blocking dates
|
||||
- [ ] Recurring date options
|
||||
- [ ] Relative date options (today + X days)
|
||||
|
||||
**Impact**: Medium - Useful for scheduling workflows
|
||||
|
||||
---
|
||||
|
||||
#### **9. Layout and Presentation Components**
|
||||
**Status**: ❌ Not Implemented
|
||||
|
||||
**Missing Components**:
|
||||
- [ ] Divider/Separator component
|
||||
- [ ] HTML/Rich Content blocks
|
||||
- [ ] Progress indicators
|
||||
- [ ] Accordion/Collapsible sections
|
||||
- [ ] Tabs for multi-section forms
|
||||
- [ ] Step indicator for multi-page forms
|
||||
- [ ] Info boxes and alerts
|
||||
|
||||
**Impact**: Medium - Enhances form presentation
|
||||
|
||||
---
|
||||
|
||||
#### **10. Number Input Enhancements**
|
||||
**Status**: 🟡 Partially Implemented
|
||||
**Current**: Basic number input with min/max
|
||||
|
||||
**Missing Features**:
|
||||
- [ ] Currency formatting with symbols
|
||||
- [ ] Percentage input with % symbol
|
||||
- [ ] Number formatting (thousands separators)
|
||||
- [ ] Increment/decrement buttons
|
||||
- [ ] Slider input for ranges
|
||||
- [ ] Calculator integration
|
||||
|
||||
**Impact**: Medium - Useful for financial forms
|
||||
|
||||
---
|
||||
|
||||
### 🔵 Low Priority (Future Enhancements)
|
||||
|
||||
#### **11. Advanced Form Features**
|
||||
**Status**: ❌ Not Implemented
|
||||
|
||||
**Features**:
|
||||
- [ ] Form templates and presets
|
||||
- [ ] Form cloning and duplication
|
||||
- [ ] Form versioning with rollback
|
||||
- [ ] Form analytics and usage stats
|
||||
- [ ] A/B testing for forms
|
||||
- [ ] Form localization/internationalization
|
||||
- [ ] Custom CSS styling per form
|
||||
|
||||
**Impact**: Low - Advanced workflow features
|
||||
|
||||
---
|
||||
|
||||
#### **12. Integration Features**
|
||||
**Status**: ❌ Not Implemented
|
||||
|
||||
**Features**:
|
||||
- [ ] Webhook integrations for form submissions
|
||||
- [ ] API endpoints for external data sources
|
||||
- [ ] Email template integration
|
||||
- [ ] PDF generation from form data
|
||||
- [ ] Signature capture component
|
||||
- [ ] QR code generation
|
||||
- [ ] Barcode scanning input
|
||||
|
||||
**Impact**: Low-Medium - Specialized workflow needs
|
||||
|
||||
---
|
||||
|
||||
## Development Timeline
|
||||
|
||||
### 🎯 Phase 1: January 2025 (Critical Fixes)
|
||||
**Goal**: Resolve configuration panel issues and complete in-progress features
|
||||
|
||||
**Priority Tasks**:
|
||||
1. ✅ **Complete Dynamic List Configuration Integration**
|
||||
- [x] Fix FormBuilderFieldSettingsModal.vue template section
|
||||
- [x] Test dynamic list settings panel functionality
|
||||
- [x] Ensure all 10 new properties are configurable
|
||||
|
||||
2. **Enhanced Validation System**
|
||||
- [ ] Implement pattern matching validation
|
||||
- [ ] Add real-time validation feedback
|
||||
- [ ] Create custom error message templates
|
||||
|
||||
3. **Accessibility Foundation**
|
||||
- [ ] Add ARIA labels to all components
|
||||
- [ ] Implement keyboard navigation
|
||||
- [ ] Create accessibility validation tool
|
||||
|
||||
### 🎯 Phase 2: February-March 2025 (High Priority Components)
|
||||
**Goal**: Complete essential component enhancements
|
||||
|
||||
1. **Enhanced Select Component**
|
||||
- [ ] Implement searchable dropdown
|
||||
- [ ] Add multi-select with chips
|
||||
- [ ] Create option groups support
|
||||
|
||||
2. **File Upload Improvements**
|
||||
- [ ] Add progress indicators
|
||||
- [ ] Implement file previews
|
||||
- [ ] Create drag & drop interface
|
||||
|
||||
3. **Rich Text Components**
|
||||
- [ ] Integrate WYSIWYG editor
|
||||
- [ ] Add character counter
|
||||
- [ ] Implement auto-resize for textarea
|
||||
|
||||
### 🎯 Phase 3: April-June 2025 (Medium Priority Features)
|
||||
**Goal**: Enhance user experience and add presentation components
|
||||
|
||||
1. **Date/Time Enhancements**
|
||||
2. **Layout Components**
|
||||
3. **Number Input Improvements**
|
||||
|
||||
### 🎯 Phase 4: July-December 2025 (Advanced Features)
|
||||
**Goal**: Add advanced workflow and integration features
|
||||
|
||||
1. **Form Templates & Analytics**
|
||||
2. **Integration Features**
|
||||
3. **Localization Support**
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Architecture Considerations
|
||||
- All new features should integrate with existing FormScriptEngine
|
||||
- Maintain compatibility with current form data structure
|
||||
- Follow existing component patterns and naming conventions
|
||||
- Ensure responsive design across all new components
|
||||
|
||||
### Development Standards
|
||||
- Use FormKit for all form controls
|
||||
- Follow Vue 3 Composition API patterns
|
||||
- Implement TypeScript interfaces for new component properties
|
||||
- Add comprehensive JSDoc documentation
|
||||
- Include unit tests for complex validation logic
|
||||
|
||||
### Testing Strategy
|
||||
- Unit tests for individual component enhancements
|
||||
- Integration tests for form builder functionality
|
||||
- E2E tests for critical user workflows
|
||||
- Accessibility testing with screen readers
|
||||
- Performance testing for large forms
|
||||
|
||||
---
|
||||
|
||||
## Progress Tracking
|
||||
|
||||
**Overall Completion**: 15% (2 of 12 major features completed)
|
||||
|
||||
### Completed ✅
|
||||
- [x] Conditional Logic System (100%)
|
||||
- [x] Dynamic List Component Enhancements (95% - configuration panel integration pending)
|
||||
|
||||
### In Progress 🟡
|
||||
- [ ] Enhanced Validation System (20%)
|
||||
- [ ] Dynamic List Configuration Panel Fix (80%)
|
||||
|
||||
### Not Started ❌
|
||||
- [ ] Accessibility Features (0%)
|
||||
- [ ] Enhanced Select Component (0%)
|
||||
- [ ] Rich Text Components (0%)
|
||||
- [ ] File Upload Enhancements (0%)
|
||||
- [ ] Date/Time Improvements (0%)
|
||||
- [ ] Layout Components (0%)
|
||||
- [ ] Number Input Enhancements (0%)
|
||||
- [ ] Advanced Form Features (0%)
|
||||
- [ ] Integration Features (0%)
|
||||
|
||||
---
|
||||
|
||||
*This roadmap is a living document that will be updated as features are completed and new requirements are identified.*
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
When implementing features from this roadmap:
|
||||
|
||||
1. Update the status from ❌ to 🟡 (In Progress) when starting
|
||||
2. Move to ✅ when completed with implementation details
|
||||
3. Update the main README.md if new documentation is added
|
||||
4. Ensure all new features have corresponding tests
|
||||
5. Update the user guide with new functionality
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Next Review**: March 2025
|
File diff suppressed because it is too large
Load Diff
@ -1,672 +0,0 @@
|
||||
# Form Builder User Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Form Builder is a modern, intuitive drag-and-drop interface for creating dynamic forms. Featuring a smart collapsible settings panel, visual grid system, and intelligent component recommendations, it makes form creation accessible to both developers and non-technical users.
|
||||
|
||||
> For technical implementation details, see the [Technical Guide](TECHNICAL_GUIDE.md)
|
||||
|
||||
## Interface Overview
|
||||
|
||||
### Three-Panel Layout
|
||||
- **Left Panel**: Component Library with categorized components
|
||||
- **Center Panel**: Form Canvas with 12-column grid system
|
||||
- **Right Panel**: Smart Collapsible Settings Panel
|
||||
|
||||
### Key Features
|
||||
- **Smart Settings Panel**: Auto-opens when components are selected, collapses to save space
|
||||
- **Visual Grid System**: Intuitive width selection with grid previews
|
||||
- **Quick Settings**: Inline editing for common properties
|
||||
- **Smart Recommendations**: Automatic width suggestions based on field type
|
||||
- **Real-time Preview**: See changes instantly as you build
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Creating Your First Form
|
||||
|
||||
1. **Start Building**
|
||||
- Navigate to the Form Builder
|
||||
- Click "New Form" or start with a template
|
||||
- The interface loads with an empty canvas
|
||||
|
||||
2. **Add Components**
|
||||
- **Drag & Drop**: Drag any component from the left panel to the canvas
|
||||
- **Click to Add**: Click a component to add it at the end of the form
|
||||
- **Smart Placement**: Components automatically find optimal grid positions
|
||||
|
||||
3. **Configure Components**
|
||||
- **Auto-Selection**: Click any component to select it
|
||||
- **Quick Settings**: The right panel automatically opens with common settings
|
||||
- **Inline Editing**: Edit labels, names, widths, and validation directly
|
||||
- **Full Settings**: Click "Full Settings" for advanced configuration
|
||||
|
||||
4. **Save & Preview**
|
||||
- **Auto-Save**: Changes are saved automatically
|
||||
- **Preview Mode**: Toggle to test your form as users will see it
|
||||
- **Form Management**: Access all saved forms from the header
|
||||
|
||||
## Smart Settings Panel System
|
||||
|
||||
### Panel Behavior
|
||||
- **Auto-Open**: Automatically expands when you select a component
|
||||
- **Collapsible**: Toggle between expanded (320px) and collapsed (48px) states
|
||||
- **Quick Access**: Common settings available without opening full modal
|
||||
- **Context-Aware**: Shows only relevant settings for each component type
|
||||
|
||||
### Quick Settings Available
|
||||
- **Label**: Component display name
|
||||
- **Field Name**: Internal identifier
|
||||
- **Width**: S/M/L/XL quick sizing buttons
|
||||
- **Required**: Toggle validation
|
||||
- **Placeholder**: Hint text (where applicable)
|
||||
|
||||
### Collapsed State Features
|
||||
- **Component Icon**: Visual indicator of selected component type
|
||||
- **Type Badge**: Short identifier (TXT, BTN, SEL, etc.)
|
||||
- **Toggle Button**: One-click expansion
|
||||
- **Space-Efficient**: Only 48px wide when collapsed
|
||||
|
||||
## Enhanced Grid System
|
||||
|
||||
### Visual Width Selection
|
||||
The new grid system replaces technical percentages with intuitive options:
|
||||
|
||||
#### Width Options
|
||||
- **Narrow (25%)**: Small inputs like age, zip codes, short codes
|
||||
- **Small (33%)**: Short text fields, city names, grouped inputs
|
||||
- **Medium (50%)**: Names, phone numbers, paired inputs
|
||||
- **Wide (75%)**: Addresses, URLs, prominent fields
|
||||
- **Full (100%)**: Text areas, headings, single-column layouts
|
||||
|
||||
#### Visual Grid Preview
|
||||
- **12-Column Grid**: Mini preview shows exactly which columns your component will occupy
|
||||
- **Active Columns**: Highlighted squares show component width
|
||||
- **Current Selection**: Visual feedback shows your current choice
|
||||
- **Grid Math**: Displays "6 of 12 columns" instead of technical details
|
||||
|
||||
### Smart Recommendations
|
||||
The system automatically suggests optimal widths based on component type:
|
||||
|
||||
- **Narrow Recommended**: Number, Date, Time, Color, OTP fields
|
||||
- **Small Recommended**: Text inputs
|
||||
- **Medium Recommended**: Email, Phone, Password, Select, Masked inputs
|
||||
- **Wide Recommended**: URL, File uploads
|
||||
- **Full Recommended**: Text areas, Headings, Choice groups, Buttons
|
||||
|
||||
#### Visual Indicators
|
||||
- **Green Ring**: Recommended options have green highlighting
|
||||
- **Selection State**: Current choice highlighted in blue
|
||||
- **Combined State**: Selected + recommended shows green background
|
||||
|
||||
## Component Library
|
||||
|
||||
### Basic Inputs
|
||||
Perfect for collecting simple data:
|
||||
|
||||
**Text Field**
|
||||
- Single-line text input
|
||||
- Smart width: Small (33%)
|
||||
- Use for: Names, titles, short answers
|
||||
- Quick settings: Label, name, placeholder, required
|
||||
|
||||
**Text Area**
|
||||
- Multi-line text input
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Comments, descriptions, long text
|
||||
- Features: Resizable, character limits
|
||||
|
||||
**Number Field**
|
||||
- Numeric input with validation
|
||||
- Smart width: Narrow (25%)
|
||||
- Use for: Age, quantity, prices
|
||||
- Features: Min/max limits, step values
|
||||
|
||||
**Email Field**
|
||||
- Email input with validation
|
||||
- Smart width: Medium (50%)
|
||||
- Use for: Contact forms, registration
|
||||
- Features: Built-in email format validation
|
||||
|
||||
**Password Field**
|
||||
- Secure password input
|
||||
- Smart width: Medium (50%)
|
||||
- Use for: Authentication forms
|
||||
- Features: Password masking, strength indicators
|
||||
|
||||
### Selection Components
|
||||
For choosing from options:
|
||||
|
||||
**Select Dropdown**
|
||||
- Single selection from options
|
||||
- Smart width: Medium (50%)
|
||||
- Use for: Countries, categories, status
|
||||
- Features: Search, custom values, option groups
|
||||
|
||||
**Checkbox Group**
|
||||
- Multiple selection options
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Preferences, multiple choices
|
||||
- Features: Select all option, layout control
|
||||
|
||||
**Radio Group**
|
||||
- Single choice from group
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Exclusive choices, yes/no questions
|
||||
- Features: Button or traditional styles
|
||||
|
||||
**Switch Toggle**
|
||||
- Boolean on/off control
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Settings, feature toggles
|
||||
- Features: Custom labels, default states
|
||||
|
||||
### Date & Time Components
|
||||
For temporal inputs:
|
||||
|
||||
**Date Picker**
|
||||
- Date selection interface
|
||||
- Smart width: Narrow (25%)
|
||||
- Use for: Birthdays, deadlines, scheduling
|
||||
- Features: Date ranges, format options, validation
|
||||
|
||||
**Time Picker**
|
||||
- Time selection interface
|
||||
- Smart width: Narrow (25%)
|
||||
- Use for: Appointments, schedules
|
||||
- Features: 12/24 hour format, minute intervals
|
||||
|
||||
**Date & Time**
|
||||
- Combined date and time picker
|
||||
- Smart width: Medium (50%)
|
||||
- Use for: Event scheduling, timestamps
|
||||
- Features: Single field for both values
|
||||
|
||||
### Advanced Components
|
||||
Specialized functionality:
|
||||
|
||||
**File Upload**
|
||||
- Standard file input
|
||||
- Smart width: Wide (75%)
|
||||
- Use for: Document uploads, attachments
|
||||
- Features: File type restrictions, size limits
|
||||
|
||||
**File Drop Zone**
|
||||
- Drag & drop upload area
|
||||
- Smart width: Wide (75%)
|
||||
- Use for: Multiple files, intuitive uploads
|
||||
- Features: Visual feedback, progress indicators
|
||||
|
||||
**OTP Input**
|
||||
- One-time password entry
|
||||
- Smart width: Narrow (25%)
|
||||
- Use for: Two-factor authentication
|
||||
- Features: Auto-focus, numeric only
|
||||
|
||||
**Masked Input**
|
||||
- Formatted text input
|
||||
- Smart width: Medium (50%)
|
||||
- Use for: Phone numbers, SSN, custom formats
|
||||
- Features: Real-time formatting, validation
|
||||
|
||||
### Layout Components
|
||||
Form structure and organization:
|
||||
|
||||
**Heading**
|
||||
- Section titles and organization
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Form sections, categories
|
||||
- Features: Multiple heading levels (H1-H6)
|
||||
|
||||
**Paragraph**
|
||||
- Descriptive text content
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Instructions, explanations
|
||||
- Features: Rich text formatting options
|
||||
|
||||
**Divider**
|
||||
- Visual section separator
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Content organization
|
||||
- Features: Various visual styles
|
||||
|
||||
**Button**
|
||||
- Action triggers
|
||||
- Smart width: Full (100%)
|
||||
- Use for: Form submission, navigation
|
||||
- Features: Custom styling, click handlers
|
||||
|
||||
## Form Configuration Workflows
|
||||
|
||||
### Quick Settings Workflow (80% of edits)
|
||||
1. Click any component to select it
|
||||
2. Settings panel auto-opens on the right
|
||||
3. Edit common properties inline:
|
||||
- Change label text
|
||||
- Adjust field width with S/M/L/XL buttons
|
||||
- Toggle required validation
|
||||
- Set placeholder text
|
||||
4. Changes apply immediately
|
||||
|
||||
### Full Settings Workflow (20% of edits)
|
||||
1. Select component in quick settings panel
|
||||
2. Click "Full Settings" button
|
||||
3. Access comprehensive modal with:
|
||||
- All component properties
|
||||
- Advanced validation rules
|
||||
- Visual width selector with grid preview
|
||||
- Component-specific options
|
||||
- Reset to defaults option
|
||||
|
||||
### Bulk Operations
|
||||
- **Duplicate**: Copy component with "Duplicate" button
|
||||
- **Delete**: Remove component with "Delete" button
|
||||
- **Reorder**: Drag components to reposition
|
||||
- **Multi-select**: Select multiple components for batch operations
|
||||
|
||||
## Form Templates & Management
|
||||
|
||||
### Template System
|
||||
- **Blank Form**: Start from scratch
|
||||
- **Contact Form**: Pre-built contact form
|
||||
- **Survey Form**: Multi-question survey template
|
||||
- **Registration Form**: User registration template
|
||||
- **Custom Templates**: Save your own templates
|
||||
|
||||
### Form Management
|
||||
- **Auto-Save**: Changes saved automatically every 30 seconds
|
||||
- **Version History**: Track form changes over time
|
||||
- **Duplicate Forms**: Copy existing forms as starting points
|
||||
- **Export/Import**: JSON format for form portability
|
||||
- **Form Library**: Organize forms by category or project
|
||||
|
||||
## Process Builder Integration
|
||||
|
||||
### Workflow Integration
|
||||
Forms integrate seamlessly with the Process Builder for automated workflows:
|
||||
|
||||
1. **Create Form**: Design your form in Form Builder
|
||||
2. **Save Form**: Forms must be saved before use in processes
|
||||
3. **Add to Process**: Select saved form in Process Builder Form Task
|
||||
4. **Data Flow**: Form submissions become process variables
|
||||
5. **Decision Logic**: Use form data in process gateways and conditions
|
||||
|
||||
### Advanced Integration Features
|
||||
- **URL Parameters**: Pre-populate form fields from process variables
|
||||
- **Conditional Logic**: Show/hide fields based on process state
|
||||
- **Multi-Step Forms**: Split complex forms across multiple process steps
|
||||
- **Data Validation**: Cross-reference form data with process requirements
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Form Design
|
||||
1. **Start Simple**: Begin with essential fields only
|
||||
2. **Use Smart Widths**: Trust the width recommendations
|
||||
3. **Group Related Fields**: Use consistent widths for grouped fields
|
||||
4. **Logical Flow**: Order fields in a natural sequence
|
||||
5. **Mobile-First**: Test forms on mobile devices
|
||||
|
||||
### Grid Layout Optimization
|
||||
1. **Mix Widths**: Combine narrow and wide fields effectively
|
||||
2. **Visual Balance**: Avoid too many full-width fields in sequence
|
||||
3. **Logical Grouping**: Use similar widths for related fields
|
||||
4. **Space Efficiency**: Let the system auto-optimize grid placement
|
||||
|
||||
### User Experience
|
||||
1. **Clear Labels**: Use descriptive, action-oriented labels
|
||||
2. **Helpful Placeholders**: Provide examples in placeholder text
|
||||
3. **Progressive Disclosure**: Use sections for complex forms
|
||||
4. **Validation Feedback**: Set appropriate validation rules
|
||||
5. **Success States**: Configure clear success messages
|
||||
|
||||
### Performance Optimization
|
||||
1. **Component Limits**: Keep forms under 50 components when possible
|
||||
2. **Conditional Logic**: Use sparingly to maintain performance
|
||||
3. **File Uploads**: Set reasonable file size limits
|
||||
4. **Auto-Save**: Leverage automatic saving features
|
||||
|
||||
## Accessibility Features
|
||||
|
||||
### Built-in Accessibility
|
||||
- **Keyboard Navigation**: Full keyboard support
|
||||
- **Screen Reader Support**: Proper ARIA labels and descriptions
|
||||
- **Focus Management**: Clear focus indicators
|
||||
- **High Contrast**: Accessible color combinations
|
||||
- **Touch Targets**: Mobile-friendly touch targets
|
||||
|
||||
### Accessibility Best Practices
|
||||
1. **Descriptive Labels**: Always provide clear field labels
|
||||
2. **Help Text**: Use help text for complex fields
|
||||
3. **Error Messages**: Provide clear, actionable error messages
|
||||
4. **Logical Order**: Ensure tab order follows visual layout
|
||||
5. **Alternative Text**: Provide alt text for any images
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Panel Not Opening**
|
||||
- Ensure component is properly selected
|
||||
- Check browser console for JavaScript errors
|
||||
- Refresh page if panel state becomes stuck
|
||||
|
||||
**Grid Layout Issues**
|
||||
- Use the optimize layout feature
|
||||
- Check for invalid width values
|
||||
- Reset component to default width
|
||||
|
||||
**Components Not Saving**
|
||||
- Verify internet connection
|
||||
- Check browser local storage limits
|
||||
- Try manual save if auto-save fails
|
||||
|
||||
**Performance Issues**
|
||||
- Reduce number of components
|
||||
- Simplify conditional logic
|
||||
- Clear browser cache
|
||||
|
||||
### Getting Help
|
||||
- **Documentation**: Complete technical guides available
|
||||
- **Examples**: Sample forms and templates provided
|
||||
- **Support**: Contact support team for assistance
|
||||
- **Community**: Join user community forums
|
||||
|
||||
## JavaScript & Dynamic Calculations
|
||||
|
||||
### Real-time Calculations
|
||||
The Form Builder supports powerful JavaScript-based calculations that update in real-time as users interact with your forms. This enables dynamic forms with automatic calculations, conditional logic, and interactive behavior.
|
||||
|
||||
#### Key Features
|
||||
- **Real-time Updates**: Calculations execute instantly when users change field values
|
||||
- **onFieldChange Handlers**: Trigger custom logic when specific fields change
|
||||
- **setField Function**: Programmatically update field values from JavaScript
|
||||
- **Cross-field Dependencies**: Create complex relationships between form fields
|
||||
- **Calculation Templates**: Pre-built templates for common calculation patterns
|
||||
|
||||
#### Basic Example
|
||||
```javascript
|
||||
// Form with real-time total calculation
|
||||
onLoad: function() {
|
||||
console.log('Form loaded, setting up calculations...');
|
||||
|
||||
// Initialize fields
|
||||
setField('quantity', 1);
|
||||
setField('price', 0);
|
||||
setField('total', 0);
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
console.log('Field changed:', fieldName, '=', value);
|
||||
|
||||
if (fieldName === 'quantity' || fieldName === 'price') {
|
||||
// Get current values
|
||||
const quantity = getField('quantity') || 0;
|
||||
const price = getField('price') || 0;
|
||||
|
||||
// Calculate and update total
|
||||
const total = quantity * price;
|
||||
setField('total', total.toFixed(2));
|
||||
|
||||
console.log('Updated total:', total);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Available JavaScript Functions
|
||||
|
||||
#### setField(fieldName, value)
|
||||
Updates a form field value and triggers the UI to refresh.
|
||||
```javascript
|
||||
// Set text field
|
||||
setField('user_name', 'John Doe');
|
||||
|
||||
// Set number field
|
||||
setField('quantity', 5);
|
||||
|
||||
// Set calculated field
|
||||
setField('total', 99.95);
|
||||
```
|
||||
|
||||
#### getField(fieldName)
|
||||
Retrieves the current value of a form field.
|
||||
```javascript
|
||||
const userName = getField('user_name');
|
||||
const quantity = getField('quantity') || 0; // Default to 0 if empty
|
||||
```
|
||||
|
||||
#### onLoad Event Handler
|
||||
Executes when the form first loads. Use for initialization and setting default values.
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Set default values
|
||||
setField('country', 'USA');
|
||||
setField('currency', 'USD');
|
||||
|
||||
// Perform initial calculations
|
||||
calculateTotals();
|
||||
}
|
||||
```
|
||||
|
||||
#### onFieldChange Event Handler
|
||||
Executes whenever a user changes a field value. Receives fieldName and new value as parameters.
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
switch(fieldName) {
|
||||
case 'quantity':
|
||||
case 'price':
|
||||
updateTotal();
|
||||
break;
|
||||
|
||||
case 'country':
|
||||
updateShippingOptions(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Real-world Examples
|
||||
|
||||
#### Invoice Calculator
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
// Initialize invoice fields
|
||||
setField('quantity', 1);
|
||||
setField('unit_price', 0);
|
||||
setField('tax_rate', 8.5); // 8.5% tax
|
||||
setField('subtotal', 0);
|
||||
setField('tax_amount', 0);
|
||||
setField('total', 0);
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (['quantity', 'unit_price', 'tax_rate'].includes(fieldName)) {
|
||||
const quantity = parseFloat(getField('quantity')) || 0;
|
||||
const unitPrice = parseFloat(getField('unit_price')) || 0;
|
||||
const taxRate = parseFloat(getField('tax_rate')) || 0;
|
||||
|
||||
const subtotal = quantity * unitPrice;
|
||||
const taxAmount = (subtotal * taxRate) / 100;
|
||||
const total = subtotal + taxAmount;
|
||||
|
||||
setField('subtotal', subtotal.toFixed(2));
|
||||
setField('tax_amount', taxAmount.toFixed(2));
|
||||
setField('total', total.toFixed(2));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Conditional Field Logic
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (fieldName === 'subscription_type') {
|
||||
if (value === 'premium') {
|
||||
setField('premium_features', 'Available');
|
||||
setField('support_level', '24/7 Priority');
|
||||
} else {
|
||||
setField('premium_features', 'Not Available');
|
||||
setField('support_level', 'Standard Business Hours');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Age Calculator
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
if (fieldName === 'birth_date') {
|
||||
const birthDate = new Date(value);
|
||||
const today = new Date();
|
||||
|
||||
let age = today.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = today.getMonth() - birthDate.getMonth();
|
||||
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
|
||||
setField('age', age);
|
||||
|
||||
// Set eligibility based on age
|
||||
if (age >= 18) {
|
||||
setField('eligible', 'Yes');
|
||||
} else {
|
||||
setField('eligible', 'No');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Best Practices for JavaScript Forms
|
||||
|
||||
#### Performance Optimization
|
||||
1. **Minimize Calculations**: Only calculate when necessary fields change
|
||||
2. **Use Default Values**: Provide fallbacks for empty fields
|
||||
3. **Debounce Heavy Operations**: For complex calculations, consider debouncing
|
||||
4. **Cache Results**: Store calculation results when appropriate
|
||||
|
||||
#### Error Handling
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
try {
|
||||
if (fieldName === 'price') {
|
||||
const price = parseFloat(value);
|
||||
if (isNaN(price)) {
|
||||
console.warn('Invalid price value:', value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform calculation
|
||||
calculateTotal(price);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in field change handler:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Debugging Tips
|
||||
1. **Use Console Logging**: Add console.log statements to track execution
|
||||
2. **Check Field Names**: Ensure field names in code match form field names exactly
|
||||
3. **Validate Data Types**: Always parse and validate input values
|
||||
4. **Test Edge Cases**: Test with empty values, zero, and negative numbers
|
||||
|
||||
### Form Templates with JavaScript
|
||||
|
||||
#### CSS & JavaScript Test Form
|
||||
A comprehensive template demonstrating real-time calculations:
|
||||
|
||||
**Field Structure:**
|
||||
- `user_name` (Text): User's name
|
||||
- `quantity` (Number): Quantity of items
|
||||
- `price` (Number): Price per item
|
||||
- `total` (Number): Calculated total (read-only)
|
||||
|
||||
**JavaScript Logic:**
|
||||
```javascript
|
||||
onLoad: function() {
|
||||
console.log('Form loaded, initializing...');
|
||||
setField('user_name', '');
|
||||
setField('quantity', 1);
|
||||
setField('price', 0);
|
||||
setField('total', 0);
|
||||
}
|
||||
|
||||
onFieldChange: function(fieldName, value) {
|
||||
console.log('Field changed:', fieldName, '=', value);
|
||||
|
||||
if (fieldName === 'user_name') {
|
||||
console.log('User name updated to:', value);
|
||||
}
|
||||
|
||||
if (fieldName === 'quantity' || fieldName === 'price') {
|
||||
const quantity = parseFloat(getField('quantity')) || 0;
|
||||
const price = parseFloat(getField('price')) || 0;
|
||||
const total = quantity * price;
|
||||
|
||||
setField('total', total.toFixed(2));
|
||||
console.log('Calculated total:', total);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Troubleshooting JavaScript Forms
|
||||
|
||||
#### Common Issues
|
||||
|
||||
**Calculations Not Updating**
|
||||
- Verify field names match exactly (case-sensitive)
|
||||
- Check browser console for JavaScript errors
|
||||
- Ensure `onFieldChange` handler is properly defined
|
||||
- Confirm form is in preview mode for testing
|
||||
|
||||
**Initial Values Not Setting**
|
||||
- Make sure `onLoad` handler is defined
|
||||
- Check that `setField` calls use correct field names
|
||||
- Verify form data structure in browser developer tools
|
||||
|
||||
**Console Errors**
|
||||
- **"getField/setField is not defined"**: JavaScript engine not properly initialized
|
||||
- **"Cannot read property"**: Field name doesn't exist or typo in field name
|
||||
- **"Unexpected token"**: Syntax error in JavaScript code
|
||||
|
||||
#### Debugging Workflow
|
||||
1. **Open Browser Developer Tools** (F12)
|
||||
2. **Check Console Tab** for error messages
|
||||
3. **Add Debug Logging**:
|
||||
```javascript
|
||||
onFieldChange: function(fieldName, value) {
|
||||
console.log('=== Field Change Debug ===');
|
||||
console.log('Field:', fieldName);
|
||||
console.log('New Value:', value);
|
||||
console.log('Current Form Data:', Object.keys(formData));
|
||||
|
||||
// Your calculation logic here
|
||||
}
|
||||
```
|
||||
4. **Test in Preview Mode** - JavaScript only executes in preview mode
|
||||
5. **Verify Field Names** - Use browser inspect element to confirm field name attributes
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Custom Components
|
||||
- **Component Extensions**: Add custom component types
|
||||
- **Styling Overrides**: Custom CSS for branded forms
|
||||
- **Validation Rules**: Create custom validation logic
|
||||
- **Integration Hooks**: Connect to external services
|
||||
|
||||
### API Integration
|
||||
- **Form Data API**: Programmatic access to form submissions
|
||||
- **Component API**: Dynamically modify form components
|
||||
- **Validation API**: Custom server-side validation
|
||||
- **Webhook Support**: Real-time form submission notifications
|
||||
|
||||
### Enterprise Features
|
||||
- **Team Collaboration**: Multi-user form editing
|
||||
- **Permission Management**: Role-based access control
|
||||
- **Audit Logging**: Track all form changes
|
||||
- **White Labeling**: Custom branding options
|
||||
|
||||
---
|
||||
|
||||
*This documentation reflects all current features including the smart collapsible settings panel, enhanced grid system with visual previews, intelligent width recommendations, and comprehensive UX improvements implemented in the latest version.*
|
||||
|
||||
Last updated: December 2024
|
@ -1,587 +0,0 @@
|
||||
# Form Builder Grid System - Complete Reference
|
||||
|
||||
## Overview
|
||||
|
||||
The form builder uses an intelligent 12-column CSS Grid system that automatically manages component placement and provides intuitive width selection through visual previews and smart recommendations. This system replaces technical percentages with user-friendly names and includes automatic gap-filling optimization.
|
||||
|
||||
## Grid Architecture
|
||||
|
||||
### CSS Grid Foundation
|
||||
```css
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-auto-flow: row dense; /* Enables automatic gap filling */
|
||||
column-gap: 16px;
|
||||
row-gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
```
|
||||
|
||||
### Component Grid Placement
|
||||
```css
|
||||
.form-component {
|
||||
grid-column: span 12; /* Default to full width */
|
||||
width: 100% !important; /* Force width within grid cell */
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
```
|
||||
|
||||
## Width Options System
|
||||
|
||||
### Available Width Options
|
||||
|
||||
| Name | Percentage | Grid Columns | Grid Span | Use Cases |
|
||||
|--------|------------|--------------|--------------|-----------|
|
||||
| Narrow | 25% | 3 of 12 | `span 3` | Small inputs like age, zip codes, short codes |
|
||||
| Small | 33% | 4 of 12 | `span 4` | Short text fields, city names, grouped inputs |
|
||||
| Medium | 50% | 6 of 12 | `span 6` | Names, phone numbers, paired inputs |
|
||||
| Wide | 75% | 9 of 12 | `span 9` | Addresses, URLs, prominent fields |
|
||||
| Full | 100% | 12 of 12 | `span 12` | Text areas, headings, single-column layouts |
|
||||
|
||||
### Width Selection Interface
|
||||
|
||||
#### Visual Grid Preview
|
||||
Each width option displays a mini 12-column grid preview:
|
||||
```vue
|
||||
<div class="grid-container-mini">
|
||||
<div
|
||||
v-for="i in 12"
|
||||
:key="i"
|
||||
class="grid-cell"
|
||||
:class="{
|
||||
'active': i <= option.gridColumns,
|
||||
'inactive': i > option.gridColumns
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Current Selection Feedback
|
||||
The interface shows:
|
||||
- Current width name (e.g., "Medium")
|
||||
- Grid columns used (e.g., "6 of 12 columns")
|
||||
- Visual representation of current selection
|
||||
- Percentage equivalent for technical reference
|
||||
|
||||
## Smart Recommendation System
|
||||
|
||||
### Field Type Recommendations
|
||||
|
||||
The system automatically suggests optimal widths based on component type:
|
||||
|
||||
#### Narrow Fields (25% - 3 columns)
|
||||
**Recommended for**: Small, precise inputs
|
||||
- `number`: Age, quantity, counts
|
||||
- `date`: Birth dates, deadlines
|
||||
- `time`: Appointment times
|
||||
- `color`: Color picker inputs
|
||||
- `otp`: Verification codes
|
||||
|
||||
```javascript
|
||||
const narrowRecommended = ['number', 'date', 'time', 'color', 'otp']
|
||||
```
|
||||
|
||||
#### Small Fields (33% - 4 columns)
|
||||
**Recommended for**: Short text inputs
|
||||
- `text`: Names, titles, short answers
|
||||
|
||||
```javascript
|
||||
const smallRecommended = ['text']
|
||||
```
|
||||
|
||||
#### Medium Fields (50% - 6 columns)
|
||||
**Recommended for**: Standard form inputs
|
||||
- `email`: Email addresses
|
||||
- `tel`: Phone numbers
|
||||
- `password`: Password fields
|
||||
- `mask`: Formatted inputs (SSN, phone)
|
||||
- `select`: Dropdown selections
|
||||
- `datetime-local`: Combined date/time
|
||||
|
||||
```javascript
|
||||
const mediumRecommended = ['email', 'tel', 'password', 'mask', 'select', 'datetime-local']
|
||||
```
|
||||
|
||||
#### Wide Fields (75% - 9 columns)
|
||||
**Recommended for**: Longer inputs requiring more space
|
||||
- `url`: Website addresses
|
||||
- `file`: File upload fields
|
||||
- `dropzone`: Drag & drop upload areas
|
||||
|
||||
```javascript
|
||||
const wideRecommended = ['url', 'file', 'dropzone']
|
||||
```
|
||||
|
||||
#### Full Width Fields (100% - 12 columns)
|
||||
**Recommended for**: Content and multi-option components
|
||||
- `textarea`: Multi-line text areas
|
||||
- `heading`: Section headings
|
||||
- `paragraph`: Descriptive text
|
||||
- `checkbox`: Checkbox groups
|
||||
- `radio`: Radio button groups
|
||||
- `range`: Range sliders
|
||||
- `switch`: Toggle switches
|
||||
- `button`: Action buttons
|
||||
- `info-display`: Information displays
|
||||
|
||||
```javascript
|
||||
const fullRecommended = [
|
||||
'textarea', 'heading', 'paragraph', 'checkbox',
|
||||
'radio', 'range', 'switch', 'button', 'info-display'
|
||||
]
|
||||
```
|
||||
|
||||
### Visual Recommendation Indicators
|
||||
|
||||
- **Green Ring**: Recommended options have green highlighting
|
||||
- **Selection State**: Current choice highlighted in blue
|
||||
- **Combined State**: Selected + recommended shows green background with checkmark
|
||||
- **Recommendation Badge**: "Recommended" text appears next to optimal choices
|
||||
|
||||
## Smart Grid Placement Algorithm
|
||||
|
||||
### Optimal Placement Logic
|
||||
|
||||
The system automatically finds the best position for new components:
|
||||
|
||||
```javascript
|
||||
findOptimalGridPlacement() {
|
||||
if (this.formComponents.length === 0) {
|
||||
// First component - full width
|
||||
return {
|
||||
gridColumn: 'span 12',
|
||||
rowIndex: 0,
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
|
||||
// Group components by their implicit rows
|
||||
const rows = this.analyzeGridRows()
|
||||
|
||||
// Find row with remaining space
|
||||
const rowWithSpace = rows.find(row => row.remainingSpace > 0)
|
||||
|
||||
if (rowWithSpace) {
|
||||
// Use remaining space in existing row
|
||||
const remainingColumns = rowWithSpace.remainingSpace
|
||||
const widthPercent = this.columnsToPercentage(remainingColumns)
|
||||
|
||||
return {
|
||||
gridColumn: `span ${remainingColumns}`,
|
||||
rowIndex: rows.indexOf(rowWithSpace),
|
||||
width: `${widthPercent}%`
|
||||
}
|
||||
} else {
|
||||
// Create new row
|
||||
return {
|
||||
gridColumn: 'span 12',
|
||||
rowIndex: rows.length,
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Row Analysis
|
||||
```javascript
|
||||
analyzeGridRows() {
|
||||
const rows = []
|
||||
let currentRowSpace = 12
|
||||
|
||||
this.formComponents.forEach(component => {
|
||||
const spanMatch = component.props.gridColumn?.match(/span\s+(\d+)/) || []
|
||||
const columnSpan = parseInt(spanMatch[1]) || 12
|
||||
|
||||
if (columnSpan <= currentRowSpace) {
|
||||
// Add to current row
|
||||
currentRowSpace -= columnSpan
|
||||
} else {
|
||||
// Start new row
|
||||
rows.push({ remainingSpace: currentRowSpace })
|
||||
currentRowSpace = 12 - columnSpan
|
||||
}
|
||||
})
|
||||
|
||||
return rows
|
||||
}
|
||||
```
|
||||
|
||||
## Grid Layout Optimization
|
||||
|
||||
### Automatic Gap Filling
|
||||
|
||||
The `grid-auto-flow: row dense` CSS property enables automatic gap filling:
|
||||
- Components automatically fill available spaces
|
||||
- No manual positioning required
|
||||
- Optimal use of screen real estate
|
||||
|
||||
### Layout Optimization Algorithm
|
||||
|
||||
```javascript
|
||||
optimizeGridLayout() {
|
||||
const rows = this.groupComponentsByRows()
|
||||
|
||||
rows.forEach(row => {
|
||||
if (row.remainingSpace > 0) {
|
||||
this.distributeRemainingSpace(row)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
distributeRemainingSpace(row) {
|
||||
if (row.components.length === 1 && row.remainingSpace > 0) {
|
||||
// Expand single component to fill row
|
||||
const comp = row.components[0]
|
||||
this.updateComponentSize(comp.component, 12)
|
||||
} else if (row.components.length > 1) {
|
||||
// Distribute space proportionally
|
||||
const extraSpanPerComponent = Math.floor(row.remainingSpace / row.components.length)
|
||||
let remainingExtraSpan = row.remainingSpace % row.components.length
|
||||
|
||||
row.components.forEach(comp => {
|
||||
let extraSpan = extraSpanPerComponent
|
||||
if (remainingExtraSpan > 0) {
|
||||
extraSpan += 1
|
||||
remainingExtraSpan--
|
||||
}
|
||||
|
||||
const newSpan = comp.span + extraSpan
|
||||
this.updateComponentSize(comp.component, newSpan)
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Collapsible Panel Integration
|
||||
|
||||
### Quick Width Selector
|
||||
|
||||
The collapsible panel includes a compact width selector:
|
||||
|
||||
```vue
|
||||
<div class="width-selector-compact">
|
||||
<button
|
||||
v-for="option in compactWidthOptions"
|
||||
:key="option.value"
|
||||
@click="updateQuickSetting('width', option.value, option.gridColumns)"
|
||||
class="width-btn"
|
||||
:class="{ 'active': getComponentWidthPercent() === option.value }"
|
||||
>
|
||||
{{ option.name }}
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Compact Options
|
||||
```javascript
|
||||
const compactWidthOptions = [
|
||||
{ name: 'S', value: 25, gridColumns: 3, description: 'Small (25%)' },
|
||||
{ name: 'M', value: 50, gridColumns: 6, description: 'Medium (50%)' },
|
||||
{ name: 'L', value: 75, gridColumns: 9, description: 'Large (75%)' },
|
||||
{ name: 'XL', value: 100, gridColumns: 12, description: 'Extra Large (100%)' }
|
||||
]
|
||||
```
|
||||
|
||||
## Grid Component Implementation
|
||||
|
||||
### Component Props Structure
|
||||
```typescript
|
||||
interface ComponentProps {
|
||||
width: string // e.g., "50%"
|
||||
gridColumn: string // e.g., "span 6"
|
||||
label?: string
|
||||
name?: string
|
||||
// ... other props
|
||||
}
|
||||
```
|
||||
|
||||
### Width Calculation Functions
|
||||
|
||||
```javascript
|
||||
// Convert percentage to grid columns
|
||||
const percentageToGridColumns = (percentage) => {
|
||||
const mapping = {
|
||||
25: 3, // 3/12 = 25%
|
||||
33: 4, // 4/12 = 33.33%
|
||||
50: 6, // 6/12 = 50%
|
||||
66: 8, // 8/12 = 66.67%
|
||||
75: 9, // 9/12 = 75%
|
||||
100: 12 // 12/12 = 100%
|
||||
}
|
||||
return mapping[percentage] || Math.round((percentage / 100) * 12)
|
||||
}
|
||||
|
||||
// Convert grid columns to percentage
|
||||
const gridColumnsToPercentage = (columns) => {
|
||||
const mapping = {
|
||||
3: 25, // 3/12 = 25%
|
||||
4: 33, // 4/12 = 33.33%
|
||||
6: 50, // 6/12 = 50%
|
||||
8: 66, // 8/12 = 66.67%
|
||||
9: 75, // 9/12 = 75%
|
||||
12: 100 // 12/12 = 100%
|
||||
}
|
||||
return mapping[columns] || Math.round((columns / 12) * 100)
|
||||
}
|
||||
|
||||
// Set component width with grid sync
|
||||
const setComponentWidth = (percentage, gridColumns) => {
|
||||
component.props.width = `${percentage}%`
|
||||
component.props.gridColumn = `span ${gridColumns}`
|
||||
}
|
||||
```
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
### Mobile Optimization
|
||||
```css
|
||||
@media (max-width: 768px) {
|
||||
.grid-container {
|
||||
grid-template-columns: 1fr; /* Single column on mobile */
|
||||
column-gap: 0;
|
||||
}
|
||||
|
||||
.form-component {
|
||||
grid-column: span 1 !important; /* Force single column */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Tablet Optimization
|
||||
```css
|
||||
@media (min-width: 769px) and (max-width: 1024px) {
|
||||
.grid-container {
|
||||
grid-template-columns: repeat(8, 1fr); /* 8 columns on tablet */
|
||||
}
|
||||
|
||||
.form-component[style*="span 12"] {
|
||||
grid-column: span 8 !important; /* Adjust full-width components */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Resize Handle System
|
||||
|
||||
### Interactive Resizing
|
||||
```vue
|
||||
<div v-if="selectedComponentId === component.id && resizeMode" class="resize-handles">
|
||||
<div
|
||||
class="resize-handle resize-handle-right"
|
||||
@mousedown="startResize($event, component)"
|
||||
></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Resize Logic with Snap-to-Grid
|
||||
```javascript
|
||||
const handleResize = (event) => {
|
||||
const deltaX = event.clientX - initialX.value
|
||||
const container = document.querySelector('.grid-container')
|
||||
const containerWidth = container.offsetWidth
|
||||
const deltaPercentage = (deltaX / containerWidth) * 100
|
||||
|
||||
let newWidth = initialWidth.value + deltaPercentage
|
||||
newWidth = Math.max(25, Math.min(100, newWidth))
|
||||
|
||||
// Snap to standard widths
|
||||
const standardWidths = [25, 33, 50, 66, 75, 100]
|
||||
for (const std of standardWidths) {
|
||||
if (Math.abs(newWidth - std) < 5) {
|
||||
newWidth = std
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to grid columns and update
|
||||
const gridColumns = percentageToGridColumns(newWidth)
|
||||
updateComponentSize(component, newWidth, gridColumns)
|
||||
}
|
||||
```
|
||||
|
||||
## CSS Styling Reference
|
||||
|
||||
### Grid Container Styles
|
||||
```css
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-auto-flow: row dense;
|
||||
column-gap: 16px;
|
||||
row-gap: 16px;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
```
|
||||
|
||||
### Width Selector Modal Styles
|
||||
```css
|
||||
.width-option {
|
||||
@apply border border-gray-200 rounded-lg p-4 transition-all duration-200 hover:border-blue-300 hover:bg-blue-50 cursor-pointer flex items-center space-x-4;
|
||||
}
|
||||
|
||||
.width-option.selected {
|
||||
@apply border-blue-500 bg-blue-50 ring-2 ring-blue-200;
|
||||
}
|
||||
|
||||
.width-option.recommended {
|
||||
@apply ring-2 ring-green-200 border-green-300;
|
||||
}
|
||||
|
||||
.width-option.selected.recommended {
|
||||
@apply border-green-500 bg-green-50 ring-2 ring-green-200;
|
||||
}
|
||||
```
|
||||
|
||||
### Grid Preview Styles
|
||||
```css
|
||||
.grid-container-mini {
|
||||
@apply grid grid-cols-12 gap-1 w-32;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
@apply h-2 rounded-sm transition-colors duration-200;
|
||||
}
|
||||
|
||||
.grid-cell.active {
|
||||
@apply bg-blue-500;
|
||||
}
|
||||
|
||||
.grid-cell.inactive {
|
||||
@apply bg-gray-200;
|
||||
}
|
||||
```
|
||||
|
||||
### Quick Selector Styles
|
||||
```css
|
||||
.width-selector-compact {
|
||||
@apply flex space-x-1;
|
||||
}
|
||||
|
||||
.width-btn {
|
||||
@apply flex-1 px-2 py-1.5 text-xs font-medium text-center rounded-md border border-gray-200 bg-white hover:border-blue-300 hover:bg-blue-50 transition-colors duration-200;
|
||||
}
|
||||
|
||||
.width-btn.active {
|
||||
@apply border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-200;
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Grid Layout Design
|
||||
1. **Start Simple**: Begin with recommended widths
|
||||
2. **Mix Widths**: Combine different widths for visual interest
|
||||
3. **Logical Grouping**: Use consistent widths for related fields
|
||||
4. **Visual Balance**: Avoid too many full-width fields in sequence
|
||||
5. **Mobile-First**: Consider how layouts adapt on smaller screens
|
||||
|
||||
### Performance Considerations
|
||||
1. **Grid Optimization**: Use the automatic optimization feature
|
||||
2. **Component Limits**: Keep forms under 50 components for best performance
|
||||
3. **Responsive Testing**: Test across different screen sizes
|
||||
4. **Browser Support**: Ensure CSS Grid polyfills for older browsers
|
||||
|
||||
### User Experience Guidelines
|
||||
1. **Intuitive Widths**: Trust the recommendation system
|
||||
2. **Visual Feedback**: Use the grid preview for precision
|
||||
3. **Consistent Patterns**: Establish width patterns across forms
|
||||
4. **Progressive Enhancement**: Start with basics, add complexity gradually
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Grid Layout Not Working**
|
||||
- Check CSS Grid browser support
|
||||
- Verify `grid-template-columns` is applied
|
||||
- Ensure components have valid `gridColumn` values
|
||||
|
||||
**Components Overlapping**
|
||||
- Check for invalid span values (must be 1-12)
|
||||
- Verify total spans in row don't exceed 12
|
||||
- Use grid optimization to fix layouts
|
||||
|
||||
**Responsive Issues**
|
||||
- Test media query breakpoints
|
||||
- Verify mobile-specific grid rules
|
||||
- Check component width calculations
|
||||
|
||||
**Performance Problems**
|
||||
- Reduce number of components
|
||||
- Use virtual scrolling for large forms
|
||||
- Optimize grid calculations
|
||||
|
||||
### Debugging Tools
|
||||
|
||||
```javascript
|
||||
// Debug grid layout
|
||||
const debugGridLayout = () => {
|
||||
const rows = groupComponentsByRows()
|
||||
console.table(rows.map(row => ({
|
||||
components: row.components.length,
|
||||
remainingSpace: row.remainingSpace,
|
||||
totalSpan: 12 - row.remainingSpace
|
||||
})))
|
||||
}
|
||||
|
||||
// Validate component grid properties
|
||||
const validateGridProps = (component) => {
|
||||
const span = component.props.gridColumn?.match(/span\s+(\d+)/)?.[1]
|
||||
const width = parseInt(component.props.width)
|
||||
|
||||
console.log({
|
||||
id: component.id,
|
||||
span: parseInt(span),
|
||||
width: width,
|
||||
valid: span >= 1 && span <= 12 && width > 0 && width <= 100
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Upgrading from Percentage-Only System
|
||||
|
||||
1. **Update Component Props**:
|
||||
```javascript
|
||||
// Old system
|
||||
component.props.width = "50%"
|
||||
|
||||
// New system
|
||||
component.props.width = "50%"
|
||||
component.props.gridColumn = "span 6"
|
||||
```
|
||||
|
||||
2. **Migrate Existing Forms**:
|
||||
```javascript
|
||||
const migrateFormToGrid = (form) => {
|
||||
return {
|
||||
...form,
|
||||
components: form.components.map(component => ({
|
||||
...component,
|
||||
props: {
|
||||
...component.props,
|
||||
gridColumn: component.props.gridColumn ||
|
||||
`span ${percentageToGridColumns(parseInt(component.props.width) || 100)}`
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Update CSS**:
|
||||
```css
|
||||
/* Remove old percentage-based widths */
|
||||
.form-component {
|
||||
/* width: var(--component-width); // Remove this */
|
||||
grid-column: var(--grid-column); /* Add this */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*This grid system documentation reflects the complete implementation including visual selection, smart recommendations, automatic optimization, and responsive behavior.*
|
||||
|
||||
**Last updated**: December 2024
|
@ -307,583 +307,3 @@ this.onFieldChange("tanggungan_maklumat", (value) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Conditional Logic Script
|
||||
|
||||
// Conditional logic for field: nyatakan_lain2
|
||||
onFieldChange("radio_bangsa", function () {
|
||||
if (getField("radio_bangsa") !== "lain") {
|
||||
hideField("nyatakan_lain2");
|
||||
} else {
|
||||
showField("nyatakan_lain2");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: nyatakan_lain2
|
||||
(function () {
|
||||
if (getField("radio_bangsa") !== "lain") {
|
||||
hideField("nyatakan_lain2");
|
||||
} else {
|
||||
showField("nyatakan_lain2");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: text_14
|
||||
onFieldChange("radio_pendidikan", function () {
|
||||
if (getField("radio_pendidikan") !== "lain") {
|
||||
hideField("text_14");
|
||||
} else {
|
||||
showField("text_14");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: text_14
|
||||
(function () {
|
||||
if (getField("radio_pendidikan") !== "lain") {
|
||||
hideField("text_14");
|
||||
} else {
|
||||
showField("text_14");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: hubungan_lain_nyatakan
|
||||
onFieldChange("hubungan_keluarga", function () {
|
||||
if (
|
||||
!String(getField("hubungan_keluarga") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("hubungan_lain_nyatakan");
|
||||
} else {
|
||||
showField("hubungan_lain_nyatakan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: hubungan_lain_nyatakan
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("hubungan_keluarga") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("hubungan_lain_nyatakan");
|
||||
} else {
|
||||
showField("hubungan_lain_nyatakan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: sebab_tunai
|
||||
onFieldChange("cara_pembayaran", function () {
|
||||
if (
|
||||
!String(getField("cara_pembayaran") || "")
|
||||
.toLowerCase()
|
||||
.includes("tunai".toLowerCase())
|
||||
) {
|
||||
hideField("sebab_tunai");
|
||||
} else {
|
||||
showField("sebab_tunai");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: sebab_tunai
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("cara_pembayaran") || "")
|
||||
.toLowerCase()
|
||||
.includes("tunai".toLowerCase())
|
||||
) {
|
||||
hideField("sebab_tunai");
|
||||
} else {
|
||||
showField("sebab_tunai");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: pendidikan_lain_tanggungan
|
||||
onFieldChange("pendidikan_tertinggi_tanggungan", function () {
|
||||
if (
|
||||
!String(getField("pendidikan_tertinggi_tanggungan") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("pendidikan_lain_tanggungan");
|
||||
} else {
|
||||
showField("pendidikan_lain_tanggungan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: pendidikan_lain_tanggungan
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("pendidikan_tertinggi_tanggungan") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("pendidikan_lain_tanggungan");
|
||||
} else {
|
||||
showField("pendidikan_lain_tanggungan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: maklumat_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("maklumat_sekolah");
|
||||
} else {
|
||||
showField("maklumat_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: maklumat_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("maklumat_sekolah");
|
||||
} else {
|
||||
showField("maklumat_sekolah");
|
||||
}
|
||||
})();
|
||||
// Conditional Logic Script
|
||||
|
||||
// Conditional logic for field: nyatakan_lain2
|
||||
onFieldChange("radio_bangsa", function () {
|
||||
if (getField("radio_bangsa") !== "lain") {
|
||||
hideField("nyatakan_lain2");
|
||||
} else {
|
||||
showField("nyatakan_lain2");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: nyatakan_lain2
|
||||
(function () {
|
||||
if (getField("radio_bangsa") !== "lain") {
|
||||
hideField("nyatakan_lain2");
|
||||
} else {
|
||||
showField("nyatakan_lain2");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: text_14
|
||||
onFieldChange("radio_pendidikan", function () {
|
||||
if (getField("radio_pendidikan") !== "lain") {
|
||||
hideField("text_14");
|
||||
} else {
|
||||
showField("text_14");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: text_14
|
||||
(function () {
|
||||
if (getField("radio_pendidikan") !== "lain") {
|
||||
hideField("text_14");
|
||||
} else {
|
||||
showField("text_14");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: hubungan_lain_nyatakan
|
||||
onFieldChange("hubungan_keluarga", function () {
|
||||
if (
|
||||
!String(getField("hubungan_keluarga") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("hubungan_lain_nyatakan");
|
||||
} else {
|
||||
showField("hubungan_lain_nyatakan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: hubungan_lain_nyatakan
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("hubungan_keluarga") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("hubungan_lain_nyatakan");
|
||||
} else {
|
||||
showField("hubungan_lain_nyatakan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: bangsa_lain_tanggungan
|
||||
onFieldChange("bangsa_tanggungan", function () {
|
||||
if (getField("bangsa_tanggungan") !== "lain_lain") {
|
||||
hideField("bangsa_lain_tanggungan");
|
||||
} else {
|
||||
showField("bangsa_lain_tanggungan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: bangsa_lain_tanggungan
|
||||
(function () {
|
||||
if (getField("bangsa_tanggungan") !== "lain_lain") {
|
||||
hideField("bangsa_lain_tanggungan");
|
||||
} else {
|
||||
showField("bangsa_lain_tanggungan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: sebab_tunai
|
||||
onFieldChange("cara_pembayaran", function () {
|
||||
if (
|
||||
!String(getField("cara_pembayaran") || "")
|
||||
.toLowerCase()
|
||||
.includes("tunai".toLowerCase())
|
||||
) {
|
||||
hideField("sebab_tunai");
|
||||
} else {
|
||||
showField("sebab_tunai");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: sebab_tunai
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("cara_pembayaran") || "")
|
||||
.toLowerCase()
|
||||
.includes("tunai".toLowerCase())
|
||||
) {
|
||||
hideField("sebab_tunai");
|
||||
} else {
|
||||
showField("sebab_tunai");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: pendidikan_lain_tanggungan
|
||||
onFieldChange("pendidikan_tertinggi_tanggungan", function () {
|
||||
if (
|
||||
!String(getField("pendidikan_tertinggi_tanggungan") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("pendidikan_lain_tanggungan");
|
||||
} else {
|
||||
showField("pendidikan_lain_tanggungan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: pendidikan_lain_tanggungan
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("pendidikan_tertinggi_tanggungan") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("pendidikan_lain_tanggungan");
|
||||
} else {
|
||||
showField("pendidikan_lain_tanggungan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: nama_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("nama_sekolah");
|
||||
} else {
|
||||
showField("nama_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: nama_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("nama_sekolah");
|
||||
} else {
|
||||
showField("nama_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: alamat_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("alamat_sekolah");
|
||||
} else {
|
||||
showField("alamat_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: alamat_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("alamat_sekolah");
|
||||
} else {
|
||||
showField("alamat_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: daerah_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("daerah_sekolah");
|
||||
} else {
|
||||
showField("daerah_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: daerah_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("daerah_sekolah");
|
||||
} else {
|
||||
showField("daerah_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: negeri_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("negeri_sekolah");
|
||||
} else {
|
||||
showField("negeri_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: negeri_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("negeri_sekolah");
|
||||
} else {
|
||||
showField("negeri_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: poskod_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("poskod_sekolah");
|
||||
} else {
|
||||
showField("poskod_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: poskod_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("poskod_sekolah");
|
||||
} else {
|
||||
showField("poskod_sekolah");
|
||||
}
|
||||
})();
|
||||
// Conditional Logic Script
|
||||
|
||||
// Conditional logic for field: nyatakan_lain2
|
||||
onFieldChange("radio_bangsa", function () {
|
||||
if (getField("radio_bangsa") !== "lain") {
|
||||
hideField("nyatakan_lain2");
|
||||
} else {
|
||||
showField("nyatakan_lain2");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: nyatakan_lain2
|
||||
(function () {
|
||||
if (getField("radio_bangsa") !== "lain") {
|
||||
hideField("nyatakan_lain2");
|
||||
} else {
|
||||
showField("nyatakan_lain2");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: text_14
|
||||
onFieldChange("radio_pendidikan", function () {
|
||||
if (getField("radio_pendidikan") !== "lain") {
|
||||
hideField("text_14");
|
||||
} else {
|
||||
showField("text_14");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: text_14
|
||||
(function () {
|
||||
if (getField("radio_pendidikan") !== "lain") {
|
||||
hideField("text_14");
|
||||
} else {
|
||||
showField("text_14");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: hubungan_lain_nyatakan
|
||||
onFieldChange("hubungan_keluarga", function () {
|
||||
if (
|
||||
!String(getField("hubungan_keluarga") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("hubungan_lain_nyatakan");
|
||||
} else {
|
||||
showField("hubungan_lain_nyatakan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: hubungan_lain_nyatakan
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("hubungan_keluarga") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("hubungan_lain_nyatakan");
|
||||
} else {
|
||||
showField("hubungan_lain_nyatakan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: bangsa_lain_tanggungan
|
||||
onFieldChange("bangsa_tanggungan", function () {
|
||||
if (getField("bangsa_tanggungan") !== "lain_lain") {
|
||||
hideField("bangsa_lain_tanggungan");
|
||||
} else {
|
||||
showField("bangsa_lain_tanggungan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: bangsa_lain_tanggungan
|
||||
(function () {
|
||||
if (getField("bangsa_tanggungan") !== "lain_lain") {
|
||||
hideField("bangsa_lain_tanggungan");
|
||||
} else {
|
||||
showField("bangsa_lain_tanggungan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: sebab_tunai
|
||||
onFieldChange("cara_pembayaran", function () {
|
||||
if (
|
||||
!String(getField("cara_pembayaran") || "")
|
||||
.toLowerCase()
|
||||
.includes("tunai".toLowerCase())
|
||||
) {
|
||||
hideField("sebab_tunai");
|
||||
} else {
|
||||
showField("sebab_tunai");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: sebab_tunai
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("cara_pembayaran") || "")
|
||||
.toLowerCase()
|
||||
.includes("tunai".toLowerCase())
|
||||
) {
|
||||
hideField("sebab_tunai");
|
||||
} else {
|
||||
showField("sebab_tunai");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: pendidikan_lain_tanggungan
|
||||
onFieldChange("pendidikan_tertinggi_tanggungan", function () {
|
||||
if (
|
||||
!String(getField("pendidikan_tertinggi_tanggungan") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("pendidikan_lain_tanggungan");
|
||||
} else {
|
||||
showField("pendidikan_lain_tanggungan");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: pendidikan_lain_tanggungan
|
||||
(function () {
|
||||
if (
|
||||
!String(getField("pendidikan_tertinggi_tanggungan") || "")
|
||||
.toLowerCase()
|
||||
.includes("lain_lain".toLowerCase())
|
||||
) {
|
||||
hideField("pendidikan_lain_tanggungan");
|
||||
} else {
|
||||
showField("pendidikan_lain_tanggungan");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: nama_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("nama_sekolah");
|
||||
} else {
|
||||
showField("nama_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: nama_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("nama_sekolah");
|
||||
} else {
|
||||
showField("nama_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: alamat_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("alamat_sekolah");
|
||||
} else {
|
||||
showField("alamat_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: alamat_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("alamat_sekolah");
|
||||
} else {
|
||||
showField("alamat_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: daerah_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("daerah_sekolah");
|
||||
} else {
|
||||
showField("daerah_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: daerah_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("daerah_sekolah");
|
||||
} else {
|
||||
showField("daerah_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: negeri_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("negeri_sekolah");
|
||||
} else {
|
||||
showField("negeri_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: negeri_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("negeri_sekolah");
|
||||
} else {
|
||||
showField("negeri_sekolah");
|
||||
}
|
||||
})();
|
||||
|
||||
// Conditional logic for field: poskod_sekolah
|
||||
onFieldChange("bersekolah_tanggungan", function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("poskod_sekolah");
|
||||
} else {
|
||||
showField("poskod_sekolah");
|
||||
}
|
||||
});
|
||||
|
||||
// Initial evaluation for field: poskod_sekolah
|
||||
(function () {
|
||||
if (getField("bersekolah_tanggungan") !== "ya") {
|
||||
hideField("poskod_sekolah");
|
||||
} else {
|
||||
showField("poskod_sekolah");
|
||||
}
|
||||
})();
|
||||
|
@ -432,286 +432,134 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"type": "repeating-group",
|
||||
"props": {
|
||||
"name": "nama_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Nama (Untuk Mualaf, nama mengikut kad pengenalan)",
|
||||
"help": "Tambah maklumat untuk setiap tanggungan",
|
||||
"name": "tanggungan_maklumat",
|
||||
"label": "Maklumat Tanggungan",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "Masukkan nama lengkap",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"name": "jenis_kad_tanggungan",
|
||||
"type": "select",
|
||||
"label": "Jenis Pengenalan",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Sila Pilih Jenis Pengenalan", "value": "" },
|
||||
{ "label": "MyKad/MyKid", "value": "mykad" },
|
||||
{ "label": "No. K/P/Polis/Tentera/No. Pasport", "value": "kp_polis" },
|
||||
{ "label": "No. Sijil Beranak", "value": "sijil_beranak" }
|
||||
"fields": [
|
||||
{
|
||||
"name": "nama_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Nama (Untuk Mualaf, nama mengikut kad pengenalan)",
|
||||
"validation": "required",
|
||||
"placeholder": "Masukkan nama lengkap"
|
||||
},
|
||||
{
|
||||
"name": "jenis_kad_tanggungan",
|
||||
"type": "select",
|
||||
"label": "Jenis Pengenalan",
|
||||
"options": [
|
||||
{ "label": "MyKad/MyKid", "value": "mykad" },
|
||||
{
|
||||
"label": "No. K/P/Polis/Tentera/No. Pasport",
|
||||
"value": "kp_polis"
|
||||
},
|
||||
{ "label": "No. Sijil Beranak", "value": "sijil_beranak" }
|
||||
],
|
||||
"validation": "required"
|
||||
},
|
||||
{
|
||||
"name": "no_pengenalan_tanggungan",
|
||||
"type": "text",
|
||||
"label": "No. Pengenalan",
|
||||
"validation": "required",
|
||||
"placeholder": "Masukkan nombor pengenalan"
|
||||
},
|
||||
{
|
||||
"name": "jantina_tanggungan",
|
||||
"type": "radio",
|
||||
"label": "Jantina",
|
||||
"options": [
|
||||
{ "label": "Lelaki", "value": "lelaki" },
|
||||
{ "label": "Perempuan", "value": "perempuan" }
|
||||
],
|
||||
"validation": "required"
|
||||
},
|
||||
{
|
||||
"name": "tarikh_lahir_tanggungan",
|
||||
"type": "date",
|
||||
"label": "Tarikh Lahir",
|
||||
"validation": "required"
|
||||
},
|
||||
{
|
||||
"name": "tempat_lahir_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Tempat Lahir",
|
||||
"placeholder": "Masukkan tempat lahir"
|
||||
},
|
||||
{
|
||||
"name": "bangsa_tanggungan",
|
||||
"type": "select",
|
||||
"label": "Bangsa",
|
||||
"options": [
|
||||
{ "label": "Melayu", "value": "melayu" },
|
||||
{ "label": "Cina", "value": "cina" },
|
||||
{ "label": "India", "value": "india" },
|
||||
{ "label": "Lain-lain (Sila Nyatakan)", "value": "lain_lain" }
|
||||
],
|
||||
"validation": "required"
|
||||
},
|
||||
{
|
||||
"name": "bangsa_lain_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Nyatakan Bangsa Lain-lain",
|
||||
"placeholder": "Sila nyatakan"
|
||||
},
|
||||
{
|
||||
"name": "status_kahwin_tanggungan",
|
||||
"type": "select",
|
||||
"label": "Status Perkahwinan",
|
||||
"options": [
|
||||
{ "label": "Berkahwin", "value": "berkahwin" },
|
||||
{
|
||||
"label": "Ibu Tinggal/Bapa Tinggal",
|
||||
"value": "ibu_bapa_tinggal"
|
||||
},
|
||||
{ "label": "Bujang", "value": "bujang" },
|
||||
{ "label": "Duda", "value": "duda" },
|
||||
{ "label": "Janda", "value": "janda" },
|
||||
{ "label": "Balu", "value": "balu" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"help": "Jika Berkenaan",
|
||||
"name": "tarikh_masuk_islam_tanggungan",
|
||||
"type": "date",
|
||||
"label": "Tarikh Masuk Islam"
|
||||
},
|
||||
{
|
||||
"help": "Jika Berkenaan",
|
||||
"name": "tarikh_mula_kfam_tanggungan",
|
||||
"type": "date",
|
||||
"label": "Tarikh Mula KFAM"
|
||||
},
|
||||
{
|
||||
"help": "Jika Berkenaan",
|
||||
"name": "warganegara_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Warganegara",
|
||||
"placeholder": "Masukkan warganegara"
|
||||
},
|
||||
{
|
||||
"name": "tempat_menetap_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Tempoh Menetap Di Selangor",
|
||||
"placeholder": "Contoh: 5 Tahun"
|
||||
},
|
||||
{
|
||||
"name": "no_telefon_tanggungan",
|
||||
"type": "text",
|
||||
"label": "No. Telefon/Telefon Bimbit",
|
||||
"placeholder": "Contoh: 03-12345678"
|
||||
}
|
||||
],
|
||||
"maxItems": 10,
|
||||
"minItems": 1,
|
||||
"buttonText": "Tambah Tanggungan",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "Sila pilih jenis pengenalan",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "no_pengenalan_tanggungan",
|
||||
"type": "text",
|
||||
"label": "No. Pengenalan",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "Masukkan nombor pengenalan",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "radio",
|
||||
"props": {
|
||||
"name": "jantina_tanggungan",
|
||||
"type": "radio",
|
||||
"label": "Jantina",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Lelaki", "value": "lelaki" },
|
||||
{ "label": "Perempuan", "value": "perempuan" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "date",
|
||||
"props": {
|
||||
"name": "tarikh_lahir_tanggungan",
|
||||
"type": "date",
|
||||
"label": "Tarikh Lahir",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "tempat_lahir_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Tempat Lahir",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan tempat lahir",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"name": "bangsa_tanggungan",
|
||||
"type": "select",
|
||||
"label": "Bangsa",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Sila Pilih Bangsa", "value": "" },
|
||||
{ "label": "Melayu", "value": "melayu" },
|
||||
{ "label": "Cina", "value": "cina" },
|
||||
{ "label": "India", "value": "india" },
|
||||
{ "label": "Lain-lain (Sila Nyatakan)", "value": "lain_lain" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "Sila pilih bangsa",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "bangsa_lain_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Nyatakan Bangsa Lain-lain",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Sila nyatakan bangsa lain-lain",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
"operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "bangsa_tanggungan",
|
||||
"value": "lain_lain",
|
||||
"operator": "not_equals"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"name": "status_kahwin_tanggungan",
|
||||
"type": "select",
|
||||
"label": "Status Perkahwinan",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Sila Pilih Status Perkahwinan", "value": "" },
|
||||
{ "label": "Berkahwin", "value": "berkahwin" },
|
||||
{ "label": "Ibu Tinggal/Bapa Tinggal", "value": "ibu_bapa_tinggal" },
|
||||
{ "label": "Bujang", "value": "bujang" },
|
||||
{ "label": "Duda", "value": "duda" },
|
||||
{ "label": "Janda", "value": "janda" },
|
||||
{ "label": "Balu", "value": "balu" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Sila pilih status perkahwinan",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "date",
|
||||
"props": {
|
||||
"help": "Jika Berkenaan",
|
||||
"name": "tarikh_masuk_islam_tanggungan",
|
||||
"type": "date",
|
||||
"label": "Tarikh Masuk Islam",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "date",
|
||||
"props": {
|
||||
"help": "Jika Berkenaan",
|
||||
"name": "tarikh_mula_kfam_tanggungan",
|
||||
"type": "date",
|
||||
"label": "Tarikh Mula KFAM",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Jika Berkenaan",
|
||||
"name": "warganegara_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Warganegara",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan warganegara",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "tempat_menetap_tanggungan",
|
||||
"type": "text",
|
||||
"label": "Tempoh Menetap Di Selangor",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Contoh: 5 Tahun",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "no_telefon_tanggungan",
|
||||
"type": "text",
|
||||
"label": "No. Telefon/Telefon Bimbit",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Contoh: 03-12345678",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"removeText": "Buang"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -721,13 +569,7 @@
|
||||
"level": 3,
|
||||
"value": "MAKLUMAT PERBANKAN (Jika Berkenaan)",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"gridColumn": "span 12"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -738,14 +580,7 @@
|
||||
"label": "Nama Pemegang Akaun",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan nama pemegang akaun",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"placeholder": "Masukkan nama pemegang akaun"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -756,14 +591,7 @@
|
||||
"label": "Bank",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan nama bank",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"placeholder": "Masukkan nama bank"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -774,14 +602,7 @@
|
||||
"label": "No. Akaun Bank",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan nombor akaun",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"placeholder": "Masukkan nombor akaun"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -799,14 +620,7 @@
|
||||
{ "label": "Muflis", "value": "muflis" },
|
||||
{ "label": "Disenarai Hitam", "value": "disenarai_hitam" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"gridColumn": "span 12"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -817,7 +631,6 @@
|
||||
"label": "Sebab Pembayaran Tunai",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Sila nyatakan sebab",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
@ -840,13 +653,7 @@
|
||||
"level": 3,
|
||||
"value": "2. PENDIDIKAN",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"gridColumn": "span 12"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -861,13 +668,7 @@
|
||||
{ "label": "Tidak", "value": "tidak" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"validation": "required"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -897,14 +698,7 @@
|
||||
{ "label": "IPTA/IPTS", "value": "ipta_ipts" },
|
||||
{ "label": "Maahad Tahfiz", "value": "maahad_tahfiz" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"gridColumn": "span 12"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -915,8 +709,7 @@
|
||||
"label": "Nyatakan Pendidikan Lain-lain",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Sila nyatakan pendidikan lain-lain",
|
||||
"placeholder": "Sila nyatakan",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
@ -932,111 +725,51 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"type": "repeating-group",
|
||||
"props": {
|
||||
"name": "nama_sekolah",
|
||||
"type": "text",
|
||||
"label": "Nama Sekolah/Institusi",
|
||||
"help": "Tambah maklumat sekolah untuk setiap tanggungan yang bersekolah",
|
||||
"name": "maklumat_sekolah",
|
||||
"label": "Nama dan Alamat Sekolah/Institusi",
|
||||
"width": "100%",
|
||||
"fields": [
|
||||
{
|
||||
"name": "nama_sekolah",
|
||||
"type": "text",
|
||||
"label": "Nama Sekolah",
|
||||
"validation": "required",
|
||||
"placeholder": "Masukkan nama sekolah/institusi"
|
||||
},
|
||||
{
|
||||
"name": "alamat_sekolah",
|
||||
"type": "textarea",
|
||||
"label": "Alamat Sekolah",
|
||||
"validation": "required",
|
||||
"placeholder": "Masukkan alamat lengkap sekolah/institusi"
|
||||
},
|
||||
{
|
||||
"name": "daerah_sekolah",
|
||||
"type": "text",
|
||||
"label": "Daerah",
|
||||
"placeholder": "Masukkan daerah"
|
||||
},
|
||||
{
|
||||
"name": "negeri_sekolah",
|
||||
"type": "text",
|
||||
"label": "Negeri",
|
||||
"placeholder": "Masukkan negeri"
|
||||
},
|
||||
{
|
||||
"name": "poskod_sekolah",
|
||||
"type": "text",
|
||||
"label": "Poskod",
|
||||
"placeholder": "Masukkan poskod"
|
||||
}
|
||||
],
|
||||
"maxItems": 10,
|
||||
"minItems": 0,
|
||||
"buttonText": "Tambah Sekolah",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan nama sekolah/institusi",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
"operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "bersekolah_tanggungan",
|
||||
"value": "ya",
|
||||
"operator": "not_equals"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "textarea",
|
||||
"props": {
|
||||
"name": "alamat_sekolah",
|
||||
"type": "textarea",
|
||||
"label": "Alamat Sekolah/Institusi",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan alamat lengkap sekolah/institusi",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
"operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "bersekolah_tanggungan",
|
||||
"value": "ya",
|
||||
"operator": "not_equals"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "daerah_sekolah",
|
||||
"type": "text",
|
||||
"label": "Daerah",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan daerah",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
"operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "bersekolah_tanggungan",
|
||||
"value": "ya",
|
||||
"operator": "not_equals"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "negeri_sekolah",
|
||||
"type": "text",
|
||||
"label": "Negeri",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan negeri",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
"operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "bersekolah_tanggungan",
|
||||
"value": "ya",
|
||||
"operator": "not_equals"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "poskod_sekolah",
|
||||
"type": "text",
|
||||
"label": "Poskod",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Masukkan poskod",
|
||||
"removeText": "Buang",
|
||||
"conditionalLogic": {
|
||||
"action": "hide",
|
||||
"enabled": true,
|
||||
@ -1063,120 +796,7 @@
|
||||
{ "label": "Tidak", "value": "tidak" },
|
||||
{ "label": "Asrama", "value": "asrama" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_family_tree",
|
||||
"level": 2,
|
||||
"value": "PILIHAN FAMILY TREE",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_family_tree",
|
||||
"value": "Sila pilih sama ada anda ingin melihat family tree atau tidak.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "radio",
|
||||
"props": {
|
||||
"help": "Pilih sama ada ingin melihat family tree atau tidak",
|
||||
"name": "lihat_family_tree",
|
||||
"type": "radio",
|
||||
"label": "Lihat Family Tree?",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Ya", "value": "ya" },
|
||||
{ "label": "Tidak", "value": "tidak" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_kategori_asnaf",
|
||||
"level": 2,
|
||||
"value": "PILIHAN KATEGORI ASNAF",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_kategori_asnaf",
|
||||
"value": "Sila pilih kategori asnaf yang bersesuaian.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "radio",
|
||||
"props": {
|
||||
"help": "Pilih kategori asnaf",
|
||||
"name": "kategori_asnaf",
|
||||
"type": "radio",
|
||||
"label": "Kategori Asnaf",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Fakir Miskin", "value": "fakir_miskin" },
|
||||
{ "label": "Bukan Fakir Miskin", "value": "bukan_fakir_miskin" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
"gridColumn": "span 12"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,263 +0,0 @@
|
||||
# System Architecture
|
||||
|
||||
This document provides a technical overview of the Corrad ProcessMaker architecture, detailing how the different components interact and the data flows through the system.
|
||||
|
||||
## High-Level Architecture
|
||||
|
||||
The application follows a modern web architecture pattern:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ User Interface │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Form Builder │ │Process Build│ │ Execution UI│ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ State Management │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Form Store │ │Process Store│ │Variable Store│ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ API Layer │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Form API │ │ Process API │ │ User API │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ Data Layer │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │Form Database│ │Process DB │ │ User DB │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
The frontend of Corrad ProcessMaker is built with Nuxt 3 and Vue 3, using the Composition API. It follows a component-based architecture with state management powered by Pinia.
|
||||
|
||||
### UI Components
|
||||
|
||||
1. **Rose UI Components**
|
||||
- Prefixed with `Rs` (e.g., `RsButton`, `RsCard`)
|
||||
- Form the foundation of the UI
|
||||
- Implemented using Tailwind CSS
|
||||
- Located in `/components/`
|
||||
|
||||
2. **Process Builder Components**
|
||||
- Located in `/components/process-flow/`
|
||||
- Use Vue Flow for visualization
|
||||
- Main components:
|
||||
- `ProcessFlowCanvas.vue`: Main canvas
|
||||
- `ProcessFlowNodes.js`: Node definitions
|
||||
- `GatewayConditionManager.vue`: Condition management
|
||||
- `FormSelector.vue`: Form selection in form tasks
|
||||
|
||||
3. **Form Builder Components**
|
||||
- Located in `/components/` (root)
|
||||
- Main components:
|
||||
- `FormBuilderCanvas.vue`: Main canvas
|
||||
- `FormBuilderComponents.vue`: Component library
|
||||
- `FormBuilderConfiguration.vue`: Property editor
|
||||
- `ComponentPreview.vue`: Form preview
|
||||
|
||||
### State Management
|
||||
|
||||
State is managed with Pinia stores:
|
||||
|
||||
1. **Process Builder Store** (`/stores/processBuilder.js`)
|
||||
- Manages process definitions
|
||||
- Handles node and edge operations
|
||||
- Manages process saving and loading
|
||||
|
||||
2. **Variable Store** (`/stores/variableStore.js`)
|
||||
- Manages process variables
|
||||
- Handles variable scopes (global, process, task)
|
||||
|
||||
3. **Form Builder Store** (`/stores/formBuilder.js`)
|
||||
- Manages form definitions
|
||||
- Handles component operations
|
||||
- Manages form saving and loading
|
||||
|
||||
### Routing and Pages
|
||||
|
||||
The application uses Nuxt's file-based routing:
|
||||
|
||||
- `/pages/process-builder/index.vue`: Process Builder UI
|
||||
- `/pages/process-builder/manage.vue`: Process management
|
||||
- `/pages/form-builder/index.vue`: Form Builder UI
|
||||
- `/pages/form-builder/manage.vue`: Form management
|
||||
|
||||
## Backend Architecture
|
||||
|
||||
The backend is powered by Nitro.js (part of Nuxt 3) and uses Prisma as the ORM.
|
||||
|
||||
### API Endpoints
|
||||
|
||||
API routes are defined in the `/server/api/` directory:
|
||||
|
||||
1. **Form Endpoints**
|
||||
- `/api/forms`: CRUD operations for forms
|
||||
- `/api/forms/[id]`: Operations on a specific form
|
||||
|
||||
2. **Process Endpoints**
|
||||
- `/api/processes`: CRUD operations for processes
|
||||
- `/api/processes/[id]`: Operations on a specific process
|
||||
- `/api/processes/[id]/execute`: Process execution
|
||||
|
||||
3. **User and Authentication Endpoints**
|
||||
- `/api/auth`: Authentication endpoints
|
||||
- `/api/users`: User management
|
||||
|
||||
### Middleware
|
||||
|
||||
Middleware is defined in `/server/middleware/` and `/middleware/`:
|
||||
|
||||
- Authentication middleware
|
||||
- Audit logging middleware
|
||||
- Error handling middleware
|
||||
|
||||
## Database Architecture
|
||||
|
||||
The database schema is defined in Prisma schema (`/prisma/schema.prisma`) and includes the following models:
|
||||
|
||||
1. **User and Role Models**
|
||||
- `user`: User information
|
||||
- `role`: Role definitions
|
||||
- `userrole`: User-role associations
|
||||
|
||||
2. **Form Model**
|
||||
- Stores form definitions
|
||||
- Components stored as JSON
|
||||
- Associated with creators
|
||||
|
||||
3. **Process Model**
|
||||
- Stores process definitions
|
||||
- Process nodes and edges stored as JSON
|
||||
- Associated with creators
|
||||
|
||||
4. **Task Model**
|
||||
- Stores task instances
|
||||
- Linked to processes and forms
|
||||
- Tracks task status and assignees
|
||||
|
||||
5. **Audit Model**
|
||||
- Logs system actions
|
||||
- Tracks changes to entities
|
||||
- Associated with users
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Form Builder Flow
|
||||
|
||||
1. User designs a form in the Form Builder
|
||||
2. Form components and properties are stored in the Form Builder store
|
||||
3. When saved, the form definition is sent to the server via API
|
||||
4. The server stores the form in the database
|
||||
5. The form becomes available for use in processes
|
||||
|
||||
### Process Builder Flow
|
||||
|
||||
1. User designs a process in the Process Builder
|
||||
2. Process nodes, edges, and properties are stored in the Process Builder store
|
||||
3. When saved, the process definition is sent to the server via API
|
||||
4. The server stores the process in the database
|
||||
5. The process becomes available for execution
|
||||
|
||||
### Process Execution Flow
|
||||
|
||||
1. A process is started through the API
|
||||
2. The server creates a process instance
|
||||
3. As the process executes, it creates tasks
|
||||
4. Users are assigned tasks based on the process definition
|
||||
5. When form tasks are encountered, users fill out forms
|
||||
6. Form data is stored in process variables
|
||||
7. Decision Points determine the next path
|
||||
8. The process continues until completion
|
||||
|
||||
## Security Architecture
|
||||
|
||||
1. **Authentication**
|
||||
- Username/password authentication
|
||||
- JWT tokens for session management
|
||||
- Token refresh mechanism
|
||||
|
||||
2. **Authorization**
|
||||
- Role-based access control
|
||||
- Permission checks at API endpoints
|
||||
- UI-level permission handling
|
||||
|
||||
3. **Data Security**
|
||||
- Password hashing
|
||||
- Input validation and sanitization
|
||||
- Database query parameterization
|
||||
|
||||
## Deployment Architecture
|
||||
|
||||
The application is designed to be deployed in various environments:
|
||||
|
||||
1. **Development**
|
||||
- Local development server
|
||||
- Hot module replacement
|
||||
- Development database
|
||||
|
||||
2. **Production**
|
||||
- Server-side rendering
|
||||
- Static asset optimization
|
||||
- Database connection pooling
|
||||
- PM2 process management
|
||||
|
||||
## Integration Points
|
||||
|
||||
The system offers several integration points:
|
||||
|
||||
1. **API Endpoints**
|
||||
- RESTful API for external system integration
|
||||
- Webhook support for notifications
|
||||
|
||||
2. **Database**
|
||||
- Direct database access for reporting
|
||||
- Database triggers for advanced integrations
|
||||
|
||||
3. **Authentication**
|
||||
- Potential for SSO integration
|
||||
- External identity provider support
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Frontend Optimization**
|
||||
- Component lazy loading
|
||||
- State management optimization
|
||||
- UI rendering performance
|
||||
|
||||
2. **Backend Optimization**
|
||||
- Database query optimization
|
||||
- API response caching
|
||||
- Server-side rendering
|
||||
|
||||
3. **Database Optimization**
|
||||
- Indexing strategy
|
||||
- Query optimization
|
||||
- Connection pooling
|
||||
|
||||
## Future Architecture Considerations
|
||||
|
||||
1. **Microservices**
|
||||
- Potential split into microservices for better scaling
|
||||
- Separate services for forms, processes, and user management
|
||||
|
||||
2. **Real-time Features**
|
||||
- WebSocket integration for real-time updates
|
||||
- Notification system
|
||||
|
||||
3. **Scalability**
|
||||
- Horizontal scaling for API servers
|
||||
- Database sharding and replication
|
||||
- Caching layer
|
@ -1,217 +0,0 @@
|
||||
# Development Guide
|
||||
|
||||
This guide is intended for developers who are new to the Corrad ProcessMaker project. It covers the setup process, coding conventions, and tips for working with the major components of the system.
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js (v16+)
|
||||
- MySQL/MariaDB database
|
||||
- Git
|
||||
- Yarn (recommended) or npm
|
||||
|
||||
### Local Development Environment
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone [repository-url]
|
||||
cd corrad-processmaker
|
||||
```
|
||||
|
||||
2. **Install dependencies**
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
3. **Set up environment variables**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env with your database connection details
|
||||
```
|
||||
|
||||
4. **Generate Prisma client**
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
5. **Start the development server**
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
6. **Access the application**
|
||||
Open your browser and navigate to `http://localhost:3000`
|
||||
|
||||
## Project Structure
|
||||
|
||||
The project follows a standard Nuxt 3 directory structure with some additional folders:
|
||||
|
||||
```
|
||||
├── assets/ # Static assets (SCSS, images)
|
||||
├── components/ # Vue components
|
||||
│ ├── process-flow/ # Process Builder components
|
||||
│ ├── formkit/ # Custom FormKit components
|
||||
│ └── rs-*/ # Rose UI components
|
||||
├── composables/ # Reusable Vue composables
|
||||
├── doc/ # Documentation
|
||||
├── layouts/ # Page layouts
|
||||
├── middleware/ # Nuxt middleware
|
||||
├── pages/ # Routes and pages
|
||||
├── plugins/ # Vue plugins
|
||||
├── prisma/ # Database schema and migrations
|
||||
├── public/ # Public static files
|
||||
├── server/ # API routes and server middleware
|
||||
└── stores/ # Pinia stores
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- **Components**: Use PascalCase for component names (e.g., `ProcessFlowCanvas.vue`)
|
||||
- **Files**: Use kebab-case for filenames (e.g., `process-builder.js`)
|
||||
- **Custom Rose UI components**: Prefix with `Rs` (e.g., `RsButton.vue`)
|
||||
- **FormKit custom components**: Use PascalCase (e.g., `DateTimePicker.vue`)
|
||||
- **Stores**: Use camelCase (e.g., `processBuilder.js`)
|
||||
- **API routes**: Use kebab-case (e.g., `/api/forms/get-all`)
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### Vue Components
|
||||
|
||||
- Use the Composition API with `<script setup>` for new components
|
||||
- Define props using `defineProps()` with proper types
|
||||
- Use emits with `defineEmits()`
|
||||
- Keep components focused on a single responsibility
|
||||
- Extract reusable logic to composables
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
// Good example
|
||||
const props = defineProps({
|
||||
nodeId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nodeType: {
|
||||
type: String,
|
||||
default: 'task'
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update', 'delete']);
|
||||
|
||||
// Component logic
|
||||
</script>
|
||||
```
|
||||
|
||||
### CSS Guidelines
|
||||
|
||||
- Use Tailwind CSS utility classes when possible
|
||||
- For custom styles, use SCSS with scoped styles
|
||||
- Follow the BEM naming convention for custom CSS classes
|
||||
- Maintain a consistent color scheme using Tailwind's theme colors
|
||||
|
||||
### State Management
|
||||
|
||||
- Use Pinia stores for shared state
|
||||
- Define clear actions, getters, and state
|
||||
- Use state composition for complex stores
|
||||
- Document store functions
|
||||
|
||||
## Working with Key Components
|
||||
|
||||
### Process Builder
|
||||
|
||||
The Process Builder uses Vue Flow for visualization and consists of several components:
|
||||
|
||||
- `ProcessFlowCanvas.vue`: Main canvas component
|
||||
- `ProcessFlowNodes.js`: Node type definitions
|
||||
- `GatewayConditionManager.vue`: Manages gateway conditions
|
||||
- `VariableManager.vue`: Manages process variables
|
||||
|
||||
To add a new node type:
|
||||
|
||||
1. Define the node type in `ProcessFlowNodes.js`
|
||||
2. Add it to the component palette in `ProcessBuilderComponents.vue`
|
||||
3. Update the node properties panel in the Process Builder page
|
||||
4. Add any necessary handlers in the store
|
||||
|
||||
### Form Builder
|
||||
|
||||
The Form Builder uses FormKit and consists of these main components:
|
||||
|
||||
- `FormBuilderCanvas.vue`: Main canvas for form layout
|
||||
- `FormBuilderComponents.vue`: Component library/palette
|
||||
- `FormBuilderConfiguration.vue`: Component property editor
|
||||
- `ComponentPreview.vue`: Live preview of forms
|
||||
|
||||
To add a new form component:
|
||||
|
||||
1. Add the component definition to the component library
|
||||
2. Add configuration options to the configuration panel
|
||||
3. Update the FormKit component if necessary
|
||||
4. Add any special rendering logic to the preview component
|
||||
|
||||
## API Integration
|
||||
|
||||
The backend API is built with Nitro.js and follows RESTful conventions:
|
||||
|
||||
- API routes are defined in the `server/api/` directory
|
||||
- Route handlers use Prisma for database operations
|
||||
- Authentication is handled via middleware
|
||||
|
||||
Example API route:
|
||||
|
||||
```js
|
||||
// server/api/forms/index.js
|
||||
export default defineEventHandler(async (event) => {
|
||||
// Get query parameters
|
||||
const query = getQuery(event);
|
||||
|
||||
// Get forms from database
|
||||
const forms = await prisma.form.findMany({
|
||||
where: {
|
||||
status: 'active'
|
||||
},
|
||||
orderBy: {
|
||||
createdDate: 'desc'
|
||||
}
|
||||
});
|
||||
|
||||
return forms;
|
||||
});
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
TODO: Add testing guidelines once testing is implemented.
|
||||
|
||||
## Deployment
|
||||
|
||||
For production deployment:
|
||||
|
||||
1. Build the application:
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
2. Start the production server:
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
Alternatively, you can use PM2 for process management:
|
||||
```bash
|
||||
pm2 start ecosystem.config.js
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
- Check the existing documentation in the `doc/` directory
|
||||
- Read the codebase, particularly the main components
|
||||
- Ask for help from other team members
|
||||
- Consult the Nuxt, Vue, and FormKit documentation for framework-specific questions
|
||||
|
||||
---
|
||||
|
||||
Remember that this is a complex application. Take time to understand how the components interact before making significant changes.
|
@ -1,158 +0,0 @@
|
||||
# Corrad ProcessMaker - Project Overview
|
||||
|
||||
## Introduction
|
||||
|
||||
Corrad ProcessMaker is a comprehensive Business Process Management (BPM) platform designed to help organizations automate workflows and streamline business processes. It combines a visual process designer with a powerful form builder to create end-to-end business process solutions.
|
||||
|
||||
## System Architecture
|
||||
|
||||
The application follows a modern web architecture:
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Frontend │ │ Backend │ │ Database │
|
||||
│ (Nuxt/Vue) │────▶│ (Nitro.js) │────▶│ (MySQL) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### Frontend
|
||||
- Built with Nuxt 3 and Vue 3
|
||||
- Uses Pinia for state management
|
||||
- Rich UI components built with Tailwind CSS
|
||||
- Form components powered by FormKit
|
||||
- Process visualization with Vue Flow
|
||||
|
||||
### Backend
|
||||
- NitroJS server (part of Nuxt 3 ecosystem)
|
||||
- RESTful API endpoints
|
||||
- Server middleware for authentication
|
||||
- Database interactions via Prisma ORM
|
||||
|
||||
### Database
|
||||
- MySQL/MariaDB relational database
|
||||
- Schema managed via Prisma
|
||||
- Key tables:
|
||||
- Users and Roles
|
||||
- Forms
|
||||
- Processes
|
||||
- Tasks
|
||||
- Audit logs
|
||||
|
||||
## Core Modules
|
||||
|
||||
### Process Builder
|
||||
|
||||
The Process Builder is the heart of the system, allowing users to design executable business processes following the BPMN standard. Key features include:
|
||||
|
||||
- Visual process design with drag-and-drop interface
|
||||
- Support for various node types:
|
||||
- Start/End events
|
||||
- Tasks (manual, form, script)
|
||||
- Decision Points for branching workflows
|
||||
- Connection management between nodes
|
||||
- Properties panel for node configuration
|
||||
- Variable management for process data
|
||||
- Integration with forms
|
||||
|
||||
### Form Builder
|
||||
|
||||
The Form Builder complements the Process Builder, enabling users to create forms that integrate with process tasks. Features include:
|
||||
|
||||
- Drag-and-drop form designer
|
||||
- Rich component library
|
||||
- Text inputs, select dropdowns, checkboxes
|
||||
- Date/time pickers, file uploads
|
||||
- Layout components
|
||||
- Real-time form preview
|
||||
- Configuration panel for component properties
|
||||
- Form versioning and management
|
||||
- Integration with processes
|
||||
|
||||
### Process Execution
|
||||
|
||||
The Process Execution module provides the end-user interface for interacting with deployed workflow processes. Key features include:
|
||||
|
||||
- Execution Dashboard for process overview
|
||||
- Task statistics and metrics
|
||||
- Recent tasks and processes
|
||||
- Task Inbox for managing assigned tasks
|
||||
- Task filtering and search
|
||||
- Status indicators and due dates
|
||||
- New Case interface for initiating processes
|
||||
- Process catalog with descriptions
|
||||
- Categorized process listing
|
||||
- Case History for tracking process instances
|
||||
- Historical record of completed processes
|
||||
- Timeline visualization of process steps
|
||||
- Task Detail interface for completing tasks
|
||||
- Dynamic form rendering
|
||||
- Case context and variables
|
||||
- Process timeline visualization
|
||||
|
||||
## User Flows
|
||||
|
||||
### Process Design and Execution
|
||||
|
||||
1. User creates a new process in Process Builder
|
||||
2. User adds and configures process nodes (start, tasks, gateways, end)
|
||||
3. User connects nodes to establish the process flow
|
||||
4. For form tasks, user selects or creates forms
|
||||
5. User sets conditions for gateways
|
||||
6. User deploys the process
|
||||
7. The process becomes available for execution
|
||||
8. Users receive tasks based on assignments
|
||||
9. Process progresses as tasks are completed
|
||||
|
||||
### Form Creation and Usage
|
||||
|
||||
1. User creates a new form in Form Builder
|
||||
2. User adds and configures form components
|
||||
3. User sets validation rules and field properties
|
||||
4. User saves and publishes the form
|
||||
5. The form becomes available for use in processes
|
||||
6. When a form task is encountered in a process, the assigned user sees the form
|
||||
7. Form submissions store data in the process variables
|
||||
|
||||
### Process Participation
|
||||
|
||||
1. User logs into the system and views their task inbox
|
||||
2. User opens and completes assigned tasks
|
||||
3. User submits task forms with required data
|
||||
4. Process advances based on task completion
|
||||
5. User can view process history and status
|
||||
6. User can initiate new process instances
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. Process execution begins at a start event
|
||||
2. As the process moves through tasks, data is collected and transformed
|
||||
3. Forms collect user input and store it in process variables
|
||||
4. Gateway conditions evaluate process variables to determine flow paths
|
||||
5. Script tasks manipulate process data
|
||||
6. Process completion provides output data
|
||||
|
||||
## Authentication and Authorization
|
||||
|
||||
- User authentication via username/password
|
||||
- Role-based access control
|
||||
- Permission management for processes and forms
|
||||
- Audit logging of user actions
|
||||
|
||||
## Integration Points
|
||||
|
||||
The system is designed for extensibility with several integration points:
|
||||
|
||||
- API endpoints for external system communication
|
||||
- Script tasks for custom code execution
|
||||
- Database connections for data persistence
|
||||
- Authentication integration with external identity providers
|
||||
|
||||
## Future Development Areas
|
||||
|
||||
- Process templates and versioning
|
||||
- Advanced analytics and reporting
|
||||
- Mobile-friendly execution interface
|
||||
- Expanded notification system
|
||||
- Enhanced variable validation
|
||||
- Subprocess support
|
||||
- API expansion for 3rd party integration
|
@ -1,383 +0,0 @@
|
||||
# Business Rules Node Troubleshooting Guide
|
||||
|
||||
## Overview
|
||||
The Business Rules node allows you to define complex conditional logic in your process flows. This guide addresses common issues with variable dropdowns and operator selection.
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### 1. Variable Dropdown Not Populating
|
||||
|
||||
**Problem**: The variable dropdown in business rules conditions shows "Select variable" but no options appear.
|
||||
|
||||
**Root Causes**:
|
||||
- Process variables are not properly loaded in the store
|
||||
- Variable types are not correctly defined
|
||||
- The `gatewayAvailableVariables` computed property is not updating
|
||||
|
||||
**Solutions**:
|
||||
|
||||
#### A. Check Variable Store State
|
||||
```javascript
|
||||
// In browser console, check if variables are loaded:
|
||||
console.log('Current process variables:', processStore.currentProcess?.variables);
|
||||
console.log('Gateway available variables:', gatewayAvailableVariables.value);
|
||||
```
|
||||
|
||||
#### B. Ensure Proper Variable Types
|
||||
Variables must use these specific types that match the VariableBrowser component:
|
||||
- `string` - for text values
|
||||
- `int` - for whole numbers
|
||||
- `decimal` - for decimal numbers
|
||||
- `boolean` - for true/false values
|
||||
- `date` - for date values
|
||||
- `datetime` - for date-time values
|
||||
- `object` - for JSON objects
|
||||
|
||||
**Incorrect**:
|
||||
```json
|
||||
{
|
||||
"myVariable": {
|
||||
"type": "number", // ❌ Should be "int" or "decimal"
|
||||
"value": 123
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Correct**:
|
||||
```json
|
||||
{
|
||||
"myVariable": {
|
||||
"type": "int", // ✅ Correct type
|
||||
"value": 123
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### C. Force Component Re-render
|
||||
If variables exist but dropdown doesn't update, the issue is with reactivity. The component uses a `variablesUpdateKey` to force re-renders:
|
||||
|
||||
```javascript
|
||||
// This key changes when variables are updated
|
||||
const variablesUpdateKey = computed(() => {
|
||||
// Creates hash of variable names to detect changes
|
||||
const variableNames = Object.keys(processStore.currentProcess.variables).sort().join(',');
|
||||
return `vars-${variableNames.length}-${hash}`;
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Operator Dropdown Not Showing
|
||||
|
||||
**Problem**: After selecting a variable, the operator dropdown remains empty.
|
||||
|
||||
**Root Cause**: The `getOperatorsForType()` function cannot determine the variable type.
|
||||
|
||||
**Solution**: Ensure the variable has a proper type. The function expects these types:
|
||||
|
||||
```javascript
|
||||
const getOperatorsForType = (variableType) => {
|
||||
switch (variableType?.toLowerCase()) {
|
||||
case 'string':
|
||||
return [
|
||||
{ value: 'eq', label: '= (Equal to)' },
|
||||
{ value: 'neq', label: '≠ (Not equal to)' },
|
||||
{ value: 'contains', label: 'Contains' },
|
||||
{ value: 'not_contains', label: 'Does not contain' },
|
||||
{ value: 'starts_with', label: 'Starts with' },
|
||||
{ value: 'ends_with', label: 'Ends with' },
|
||||
{ value: 'is_empty', label: 'Is empty' },
|
||||
{ value: 'is_not_empty', label: 'Is not empty' }
|
||||
];
|
||||
case 'int':
|
||||
case 'decimal':
|
||||
return [
|
||||
{ value: 'eq', label: '= (Equal to)' },
|
||||
{ value: 'neq', label: '≠ (Not equal to)' },
|
||||
{ value: 'gt', label: '> (Greater than)' },
|
||||
{ value: 'gte', label: '≥ (Greater than or equal to)' },
|
||||
{ value: 'lt', label: '< (Less than)' },
|
||||
{ value: 'lte', label: '≤ (Less than or equal to)' },
|
||||
{ value: 'between', label: 'Between (inclusive)' }
|
||||
];
|
||||
case 'boolean':
|
||||
return [
|
||||
{ value: 'eq', label: '= (Equal to)' },
|
||||
{ value: 'is_true', label: 'Is True' },
|
||||
{ value: 'is_false', label: 'Is False' }
|
||||
];
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
return [
|
||||
{ value: 'eq', label: '= (Equal to)' },
|
||||
{ value: 'neq', label: '≠ (Not equal to)' },
|
||||
{ value: 'gt', label: '> (After)' },
|
||||
{ value: 'gte', label: '≥ (On or after)' },
|
||||
{ value: 'lt', label: '< (Before)' },
|
||||
{ value: 'lte', label: '≤ (On or before)' },
|
||||
{ value: 'between', label: 'Between dates' }
|
||||
];
|
||||
default:
|
||||
return [
|
||||
{ value: 'eq', label: '= (Equal to)' },
|
||||
{ value: 'neq', label: '≠ (Not equal to)' }
|
||||
];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Variable Browser Component Issues
|
||||
|
||||
**Problem**: VariableBrowser component doesn't show variables even when they exist.
|
||||
|
||||
**Debugging Steps**:
|
||||
|
||||
1. **Check Props**: Ensure `availableVariables` prop is passed correctly:
|
||||
```vue
|
||||
<VariableBrowser
|
||||
v-model="condition.variable"
|
||||
:availableVariables="props.availableVariables" <!-- ✅ Correct prop passing -->
|
||||
:allowCreate="true"
|
||||
@change="updateConditionVariable(groupIndex, condIndex)"
|
||||
/>
|
||||
```
|
||||
|
||||
2. **Verify Variable Structure**: Each variable must have this structure:
|
||||
```javascript
|
||||
{
|
||||
name: "variableName",
|
||||
type: "string|int|decimal|boolean|date|datetime|object",
|
||||
scope: "global|local",
|
||||
description: "Variable description",
|
||||
currentValue: null // Optional current value for preview
|
||||
}
|
||||
```
|
||||
|
||||
3. **Check Grouping Logic**: The VariableBrowser groups variables by type:
|
||||
```javascript
|
||||
const groupedVariables = computed(() => {
|
||||
const types = {
|
||||
string: { label: 'Text Variables', variables: [] },
|
||||
int: { label: 'Integer Variables', variables: [] },
|
||||
decimal: { label: 'Decimal Variables', variables: [] },
|
||||
boolean: { label: 'Boolean Variables', variables: [] },
|
||||
date: { label: 'Date Variables', variables: [] },
|
||||
datetime: { label: 'DateTime Variables', variables: [] },
|
||||
object: { label: 'Object Variables', variables: [] }
|
||||
};
|
||||
|
||||
// Variables are grouped by type
|
||||
variables.forEach(variable => {
|
||||
const type = variable.type || 'string';
|
||||
if (types[type]) {
|
||||
types[type].variables.push(variable);
|
||||
}
|
||||
});
|
||||
|
||||
return Object.values(types).filter(group => group.variables.length > 0);
|
||||
});
|
||||
```
|
||||
|
||||
## Correct Business Rule Structure
|
||||
|
||||
### Complete Working Example
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "business-rule-123",
|
||||
"type": "business-rule",
|
||||
"label": "Eligibility Check",
|
||||
"position": { "x": 400, "y": 200 },
|
||||
"data": {
|
||||
"label": "Eligibility Check",
|
||||
"description": "Check if applicant meets eligibility criteria",
|
||||
"ruleGroups": [
|
||||
{
|
||||
"id": "group-1",
|
||||
"name": "Age and Status Check",
|
||||
"operator": "AND",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-1-1",
|
||||
"variable": "applicantAge",
|
||||
"operator": "greater_than",
|
||||
"value": 18
|
||||
},
|
||||
{
|
||||
"id": "condition-1-2",
|
||||
"variable": "maritalStatus",
|
||||
"operator": "equals",
|
||||
"value": "married"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"id": "action-1-1",
|
||||
"type": "set_variable",
|
||||
"variable": "isEligible",
|
||||
"value": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputVariable": "ruleResult",
|
||||
"errorVariable": "ruleError"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Required Process Variables
|
||||
|
||||
```json
|
||||
{
|
||||
"applicantAge": {
|
||||
"name": "applicantAge",
|
||||
"type": "int",
|
||||
"scope": "global",
|
||||
"value": 25,
|
||||
"description": "Applicant's age in years"
|
||||
},
|
||||
"maritalStatus": {
|
||||
"name": "maritalStatus",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "married",
|
||||
"description": "Applicant's marital status"
|
||||
},
|
||||
"isEligible": {
|
||||
"name": "isEligible",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Whether applicant is eligible"
|
||||
},
|
||||
"ruleResult": {
|
||||
"name": "ruleResult",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Business rule execution result"
|
||||
},
|
||||
"ruleError": {
|
||||
"name": "ruleError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Business rule execution error"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### 1. Browser Console Debugging
|
||||
```javascript
|
||||
// Check if variables are loaded
|
||||
console.log('Process variables:', processStore.currentProcess?.variables);
|
||||
|
||||
// Check available variables for business rules
|
||||
console.log('Available variables:', gatewayAvailableVariables.value);
|
||||
|
||||
// Check selected node data
|
||||
console.log('Selected node:', selectedNodeData.value);
|
||||
|
||||
// Debug operator dropdown issue
|
||||
const testVariable = 'selectStatusperkahwinan';
|
||||
const foundVar = gatewayAvailableVariables.value.find(v => v.name === testVariable);
|
||||
console.log('Found variable:', foundVar);
|
||||
console.log('Variable type:', foundVar?.type);
|
||||
|
||||
// Test getOperatorsForType function (if available in component)
|
||||
if (typeof getOperatorsForType === 'function') {
|
||||
console.log('Operators for string:', getOperatorsForType('string'));
|
||||
console.log('Operators for boolean:', getOperatorsForType('boolean'));
|
||||
console.log('Operators for decimal:', getOperatorsForType('decimal'));
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Component Key Debugging
|
||||
If the component isn't updating, check the key attribute:
|
||||
```vue
|
||||
<BusinessRuleNodeConfigurationModal
|
||||
:key="`business-rule-${selectedNodeData.id}-${variablesUpdateKey}`"
|
||||
<!-- This key should change when variables update -->
|
||||
/>
|
||||
```
|
||||
|
||||
### 3. Variable Type Validation
|
||||
```javascript
|
||||
// Validate variable types
|
||||
const validateVariableTypes = (variables) => {
|
||||
const validTypes = ['string', 'int', 'decimal', 'boolean', 'date', 'datetime', 'object'];
|
||||
|
||||
Object.values(variables).forEach(variable => {
|
||||
if (!validTypes.includes(variable.type)) {
|
||||
console.error(`Invalid variable type: ${variable.type} for ${variable.name}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always define variable types correctly** - Use the exact types expected by the VariableBrowser
|
||||
2. **Include unique IDs** - Each condition and action should have a unique ID
|
||||
3. **Test incrementally** - Add one condition at a time to isolate issues
|
||||
4. **Use descriptive names** - Make variable and rule names clear and meaningful
|
||||
5. **Check browser console** - Most issues will show up in console errors
|
||||
|
||||
## Operator Values Reference
|
||||
|
||||
Use these exact operator values in your JSON definitions:
|
||||
|
||||
### String Operators
|
||||
- `eq` - Equal to
|
||||
- `neq` - Not equal to
|
||||
- `contains` - Contains substring
|
||||
- `not_contains` - Does not contain substring
|
||||
- `starts_with` - Starts with
|
||||
- `ends_with` - Ends with
|
||||
- `is_empty` - Is empty
|
||||
- `is_not_empty` - Is not empty
|
||||
|
||||
### Number Operators (int/decimal)
|
||||
- `eq` - Equal to
|
||||
- `neq` - Not equal to
|
||||
- `gt` - Greater than
|
||||
- `gte` - Greater than or equal to
|
||||
- `lt` - Less than
|
||||
- `lte` - Less than or equal to
|
||||
- `between` - Between (inclusive)
|
||||
|
||||
### Boolean Operators
|
||||
- `eq` - Equal to
|
||||
- `is_true` - Is True
|
||||
- `is_false` - Is False
|
||||
|
||||
### Date/DateTime Operators
|
||||
- `eq` - Equal to
|
||||
- `neq` - Not equal to
|
||||
- `gt` - After
|
||||
- `gte` - On or after
|
||||
- `lt` - Before
|
||||
- `lte` - On or before
|
||||
- `between` - Between dates
|
||||
|
||||
## Common Error Messages
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| "Variable not found" | Variable name doesn't match exactly | Check spelling and case sensitivity |
|
||||
| "No operators available" | Variable type not recognized | Ensure variable type is one of the supported types |
|
||||
| "Dropdown not populating" | Variables not loaded or wrong structure | Check variable store and structure |
|
||||
| "Component not updating" | Reactivity issue | Check component key and force re-render |
|
||||
| "Operator dropdown empty" | Using wrong operator values | Use correct operator values from reference above |
|
||||
|
||||
## Final Checklist
|
||||
|
||||
Before reporting issues, verify:
|
||||
|
||||
- [ ] All variables have correct types (`string`, `int`, `decimal`, `boolean`, `date`, `datetime`, `object`)
|
||||
- [ ] Process variables are loaded in the store
|
||||
- [ ] Component receives `availableVariables` prop
|
||||
- [ ] Each condition/action has unique ID
|
||||
- [ ] Browser console shows no errors
|
||||
- [ ] Variable names match exactly between definition and usage
|
@ -1,121 +0,0 @@
|
||||
# Node Color Customization
|
||||
|
||||
## Overview
|
||||
|
||||
The process builder now supports custom colors for nodes, allowing users to personalize the appearance of their process flows. Each node (except start and end nodes) can have customized background color, border color, and text color.
|
||||
|
||||
## Features
|
||||
|
||||
### Color Settings Panel
|
||||
|
||||
When a node is selected, the properties panel includes a "Color Settings" section with:
|
||||
|
||||
- **Background Color**: Color picker and text input for the node's background
|
||||
- **Border Color**: Color picker and text input for the node's border
|
||||
- **Text Color**: Color picker and text input for ALL text content in the node (title, descriptions, details, icons)
|
||||
- **Reset Button**: Restores default colors with one click
|
||||
|
||||
### Supported Node Types
|
||||
|
||||
Color customization is available for:
|
||||
- Form nodes
|
||||
- API nodes
|
||||
- Script nodes
|
||||
- Business rule nodes
|
||||
- Notification nodes
|
||||
- Gateway/Decision nodes
|
||||
|
||||
### Shape Compatibility
|
||||
|
||||
Colors work with all supported node shapes:
|
||||
- Rectangle
|
||||
- Rounded Rectangle
|
||||
- Circle
|
||||
- Diamond
|
||||
- Hexagon
|
||||
- Parallelogram
|
||||
- Trapezoid
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select any node (except start/end nodes)
|
||||
2. In the Properties panel, locate the "Color Settings" section
|
||||
3. Use color pickers or enter hex values directly
|
||||
4. Changes apply immediately to the canvas
|
||||
5. Use "Reset to Default Colors" to restore original appearance
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Color Storage
|
||||
Colors are stored in the node's data object:
|
||||
```javascript
|
||||
{
|
||||
backgroundColor: '#ffffff',
|
||||
borderColor: '#ddd',
|
||||
textColor: '#333333'
|
||||
}
|
||||
```
|
||||
|
||||
### CSS Variables
|
||||
The implementation uses CSS custom properties for dynamic styling:
|
||||
```css
|
||||
--node-bg-color: backgroundColor
|
||||
--node-border-color: borderColor
|
||||
--node-text-color: textColor
|
||||
```
|
||||
|
||||
### Backward Compatibility
|
||||
Existing nodes without color properties automatically receive default values when selected or loaded.
|
||||
|
||||
## Default Colors
|
||||
|
||||
Each node type has its own distinctive color scheme:
|
||||
|
||||
### Form Nodes
|
||||
- **Background**: `#faf5ff` (purple tint)
|
||||
- **Border**: `#9333ea` (purple)
|
||||
- **Text**: `#6b21a8` (dark purple)
|
||||
|
||||
### API Nodes
|
||||
- **Background**: `#eff6ff` (blue tint)
|
||||
- **Border**: `#3b82f6` (blue)
|
||||
- **Text**: `#1e40af` (dark blue)
|
||||
|
||||
### Gateway/Decision Nodes
|
||||
- **Background**: `#fff7ed` (orange tint)
|
||||
- **Border**: `#f97316` (orange)
|
||||
- **Text**: `#c2410c` (dark orange)
|
||||
|
||||
### Script Nodes
|
||||
- **Background**: `#f9fafb` (gray tint)
|
||||
- **Border**: `#6b7280` (gray)
|
||||
- **Text**: `#374151` (dark gray)
|
||||
|
||||
### Business Rule Nodes
|
||||
- **Background**: `#fdf4ff` (violet tint)
|
||||
- **Border**: `#a855f7` (violet)
|
||||
- **Text**: `#7c3aed` (dark violet)
|
||||
|
||||
### Notification Nodes
|
||||
- **Background**: `#f0f9ff` (sky tint)
|
||||
- **Border**: `#0ea5e9` (sky blue)
|
||||
- **Text**: `#0284c7` (dark sky blue)
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Accessibility
|
||||
- Ensure sufficient contrast between text and background colors
|
||||
- Test readability with different color combinations
|
||||
- Consider colorblind users when choosing color schemes
|
||||
- Text color affects ALL text elements in the node (titles, descriptions, details, and icons)
|
||||
- Choose text colors that work well with your chosen background color
|
||||
|
||||
### Consistency
|
||||
- Use consistent color schemes across related processes
|
||||
- Consider using colors to indicate node types or priorities
|
||||
- Document color meanings for team collaboration
|
||||
|
||||
### Performance
|
||||
- Color changes are applied in real-time
|
||||
- Colors persist when saving/loading processes
|
||||
- No impact on process execution or functionality
|
@ -1,833 +0,0 @@
|
||||
# Process Flow Creation Guide
|
||||
|
||||
## Overview
|
||||
This guide explains how to create process flows using the node-based process builder. Process flows are defined as JSON structures containing nodes and edges that represent business processes.
|
||||
|
||||
## Process Definition Structure
|
||||
|
||||
### Root Structure
|
||||
```json
|
||||
{
|
||||
"nodes": [...],
|
||||
"edges": [...],
|
||||
"viewport": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"zoom": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Node Types
|
||||
|
||||
### 1. Start Node
|
||||
**Purpose**: Entry point for the process
|
||||
```json
|
||||
{
|
||||
"id": "start-{timestamp}",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"position": {"x": 210, "y": 180},
|
||||
"data": {
|
||||
"label": "Start",
|
||||
"description": "Process start point"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Form Node
|
||||
**Purpose**: User task requiring form completion
|
||||
```json
|
||||
{
|
||||
"id": "form-{timestamp}",
|
||||
"type": "form",
|
||||
"label": "Form Name",
|
||||
"position": {"x": 405, "y": 135},
|
||||
"data": {
|
||||
"label": "Borang Maklumat Peribadi",
|
||||
"formId": 1,
|
||||
"formName": "Borang Maklumat Peribadi",
|
||||
"formUuid": "9f08fc8f-b170-478a-85fd-fa3364401533",
|
||||
"description": "Form: Borang Maklumat Peribadi",
|
||||
"assignmentType": "public|role|user",
|
||||
"assignedRoles": [],
|
||||
"assignedUsers": [],
|
||||
"assignmentVariable": "",
|
||||
"assignmentVariableType": "user_id",
|
||||
"inputMappings": [
|
||||
{
|
||||
"formField": "reference_number",
|
||||
"processVariable": "applicationId"
|
||||
}
|
||||
],
|
||||
"outputMappings": [
|
||||
{
|
||||
"formField": "text_3",
|
||||
"processVariable": "text3"
|
||||
}
|
||||
],
|
||||
"fieldConditions": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Assignment Types:**
|
||||
- `public`: Anyone can complete the form
|
||||
- `role`: Assigned to specific roles
|
||||
- `user`: Assigned to specific users
|
||||
|
||||
### 3. API Node
|
||||
**Purpose**: External API calls
|
||||
```json
|
||||
{
|
||||
"id": "api-{timestamp}",
|
||||
"type": "api",
|
||||
"label": "Submit Profile API",
|
||||
"position": {"x": 600, "y": 135},
|
||||
"data": {
|
||||
"label": "Submit Profile API",
|
||||
"description": "Submit user profile to external system",
|
||||
"apiUrl": "https://jsonplaceholder.typicode.com/posts",
|
||||
"apiMethod": "GET|POST|PUT|DELETE|PATCH",
|
||||
"headers": "{ \"Content-Type\": \"application/json\" }",
|
||||
"requestBody": "{\n \"applicantName\": \"{text3}\",\n \"idType\": \"{select1}\"\n}",
|
||||
"outputVariable": "apiResponse",
|
||||
"errorVariable": "apiError",
|
||||
"continueOnError": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**HTTP Methods**: GET, POST, PUT, DELETE, PATCH
|
||||
**Variable References**: Use `{variableName}` syntax in URL, headers, and body
|
||||
|
||||
### 4. Script Node
|
||||
**Purpose**: Data processing and transformation
|
||||
```json
|
||||
{
|
||||
"id": "script-{timestamp}",
|
||||
"type": "script",
|
||||
"label": "Process API Response",
|
||||
"position": {"x": 800, "y": 135},
|
||||
"data": {
|
||||
"label": "Process API Response",
|
||||
"description": "Transform API response for document verification step",
|
||||
"scriptLanguage": "javascript",
|
||||
"scriptCode": "// Extract important data from API response\nconst apiData = processVariables.apiResponse;\n\nif (apiData && apiData.data) {\n processVariables.applicationId = apiData.data.id || 'APP-' + Date.now();\n}",
|
||||
"inputVariables": ["apiResponse", "text3", "radioPendidikan"],
|
||||
"outputVariables": [
|
||||
{
|
||||
"name": "applicationId",
|
||||
"type": "string",
|
||||
"description": "Generated application ID"
|
||||
}
|
||||
],
|
||||
"errorVariable": "scriptError",
|
||||
"continueOnError": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Script Languages**: javascript, python
|
||||
**Variable Access**: Use `processVariables.variableName` to access/set variables
|
||||
|
||||
### 5. Decision Node (Gateway)
|
||||
**Purpose**: Conditional branching
|
||||
```json
|
||||
{
|
||||
"id": "gateway-{timestamp}",
|
||||
"type": "gateway",
|
||||
"label": "Check Documents",
|
||||
"position": {"x": 700, "y": 200},
|
||||
"data": {
|
||||
"label": "Check Documents",
|
||||
"description": "Route based on document requirements",
|
||||
"gatewayType": "exclusive",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-1",
|
||||
"label": "Documents Required",
|
||||
"variable": "documentsRequired",
|
||||
"operator": "equals",
|
||||
"value": true,
|
||||
"targetNode": "form-verification"
|
||||
},
|
||||
{
|
||||
"id": "condition-2",
|
||||
"label": "No Documents",
|
||||
"variable": "documentsRequired",
|
||||
"operator": "equals",
|
||||
"value": false,
|
||||
"targetNode": "end-approved"
|
||||
}
|
||||
],
|
||||
"defaultPath": "end-rejected"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Gateway Types**: exclusive, inclusive, parallel
|
||||
**Operators**: equals, not_equals, greater_than, less_than, contains
|
||||
|
||||
### 6. Business Rule Node
|
||||
**Purpose**: Complex business logic evaluation
|
||||
```json
|
||||
{
|
||||
"id": "business-rule-{timestamp}",
|
||||
"type": "business-rule",
|
||||
"label": "Eligibility Rules",
|
||||
"position": {"x": 900, "y": 200},
|
||||
"data": {
|
||||
"label": "Eligibility Rules",
|
||||
"description": "Evaluate application eligibility",
|
||||
"ruleGroups": [
|
||||
{
|
||||
"id": "group-1",
|
||||
"name": "Age Requirements",
|
||||
"operator": "AND",
|
||||
"conditions": [
|
||||
{
|
||||
"variable": "applicantAge",
|
||||
"operator": "greater_than",
|
||||
"value": 18
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "set_variable",
|
||||
"variable": "ageEligible",
|
||||
"value": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputVariable": "ruleResult",
|
||||
"errorVariable": "ruleError"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Notification Node
|
||||
**Purpose**: Send notifications
|
||||
```json
|
||||
{
|
||||
"id": "notification-{timestamp}",
|
||||
"type": "notification",
|
||||
"label": "Send Approval Email",
|
||||
"position": {"x": 1000, "y": 200},
|
||||
"data": {
|
||||
"label": "Send Approval Email",
|
||||
"description": "Notify applicant of approval",
|
||||
"notificationType": "email|sms|push",
|
||||
"recipientType": "variable|role|user|email",
|
||||
"recipientValue": "applicantEmail",
|
||||
"subject": "Application Approved - {applicationId}",
|
||||
"message": "Dear {applicantName},\n\nYour application {applicationId} has been approved.",
|
||||
"template": "approval-template",
|
||||
"errorVariable": "notificationError"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Notification Types**: email, sms, push, internal
|
||||
**Recipient Types**: variable, role, user, email
|
||||
|
||||
### 8. End Node
|
||||
**Purpose**: Process termination
|
||||
```json
|
||||
{
|
||||
"id": "end-{timestamp}",
|
||||
"type": "end",
|
||||
"label": "End",
|
||||
"position": {"x": 1200, "y": 180},
|
||||
"data": {
|
||||
"label": "End",
|
||||
"description": "Process completion point"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Edges (Connections)
|
||||
|
||||
### Basic Edge
|
||||
```json
|
||||
{
|
||||
"id": "{sourceId}-{targetId}-{timestamp}",
|
||||
"source": "start-1751870920411",
|
||||
"target": "form-1751870928350",
|
||||
"type": "smoothstep",
|
||||
"animated": true,
|
||||
"label": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Edge (from Gateway)
|
||||
```json
|
||||
{
|
||||
"id": "gateway-condition-{timestamp}",
|
||||
"source": "gateway-1751870920411",
|
||||
"target": "form-1751870928350",
|
||||
"type": "smoothstep",
|
||||
"animated": true,
|
||||
"label": "Documents Required",
|
||||
"data": {
|
||||
"condition": "documentsRequired == true"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Process Variables
|
||||
|
||||
### Variable Definition Structure
|
||||
Process variables are stored as a JSON object where each variable has a specific structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"variableName": {
|
||||
"name": "variableName",
|
||||
"type": "string|number|boolean|object|array",
|
||||
"scope": "global|local",
|
||||
"value": null,
|
||||
"description": "Variable description"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Complete Process Variables Format
|
||||
Based on the actual database structure, here's the complete format for process variables:
|
||||
|
||||
```json
|
||||
{
|
||||
"text3": {
|
||||
"name": "text3",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "Ahmad Bin Ali",
|
||||
"description": "Form data from text_3 in Borang Maklumat Peribadi"
|
||||
},
|
||||
"select1": {
|
||||
"name": "select1",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "staff",
|
||||
"description": "Form data from select_1 in Borang Maklumat Peribadi"
|
||||
},
|
||||
"apiResponse": {
|
||||
"name": "apiResponse",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": {
|
||||
"data": {
|
||||
"id": "APP-2024-001234",
|
||||
"score": 85,
|
||||
"status": "pending_verification",
|
||||
"profileId": "PROF-567890",
|
||||
"riskLevel": "low",
|
||||
"documentCount": 3,
|
||||
"submissionDate": "2024-01-15T10:30:00Z"
|
||||
},
|
||||
"status": "success",
|
||||
"message": "Profile submitted successfully"
|
||||
},
|
||||
"description": "API response from Submit Profile Data"
|
||||
},
|
||||
"radioJantina": {
|
||||
"name": "radioJantina",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "Lelaki",
|
||||
"description": "Form data from radio_jantina in Borang Maklumat Peribadi"
|
||||
},
|
||||
"documentsRequired": {
|
||||
"name": "documentsRequired",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": true,
|
||||
"description": "Whether documents verification is required"
|
||||
},
|
||||
"applicationId": {
|
||||
"name": "applicationId",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "APP-1751871528249",
|
||||
"description": "Generated application ID from script processing"
|
||||
},
|
||||
"apiError": {
|
||||
"name": "apiError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "API error from Submit Profile Data"
|
||||
},
|
||||
"scriptError": {
|
||||
"name": "scriptError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Error information from script execution"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Variable Types and Examples
|
||||
|
||||
#### String Variables
|
||||
```json
|
||||
{
|
||||
"applicantName": {
|
||||
"name": "applicantName",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "Ahmad Bin Ali",
|
||||
"description": "Applicant's full name"
|
||||
},
|
||||
"verificationLevel": {
|
||||
"name": "verificationLevel",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "enhanced",
|
||||
"description": "Level of verification required (standard/enhanced)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Number Variables
|
||||
```json
|
||||
{
|
||||
"profileScore": {
|
||||
"name": "profileScore",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 85,
|
||||
"description": "Profile evaluation score from API"
|
||||
},
|
||||
"requestAmount": {
|
||||
"name": "requestAmount",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 1500.50,
|
||||
"description": "Requested loan amount"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Boolean Variables
|
||||
```json
|
||||
{
|
||||
"documentsRequired": {
|
||||
"name": "documentsRequired",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": true,
|
||||
"description": "Whether documents verification is required"
|
||||
},
|
||||
"nextStepReady": {
|
||||
"name": "nextStepReady",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": false,
|
||||
"description": "Whether ready for next step in process"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Object Variables
|
||||
```json
|
||||
{
|
||||
"apiResponse": {
|
||||
"name": "apiResponse",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": {
|
||||
"data": {
|
||||
"id": "APP-2024-001234",
|
||||
"score": 85,
|
||||
"status": "pending_verification"
|
||||
},
|
||||
"status": "success",
|
||||
"message": "Profile submitted successfully"
|
||||
},
|
||||
"description": "Complete API response object"
|
||||
},
|
||||
"userProfile": {
|
||||
"name": "userProfile",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": {
|
||||
"name": "Ahmad Bin Ali",
|
||||
"email": "ahmad@example.com",
|
||||
"phone": "+60123456789",
|
||||
"address": {
|
||||
"street": "Jalan Bangsar",
|
||||
"city": "Kuala Lumpur",
|
||||
"postcode": "59000"
|
||||
}
|
||||
},
|
||||
"description": "User profile information"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Array Variables
|
||||
```json
|
||||
{
|
||||
"assignedRoles": {
|
||||
"name": "assignedRoles",
|
||||
"type": "array",
|
||||
"scope": "global",
|
||||
"value": ["manager", "supervisor", "admin"],
|
||||
"description": "List of roles assigned to task"
|
||||
},
|
||||
"documentTypes": {
|
||||
"name": "documentTypes",
|
||||
"type": "array",
|
||||
"scope": "global",
|
||||
"value": [
|
||||
{
|
||||
"type": "IC",
|
||||
"required": true,
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"type": "Passport",
|
||||
"required": false,
|
||||
"status": "not_required"
|
||||
}
|
||||
],
|
||||
"description": "Array of document requirement objects"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Date Variables
|
||||
```json
|
||||
{
|
||||
"dateMasukislam": {
|
||||
"name": "dateMasukislam",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "2020-05-15",
|
||||
"description": "Islam conversion date in YYYY-MM-DD format"
|
||||
},
|
||||
"verificationDate": {
|
||||
"name": "verificationDate",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "2024-01-15T10:30:00Z",
|
||||
"description": "ISO 8601 formatted verification timestamp"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Variable Scopes
|
||||
- **global**: Available throughout the entire process
|
||||
- **local**: Available only within the current node/task
|
||||
|
||||
### Variable Naming Conventions
|
||||
- Use **camelCase** for consistency (e.g., `applicantName`, `verificationLevel`)
|
||||
- Form field mappings often use **snake_case** to match form field names
|
||||
- Keep names descriptive and meaningful
|
||||
- Avoid spaces and special characters
|
||||
- Use consistent prefixes for related variables
|
||||
|
||||
### Variable Usage Patterns
|
||||
|
||||
#### In API Calls
|
||||
```json
|
||||
{
|
||||
"apiUrl": "https://api.example.com/users/{userId}/profile",
|
||||
"headers": "{ \"Authorization\": \"Bearer {apiToken}\", \"Content-Type\": \"application/json\" }",
|
||||
"requestBody": "{\n \"name\": \"{applicantName}\",\n \"score\": {profileScore},\n \"verified\": {documentsRequired}\n}"
|
||||
}
|
||||
```
|
||||
|
||||
#### In Script Nodes
|
||||
```javascript
|
||||
// Accessing variables
|
||||
const applicantName = processVariables.applicantName;
|
||||
const score = processVariables.profileScore;
|
||||
const apiData = processVariables.apiResponse;
|
||||
|
||||
// Setting variables
|
||||
processVariables.applicationId = 'APP-' + Date.now();
|
||||
processVariables.verificationLevel = score > 80 ? 'enhanced' : 'standard';
|
||||
processVariables.nextStepReady = true;
|
||||
|
||||
// Working with objects
|
||||
if (processVariables.apiResponse && processVariables.apiResponse.data) {
|
||||
processVariables.externalId = processVariables.apiResponse.data.id;
|
||||
}
|
||||
```
|
||||
|
||||
#### In Gateway Conditions
|
||||
```json
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"variable": "profileScore",
|
||||
"operator": "greater_than",
|
||||
"value": 75
|
||||
},
|
||||
{
|
||||
"variable": "documentsRequired",
|
||||
"operator": "equals",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"variable": "verificationLevel",
|
||||
"operator": "equals",
|
||||
"value": "enhanced"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### In Notifications
|
||||
```json
|
||||
{
|
||||
"subject": "Application {applicationId} - Status Update",
|
||||
"message": "Dear {applicantName},\n\nYour application {applicationId} has been processed.\nScore: {profileScore}\nStatus: {verificationLevel}\n\nThank you."
|
||||
}
|
||||
```
|
||||
|
||||
#### In Form Mappings
|
||||
```json
|
||||
{
|
||||
"inputMappings": [
|
||||
{
|
||||
"formField": "reference_number",
|
||||
"processVariable": "applicationId"
|
||||
},
|
||||
{
|
||||
"formField": "applicant_name",
|
||||
"processVariable": "applicantName"
|
||||
}
|
||||
],
|
||||
"outputMappings": [
|
||||
{
|
||||
"formField": "verification_result",
|
||||
"processVariable": "verificationResult"
|
||||
},
|
||||
{
|
||||
"formField": "verification_notes",
|
||||
"processVariable": "verificationNotes"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Error Variables
|
||||
Always include error variables for nodes that can fail:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiError": {
|
||||
"name": "apiError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "API error from Submit Profile Data"
|
||||
},
|
||||
"scriptError": {
|
||||
"name": "scriptError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Error information from script execution"
|
||||
},
|
||||
"verificationApiError": {
|
||||
"name": "verificationApiError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "API error from verification submission"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Variable Best Practices
|
||||
1. **Initialize with null values** for variables that will be set during process execution
|
||||
2. **Provide clear descriptions** explaining the variable's purpose and source
|
||||
3. **Use consistent typing** - don't mix string and number types for the same concept
|
||||
4. **Group related variables** with similar naming patterns
|
||||
5. **Plan for error handling** by including error variables
|
||||
6. **Document data formats** especially for dates and complex objects
|
||||
|
||||
## Process Settings
|
||||
|
||||
### Basic Settings
|
||||
```json
|
||||
{
|
||||
"owner": "",
|
||||
"category": "HR",
|
||||
"priority": "normal|high|low",
|
||||
"processType": "standard|template",
|
||||
"allowParallel": false,
|
||||
"requireApproval": false,
|
||||
"enableAuditTrail": true,
|
||||
"sendNotifications": true,
|
||||
"executionPermission": "public|authenticated|role",
|
||||
"modificationPermission": "owner|managers|admin"
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Settings
|
||||
```json
|
||||
{
|
||||
"autoTimeout": 24,
|
||||
"maxExecutionTime": 60,
|
||||
"dataPersistence": "short_term|long_term|permanent",
|
||||
"dataRetentionPolicy": "",
|
||||
"enableErrorRecovery": true,
|
||||
"encryptSensitiveData": false,
|
||||
"logVariableChanges": true,
|
||||
"allowedRoles": ""
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example Process
|
||||
|
||||
### Simple Approval Process
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start-001",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"position": {"x": 100, "y": 100},
|
||||
"data": {"label": "Start", "description": "Begin approval process"}
|
||||
},
|
||||
{
|
||||
"id": "form-application",
|
||||
"type": "form",
|
||||
"label": "Application Form",
|
||||
"position": {"x": 300, "y": 100},
|
||||
"data": {
|
||||
"formId": 1,
|
||||
"assignmentType": "public",
|
||||
"outputMappings": [
|
||||
{"formField": "applicant_name", "processVariable": "applicantName"},
|
||||
{"formField": "amount", "processVariable": "requestAmount"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gateway-amount",
|
||||
"type": "gateway",
|
||||
"label": "Check Amount",
|
||||
"position": {"x": 500, "y": 100},
|
||||
"data": {
|
||||
"gatewayType": "exclusive",
|
||||
"conditions": [
|
||||
{
|
||||
"variable": "requestAmount",
|
||||
"operator": "greater_than",
|
||||
"value": 1000,
|
||||
"targetNode": "form-manager-approval"
|
||||
}
|
||||
],
|
||||
"defaultPath": "notification-auto-approve"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "form-manager-approval",
|
||||
"type": "form",
|
||||
"label": "Manager Approval",
|
||||
"position": {"x": 500, "y": 250},
|
||||
"data": {
|
||||
"formId": 2,
|
||||
"assignmentType": "role",
|
||||
"assignedRoles": ["manager"],
|
||||
"inputMappings": [
|
||||
{"formField": "applicant", "processVariable": "applicantName"},
|
||||
{"formField": "amount", "processVariable": "requestAmount"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "notification-auto-approve",
|
||||
"type": "notification",
|
||||
"label": "Auto Approval",
|
||||
"position": {"x": 700, "y": 100},
|
||||
"data": {
|
||||
"notificationType": "email",
|
||||
"recipientType": "variable",
|
||||
"recipientValue": "applicantEmail",
|
||||
"subject": "Application Approved",
|
||||
"message": "Your request for {requestAmount} has been automatically approved."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "end-001",
|
||||
"type": "end",
|
||||
"label": "End",
|
||||
"position": {"x": 900, "y": 100},
|
||||
"data": {"label": "End", "description": "Process completed"}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "e1", "source": "start-001", "target": "form-application", "type": "smoothstep"},
|
||||
{"id": "e2", "source": "form-application", "target": "gateway-amount", "type": "smoothstep"},
|
||||
{"id": "e3", "source": "gateway-amount", "target": "form-manager-approval", "type": "smoothstep", "label": "> 1000"},
|
||||
{"id": "e4", "source": "gateway-amount", "target": "notification-auto-approve", "type": "smoothstep", "label": "≤ 1000"},
|
||||
{"id": "e5", "source": "form-manager-approval", "target": "end-001", "type": "smoothstep"},
|
||||
{"id": "e6", "source": "notification-auto-approve", "target": "end-001", "type": "smoothstep"}
|
||||
],
|
||||
"viewport": {"x": 0, "y": 0, "zoom": 1}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Node Naming
|
||||
- Use descriptive labels that explain the purpose
|
||||
- Include timestamps in IDs for uniqueness
|
||||
- Keep descriptions clear and concise
|
||||
|
||||
### Variable Management
|
||||
- Use consistent naming conventions (camelCase)
|
||||
- Document all variables with descriptions
|
||||
- Group related variables logically
|
||||
|
||||
### Flow Design
|
||||
- Start simple and add complexity gradually
|
||||
- Use gateways for clear decision points
|
||||
- Include error handling paths
|
||||
- Test each node configuration
|
||||
|
||||
### Error Handling
|
||||
- Always define error variables for API and script nodes
|
||||
- Use `continueOnError` appropriately
|
||||
- Include notification nodes for error scenarios
|
||||
- Plan alternative paths for failures
|
||||
|
||||
### Performance Considerations
|
||||
- Minimize API calls in loops
|
||||
- Use appropriate timeout values
|
||||
- Consider parallel execution where possible
|
||||
- Keep script code efficient
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Data Collection → Processing → Approval
|
||||
1. Form node for data collection
|
||||
2. Script node for data validation/transformation
|
||||
3. Gateway for routing based on criteria
|
||||
4. Form node for approval tasks
|
||||
5. Notification for status updates
|
||||
|
||||
### External Integration
|
||||
1. Form node for user input
|
||||
2. Script node for data preparation
|
||||
3. API node for external system call
|
||||
4. Script node for response processing
|
||||
5. Gateway for success/failure routing
|
||||
|
||||
### Multi-level Approval
|
||||
1. Form node for request submission
|
||||
2. Gateway for routing based on amount/type
|
||||
3. Multiple form nodes for different approval levels
|
||||
4. Parallel gateways for concurrent approvals
|
||||
5. Join gateway to merge approval paths
|
@ -1,347 +0,0 @@
|
||||
# Process Builder Improvements
|
||||
|
||||
## Overview
|
||||
This document outlines the planned improvements for the Process Builder core components. The improvements are designed to be manageable and maintainable while adding essential functionality.
|
||||
|
||||
## Recently Completed Features ✅
|
||||
|
||||
### December 2024 - Process Settings Management
|
||||
**Status: Completed** ✅
|
||||
|
||||
Implemented comprehensive process configuration management with a professional-grade settings interface:
|
||||
|
||||
- **Process Info Tab**: Complete process metadata management including name, description, priority levels, categorization, and ownership assignment
|
||||
- **Execution Settings Tab**: Advanced process execution controls including process types (standard, approval, data collection, automation, review), timeout management, parallel execution, error recovery, and notification settings
|
||||
- **Variables & Data Tab**: Data persistence policies with multiple retention options (session, temporary, short-term, long-term, permanent), variable change logging, sensitive data encryption, and compliance-ready retention policies
|
||||
- **Permissions Tab**: Enterprise-grade access control with execution permissions, role-based access, modification permissions, approval workflows, and comprehensive audit trails
|
||||
- **JSON Export Tab**: Complete configuration export functionality with metadata, copy-to-clipboard, and download capabilities for API integration and backup purposes
|
||||
|
||||
**Technical Implementation:**
|
||||
- Fully integrated with existing process builder store
|
||||
- Reactive state management with local change tracking
|
||||
- Professional UI with tabbed interface and validation
|
||||
- Comprehensive TypeScript interfaces for settings structure
|
||||
- Future-ready API integration patterns documented
|
||||
|
||||
### December 2024 - Database Integration & API System
|
||||
**Status: Completed** ✅
|
||||
|
||||
Complete database integration replacing local state with persistent storage:
|
||||
|
||||
- **REST API Endpoints**: Full CRUD operations for processes with pagination, search, and filtering
|
||||
- **Advanced Operations**: Process duplication, publishing, and template management
|
||||
- **URL Parameter System**: Direct process linking via `/process-builder?id={uuid}` pattern
|
||||
- **Navigation Integration**: Seamless routing between management and builder interfaces
|
||||
- **Error Handling**: Comprehensive validation and graceful error recovery
|
||||
- **Backward Compatibility**: Automatic upgrade of legacy process definitions
|
||||
- **Toast Notifications**: User feedback system for all operations
|
||||
- **Data Validation**: Robust Zod schemas ensuring data integrity
|
||||
|
||||
**Technical Implementation:**
|
||||
- Complete API system in `/server/api/process/` with UUID and numeric ID support
|
||||
- Enhanced process store with database integration and smart caching
|
||||
- URL parameter handling with route watching and error recovery
|
||||
- Success/error messaging with toast notification fallbacks
|
||||
- Migration-ready database schema with comprehensive process metadata
|
||||
|
||||
### December 2024 - Vue Flow Integration Fixes
|
||||
**Status: Completed** ✅
|
||||
|
||||
Critical performance and functionality fixes for Vue Flow integration:
|
||||
|
||||
- **Connection Dragging Fix**: Resolved interference preventing connector dragging between nodes
|
||||
- **Performance Optimizations**: Reduced re-renders and improved memory management
|
||||
- **State Synchronization**: Enhanced bidirectional data flow between store and Vue Flow
|
||||
- **Drag State Management**: Proper handling of node dragging without store interference
|
||||
- **Memory Management**: Cleanup of watchers and event listeners to prevent leaks
|
||||
- **Debounced Updates**: Smooth position syncing without blocking user interactions
|
||||
|
||||
**Technical Implementation:**
|
||||
- Optimized node sync handling with drag state awareness
|
||||
- Enhanced edge change detection with duplicate prevention
|
||||
- Improved computed properties for reactive data binding
|
||||
- Proper lifecycle management with cleanup on component unmount
|
||||
- Debounced position synchronization for smooth user experience
|
||||
|
||||
### December 2024 - Enhanced Form Node Configuration
|
||||
**Status: Completed** ✅
|
||||
|
||||
Completely redesigned form task configuration with step-by-step workflow:
|
||||
|
||||
- **3-Step Configuration Process**: Form selection, data mapping, and field conditions
|
||||
- **Bidirectional Data Mapping**: Input and output variable mapping between process and forms
|
||||
- **Dynamic Field Conditions**: Complex conditional logic for field behavior (readonly, hidden, required, optional)
|
||||
- **Professional UI Design**: Consistent with enterprise BPM standards
|
||||
- **Auto-Save Mechanisms**: Reliable data persistence throughout configuration
|
||||
|
||||
### December 2024 - 4-Point Connection System
|
||||
**Status: Completed** ✅
|
||||
|
||||
Enhanced node connection system to prevent edge overlaps and improve visual clarity:
|
||||
|
||||
- **Universal 4-Point System**: All nodes support top, bottom, left, right connection points
|
||||
- **Intelligent Handle Display**: Handles appear on hover with smooth transitions
|
||||
- **Color-Coded Connections**: Green for outputs, blue for inputs
|
||||
- **Smart Edge Routing**: Automatic routing to prevent visual overlaps
|
||||
- **Enhanced User Experience**: Improved connection feedback and visual indicators
|
||||
|
||||
### December 2024 - Variable System Enhancements
|
||||
**Status: Completed** ✅
|
||||
|
||||
Improved variable management with simplified scope handling:
|
||||
|
||||
- **Unified Variable Management**: Streamlined global variable system
|
||||
- **Enhanced Variable Manager UI**: Professional interface with search, filtering, and type indicators
|
||||
- **Template Integration**: Variables properly loaded from process templates
|
||||
- **Reactive Updates**: Real-time synchronization between variable manager and node configurations
|
||||
|
||||
## Variable System
|
||||
|
||||
### 1. Global Variables
|
||||
```javascript
|
||||
{
|
||||
name: 'string',
|
||||
type: 'string|number|boolean|object|array',
|
||||
defaultValue: any,
|
||||
description: 'string',
|
||||
scope: 'global',
|
||||
isRequired: boolean,
|
||||
isReadOnly: boolean
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Process Variables
|
||||
```javascript
|
||||
{
|
||||
name: 'string',
|
||||
type: 'string|number|boolean|object|array',
|
||||
defaultValue: any,
|
||||
description: 'string',
|
||||
scope: 'process',
|
||||
isRequired: boolean,
|
||||
isReadOnly: boolean,
|
||||
direction: 'in|out|inout' // for process arguments
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Task/Form Arguments
|
||||
```javascript
|
||||
{
|
||||
name: 'string',
|
||||
type: 'string|number|boolean|object|array',
|
||||
defaultValue: any,
|
||||
description: 'string',
|
||||
direction: 'in|out|inout',
|
||||
isRequired: boolean,
|
||||
validation: {
|
||||
rules: [],
|
||||
customValidation: 'string' // custom validation script
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Core Components Improvements
|
||||
|
||||
### 1. Start Event
|
||||
```javascript
|
||||
{
|
||||
type: 'start',
|
||||
data: {
|
||||
description: 'Process start point',
|
||||
triggerType: 'manual', // manual, scheduled
|
||||
schedule: null, // for scheduled triggers
|
||||
variables: {
|
||||
input: [], // process input arguments
|
||||
output: [] // process output arguments
|
||||
},
|
||||
globalVariables: [] // global variables used in this process
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. End Event
|
||||
```javascript
|
||||
{
|
||||
type: 'end',
|
||||
data: {
|
||||
description: 'Process end point',
|
||||
resultType: 'success', // success, error
|
||||
variables: {
|
||||
input: [], // variables required for end event
|
||||
output: [] // variables to be returned
|
||||
},
|
||||
returnValues: [] // values to return to calling process
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Task
|
||||
```javascript
|
||||
{
|
||||
type: 'task',
|
||||
data: {
|
||||
description: 'A general task',
|
||||
assignee: '',
|
||||
taskType: 'manual', // manual, automated
|
||||
priority: 'medium', // low, medium, high
|
||||
dueDate: null,
|
||||
variables: {
|
||||
input: [], // task input arguments
|
||||
output: [] // task output arguments
|
||||
},
|
||||
notifications: {
|
||||
onAssign: true,
|
||||
onComplete: true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Form Task
|
||||
```javascript
|
||||
{
|
||||
type: 'form',
|
||||
data: {
|
||||
description: 'Form submission task',
|
||||
formId: null,
|
||||
formName: null,
|
||||
formSettings: {
|
||||
allowDraft: true,
|
||||
autoSave: true
|
||||
},
|
||||
variables: {
|
||||
input: [], // form input arguments
|
||||
output: [] // form output arguments
|
||||
},
|
||||
dataMapping: {
|
||||
input: [], // map process variables to form
|
||||
output: [] // map form to process variables
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Gateway
|
||||
```javascript
|
||||
{
|
||||
type: 'gateway',
|
||||
data: {
|
||||
description: 'Decision gateway',
|
||||
conditions: [],
|
||||
defaultPath: 'Default',
|
||||
gatewayType: 'exclusive', // exclusive, parallel
|
||||
variables: {
|
||||
input: [], // variables needed for conditions
|
||||
output: [] // variables to pass to next node
|
||||
},
|
||||
timeout: {
|
||||
enabled: false,
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## New Core Components
|
||||
|
||||
### 1. Script Task
|
||||
```javascript
|
||||
{
|
||||
type: 'script',
|
||||
data: {
|
||||
description: 'Execute custom script',
|
||||
scriptType: 'javascript',
|
||||
script: '',
|
||||
variables: {
|
||||
input: [], // script input arguments
|
||||
output: [] // script output arguments
|
||||
},
|
||||
timeout: 30 // seconds
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
### Phase 1 - Essential Improvements
|
||||
1. Implement basic variable system
|
||||
- Global variables
|
||||
- Process variables
|
||||
- Task/Form arguments
|
||||
2. Add basic trigger types to Start Event
|
||||
3. Add result types to End Event
|
||||
4. Add task priorities and due dates
|
||||
5. Add form settings for drafts and auto-save
|
||||
|
||||
### Phase 2 - Enhanced Features
|
||||
1. Add variable validation system
|
||||
2. Add data mapping for forms
|
||||
3. Add script task component
|
||||
4. Add timeout handling
|
||||
5. Add notifications system
|
||||
|
||||
### Phase 3 - Advanced Features
|
||||
1. Add subprocess component
|
||||
2. Add advanced gateway conditions
|
||||
3. Add process templates
|
||||
4. Add process versioning
|
||||
5. Add process analytics
|
||||
|
||||
## Variable System Features
|
||||
|
||||
### 1. Variable Types
|
||||
- String
|
||||
- Number
|
||||
- Boolean
|
||||
- Object
|
||||
- Array
|
||||
- Date
|
||||
- File
|
||||
- Custom types
|
||||
|
||||
### 2. Variable Scopes
|
||||
- Global (accessible across all processes)
|
||||
- Process (accessible within a process)
|
||||
- Task/Form (accessible within a task/form)
|
||||
- Local (accessible within a script)
|
||||
|
||||
### 3. Variable Operations
|
||||
- Create/Delete
|
||||
- Read/Write
|
||||
- Copy/Move
|
||||
- Transform
|
||||
- Validate
|
||||
- Persist
|
||||
|
||||
### 4. Variable Passing
|
||||
- Process to Process
|
||||
- Task to Task
|
||||
- Form to Process
|
||||
- Script to Process
|
||||
- Gateway Conditions
|
||||
|
||||
## Notes
|
||||
- Keep improvements focused on essential functionality
|
||||
- Maintain backward compatibility
|
||||
- Ensure easy maintenance
|
||||
- Document all new features
|
||||
- Add proper validation
|
||||
- Include error handling
|
||||
- Implement proper variable scoping
|
||||
- Add variable type checking
|
||||
- Include variable persistence
|
||||
|
||||
## Future Considerations
|
||||
- Process templates
|
||||
- Process versioning
|
||||
- Process analytics
|
||||
- Advanced notifications
|
||||
- Custom validations
|
||||
- Process documentation
|
||||
- Process testing
|
||||
- Process deployment
|
||||
- Variable encryption
|
||||
- Variable versioning
|
||||
- Variable dependencies
|
||||
|
||||
Last updated: June 10, 2024
|
File diff suppressed because it is too large
Load Diff
@ -1,782 +0,0 @@
|
||||
# Process Builder Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The Process Builder is a visual workflow designer that allows you to create, edit, and manage business processes using the BPMN (Business Process Model and Notation) standard. With an intuitive drag-and-drop interface, you can design complex workflows that model your organization's business processes.
|
||||
|
||||
## Recent Updates (December 2024)
|
||||
|
||||
### New Features & Improvements
|
||||
|
||||
#### **Direct Process Linking**
|
||||
- **URL Parameters**: Access specific processes directly via `/process-builder?id={process-id}`
|
||||
- **Bookmarkable Links**: Share and bookmark direct links to processes
|
||||
- **Navigation Integration**: Seamless navigation from process management to builder
|
||||
- **Error Handling**: Graceful fallback for invalid or missing process IDs
|
||||
|
||||
#### **Enhanced Save & Navigation**
|
||||
- **Success Notifications**: Toast messages confirm successful saves and operations
|
||||
- **Smart Navigation**: Automatic URL updates when creating or editing processes
|
||||
- **Unsaved Changes Detection**: Improved modal that correctly detects when changes are saved
|
||||
- **Loading States**: Visual feedback during save, load, and navigation operations
|
||||
|
||||
#### **Database Integration**
|
||||
- **Persistent Storage**: All processes now saved to database with comprehensive API
|
||||
- **Real-time Sync**: Changes automatically persisted with proper error handling
|
||||
- **Backup Compatibility**: Legacy process definitions automatically upgraded
|
||||
- **Data Validation**: Robust validation ensures data integrity
|
||||
|
||||
#### **Connection System Fixes**
|
||||
- **Reliable Dragging**: Fixed issue with connector dragging between nodes
|
||||
- **Performance Optimized**: Reduced interference with Vue Flow's internal operations
|
||||
- **Smooth Interactions**: Improved responsiveness during node manipulation
|
||||
- **Connection Feedback**: Better visual feedback during connection creation
|
||||
|
||||
#### **Process Management Consistency**
|
||||
- **Unified Design**: Process and form management pages now share consistent UI
|
||||
- **Search & Filter**: Enhanced search functionality across all management interfaces
|
||||
- **Action Buttons**: Standardized edit, duplicate, and delete operations
|
||||
- **Loading States**: Consistent loading indicators and error handling
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Accessing the Process Builder
|
||||
1. Navigate to `/process-builder` in your browser
|
||||
2. You'll see a three-panel interface:
|
||||
- Left: Component Palette
|
||||
- Middle: Process Canvas
|
||||
- Right: Properties Panel
|
||||
|
||||
### Quick Start Guide
|
||||
1. **Create a New Process**
|
||||
- Click "New Process" in the header
|
||||
- Enter a process name and description
|
||||
- Start adding components
|
||||
|
||||
2. **Add Process Elements**
|
||||
- Drag components from the left panel onto the canvas
|
||||
- Components include Start/End events, Tasks, Gateways, etc.
|
||||
- Connect elements by dragging from one node's handle to another
|
||||
- Handles appear when hovering over nodes:
|
||||
- Top handle: Input connection point
|
||||
- Bottom handle: Output connection point
|
||||
|
||||
3. **Configure Elements**
|
||||
- Click any element in the canvas to select it
|
||||
- Use the right panel to configure its properties
|
||||
- Changes are previewed in real-time
|
||||
|
||||
4. **Save and Deploy**
|
||||
- Click "Save Process" to store your changes
|
||||
- Once ready, you can deploy the process for execution
|
||||
|
||||
## Process Components
|
||||
|
||||
### Events
|
||||
Events represent something that happens during the course of a process:
|
||||
|
||||
- **Start Event** (Green Icon)
|
||||
- Indicates where a process begins
|
||||
- Only has output handle (bottom)
|
||||
- Properties: Description, triggers
|
||||
|
||||
- **End Event** (Red Icon)
|
||||
- Indicates where a process path ends
|
||||
- Only has input handle (top)
|
||||
- Properties: Description, result types
|
||||
|
||||
### Activities
|
||||
Activities represent work performed in a process:
|
||||
|
||||
- **Task** (Blue Icon)
|
||||
- A simple atomic activity within the process
|
||||
- Has both input and output handles
|
||||
- Properties: Assignee, description
|
||||
|
||||
- **Form Task** (Emerald Icon)
|
||||
- A task that requires form data
|
||||
- Has 4 connection points: top, bottom, left, right handles
|
||||
- **Enhanced Configuration**: Complete 3-step configuration workflow
|
||||
- **Step 1 - Form Selection**: Choose existing forms or create new ones
|
||||
- **Step 2 - Data Mapping**: Configure bidirectional data flow
|
||||
- **Input Mappings**: Map process variables to pre-fill form fields
|
||||
- **Output Mappings**: Capture form submission data in process variables
|
||||
- **Step 3 - Field Conditions**: Dynamic field behavior based on process state
|
||||
- Make fields readonly, hidden, required, or optional
|
||||
- Support for complex conditional logic with multiple operators
|
||||
- Real-time form adaptations based on process variables
|
||||
- Connects to forms created in the Form Builder
|
||||
- Visualizes bidirectional data flow with color-coded mapping sections
|
||||
|
||||
- **API Task** (Indigo Icon)
|
||||
- Automated task that makes API calls
|
||||
- Has both input and output handles
|
||||
- Step-by-step configuration workflow
|
||||
- Properties: Endpoint, method, headers, request/response mapping
|
||||
|
||||
- **Business Rule Task** (Purple Icon)
|
||||
- Executes custom business rules and logic
|
||||
- Has both input and output handles
|
||||
- Step-by-step configuration workflow
|
||||
- Properties: Rule conditions, actions, and variables
|
||||
|
||||
- **Script Task** (Grey Icon)
|
||||
- Automated task that executes code
|
||||
- Has both input and output handles
|
||||
- Properties: Language, description, script content
|
||||
|
||||
### Gateways
|
||||
Decision points control flow divergence and convergence:
|
||||
|
||||
- **Decision Point** (Orange Diamond Icon)
|
||||
- Creates alternative paths based on conditions
|
||||
- Has both input and output handles
|
||||
- Shows the number of configured paths in a badge
|
||||
- Displays path names within the diamond
|
||||
- Enhanced configuration with visual flow indicators
|
||||
- Properties: Conditions, default path, description
|
||||
- Connection labels automatically match condition paths
|
||||
|
||||
## Node Configuration UI
|
||||
|
||||
All node configuration components follow a consistent design pattern for improved usability:
|
||||
|
||||
### Common UI Elements
|
||||
- **Modal Headers**: Clear titles with descriptive text and appropriate icons
|
||||
- **Step Indicators**: Numbered workflow steps for complex configurations
|
||||
- **Quick Reference**: Help text and examples for common use cases
|
||||
- **Color-Coding**: Consistent color themes for each node type
|
||||
- Purple for Business Rules
|
||||
- Indigo for API Tasks
|
||||
- Emerald for Form Tasks
|
||||
- Blue for Variables
|
||||
|
||||
### Form Task Configuration
|
||||
1. **Form Selection**: Choose from available forms or create a new one
|
||||
2. **Data Mapping**: Bidirectional mapping between process variables and form fields
|
||||
3. **Options**: Configure submission behavior and user experience
|
||||
|
||||
### Business Rule Configuration
|
||||
1. **Rule Definition**: Create conditions based on process variables
|
||||
2. **Actions**: Define what happens when conditions are met
|
||||
3. **Testing**: Validate rules against sample data
|
||||
|
||||
### API Task Configuration
|
||||
1. **Endpoint Definition**: Configure API URL, method, and authentication
|
||||
2. **Request Mapping**: Map process variables to API request parameters
|
||||
3. **Response Mapping**: Extract data from API responses into process variables
|
||||
|
||||
### Decision Point Configuration
|
||||
1. **Path Creation**: Add multiple decision paths
|
||||
2. **Condition Building**: Create boolean conditions for each path
|
||||
3. **Default Path**: Configure fallback path when no conditions are met
|
||||
|
||||
## Working with Variables
|
||||
|
||||
The Variable Manager provides a central location to define and manage global process variables:
|
||||
|
||||
- **Variable Types**: String, Int, Decimal, Object, DateTime, Date, Boolean
|
||||
- **Search**: Quickly find variables with the search functionality
|
||||
- **Organization**: Variables displayed in cards with type indicators
|
||||
- **Editing**: Easily edit or delete variables as needed
|
||||
- **Empty State**: Clear guidance when no variables exist
|
||||
|
||||
## Working with the Process Canvas
|
||||
|
||||
### Navigation
|
||||
- **Pan**: Click and drag the canvas background or use middle mouse button
|
||||
- **Zoom**: Use mouse wheel or zoom controls in top-right
|
||||
- **Reset View**: Use the fit-view button in controls
|
||||
|
||||
### Element Manipulation
|
||||
- **Select**: Click on an element
|
||||
- **Multi-select**: Hold Shift while clicking elements
|
||||
- **Move**: Drag selected elements
|
||||
- **Delete**: Press Delete key, double-click element, or use the Delete button in the properties panel
|
||||
- **Connect**: Drag from one node's handle to another's
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- **Delete**: Remove selected elements
|
||||
- **Shift**: Enable node selection
|
||||
- **Control**: Multi-select nodes
|
||||
|
||||
### Connection Points
|
||||
All nodes now feature **4 connection points** for maximum flexibility:
|
||||
- **Top Handle**: Primary input connection point
|
||||
- **Right Handle**: Secondary output connection point
|
||||
- **Bottom Handle**: Primary output connection point
|
||||
- **Left Handle**: Secondary input connection point
|
||||
|
||||
**Special Cases**:
|
||||
- **Start Nodes**: Only have output handles (right + bottom)
|
||||
- **End Nodes**: Only have input handles (top + left)
|
||||
|
||||
**Enhanced Visibility**:
|
||||
- Handles are invisible by default for clean appearance
|
||||
- Handles become visible when hovering over nodes
|
||||
- Handles are highlighted during connection mode
|
||||
- Color-coded: Green for outputs, Blue for inputs
|
||||
- Smooth transitions and hover effects for better user experience
|
||||
|
||||
**Creating Connections**:
|
||||
1. Hover over a node to reveal handles
|
||||
2. Click and drag from any handle
|
||||
3. Drop on another node's compatible handle
|
||||
4. Connection automatically routes to prevent overlaps
|
||||
5. Use specific handles to control connection positioning
|
||||
|
||||
## Process Properties
|
||||
|
||||
### Basic Settings
|
||||
- Process name and description
|
||||
- Version information
|
||||
- Start conditions
|
||||
- Process timeouts
|
||||
|
||||
### Variables
|
||||
- Process data variables
|
||||
- Input and output parameters
|
||||
- Data types and validation
|
||||
|
||||
### Participants
|
||||
- User assignments
|
||||
- Role-based assignments
|
||||
- Group assignments
|
||||
|
||||
## Process Settings
|
||||
|
||||
The Process Settings feature provides comprehensive configuration options for managing all aspects of your business process. Access Process Settings through the dropdown menu in the header by clicking the three-dot menu and selecting "Process Settings".
|
||||
|
||||
### Accessing Process Settings
|
||||
|
||||
1. **From the Header Menu**
|
||||
- Click the three-dot menu (⋮) in the top-right of the Process Builder
|
||||
- Select "Process Settings" from the dropdown menu
|
||||
- The settings modal opens with tabbed configuration options
|
||||
|
||||
2. **When to Use Process Settings**
|
||||
- Configure process metadata and ownership
|
||||
- Set execution parameters and timeouts
|
||||
- Define data persistence and security policies
|
||||
- Manage permissions and access control
|
||||
- Export process configuration for integration
|
||||
|
||||
### Settings Tabs Overview
|
||||
|
||||
The Process Settings modal is organized into five comprehensive tabs:
|
||||
|
||||
#### 1. Process Info Tab 📋
|
||||
|
||||
Configure basic process information and ownership:
|
||||
|
||||
**Process Details**:
|
||||
- **Process Name**: The display name for your process
|
||||
- **Process Description**: Detailed description of what the process accomplishes
|
||||
- **Priority Level**: Set process priority (Low, Normal, High, Critical)
|
||||
- **Process Category**: Department or functional area (e.g., HR, Finance, Operations)
|
||||
- **Owner/Manager**: Person responsible for the process
|
||||
|
||||
**Best Practices**:
|
||||
- Use descriptive names that clearly indicate the process purpose
|
||||
- Include business context in descriptions
|
||||
- Set appropriate priority levels to help with resource allocation
|
||||
- Assign clear ownership for accountability
|
||||
|
||||
#### 2. Execution Settings Tab ⚙️
|
||||
|
||||
Control how your process executes and handles different scenarios:
|
||||
|
||||
**Process Type Configuration**:
|
||||
- **Standard Process**: Regular business workflow
|
||||
- **Approval Workflow**: Multi-step approval process
|
||||
- **Data Collection**: Information gathering process
|
||||
- **Automated Task**: System-driven automation
|
||||
- **Review Process**: Quality assurance or compliance review
|
||||
|
||||
**Timing and Performance**:
|
||||
- **Max Execution Time**: Maximum time allowed for process completion (1-10,080 minutes)
|
||||
- **Auto-Timeout**: Automatically timeout after specified hours (1-168 hours)
|
||||
- **Allow Parallel Execution**: Enable multiple instances to run simultaneously
|
||||
- **Enable Error Recovery**: Automatically retry failed steps before stopping
|
||||
|
||||
**Notifications**:
|
||||
- **Send Completion Notifications**: Notify stakeholders when process completes
|
||||
|
||||
**Example Configuration**:
|
||||
```
|
||||
Process Type: Approval Workflow
|
||||
Max Execution Time: 480 minutes (8 hours)
|
||||
Auto-Timeout: 72 hours (3 days)
|
||||
Parallel Execution: Enabled
|
||||
Error Recovery: Enabled
|
||||
Notifications: Enabled
|
||||
```
|
||||
|
||||
#### 3. Variables & Data Tab 🗄️
|
||||
|
||||
Configure data handling and variable persistence:
|
||||
|
||||
**Data Persistence Options**:
|
||||
- **Session Only**: Data deleted when session ends
|
||||
- **Temporary (24 hours)**: Data retained for 24 hours
|
||||
- **Short Term (7 days)**: Data retained for one week
|
||||
- **Long Term (30 days)**: Data retained for one month
|
||||
- **Permanent**: Data retained indefinitely
|
||||
|
||||
**Security and Compliance**:
|
||||
- **Log Variable Changes**: Track all variable modifications during execution
|
||||
- **Encrypt Sensitive Data**: Automatically encrypt variables marked as sensitive
|
||||
- **Data Retention Policy**: Custom policy description for compliance requirements
|
||||
|
||||
**Best Practices**:
|
||||
- Choose appropriate persistence based on business and legal requirements
|
||||
- Enable variable change logging for audit trails
|
||||
- Document retention policies clearly for compliance
|
||||
- Consider data privacy regulations (GDPR, CCPA) when setting retention
|
||||
|
||||
#### 4. Permissions Tab 🔒
|
||||
|
||||
Define access control and security settings:
|
||||
|
||||
**Execution Permissions**:
|
||||
- **Anyone**: Public access (use with caution)
|
||||
- **Authenticated Users**: Any logged-in user
|
||||
- **Specific Roles**: Define custom role-based access
|
||||
- **Process Managers Only**: Restrict to process managers
|
||||
- **Administrators Only**: Admin-only access
|
||||
|
||||
**Modification Permissions**:
|
||||
- **Process Owner Only**: Original creator only
|
||||
- **Process Managers**: Designated process managers
|
||||
- **Administrators**: System administrators
|
||||
- **Anyone with Edit Rights**: Users with edit permissions
|
||||
|
||||
**Advanced Security**:
|
||||
- **Require Approval for Changes**: Changes must be approved before taking effect
|
||||
- **Enable Audit Trail**: Detailed logs of all access and modifications
|
||||
|
||||
**Role-Based Configuration Example**:
|
||||
```
|
||||
Execution Permission: Specific Roles
|
||||
Allowed Roles: hr_manager, hr_admin, department_head
|
||||
Modification Permission: Process Managers
|
||||
Require Approval: Enabled
|
||||
Audit Trail: Enabled
|
||||
```
|
||||
|
||||
#### 5. JSON Export Tab 📊
|
||||
|
||||
Export complete process configuration for developers and system integration:
|
||||
|
||||
**Export Features**:
|
||||
- **Process Metadata**: Complete process information and settings
|
||||
- **Workflow Structure**: All nodes and edges with their configurations
|
||||
- **Variables**: All process variables and their definitions
|
||||
- **Permissions**: Access control and security settings
|
||||
- **Timestamps**: Creation and modification dates
|
||||
|
||||
**Export Options**:
|
||||
- **Copy to Clipboard**: Quick copy for immediate use
|
||||
- **Download JSON**: Save complete configuration file
|
||||
- **Developer Integration**: Use exported JSON for API integration or backup
|
||||
|
||||
**Export Data Structure**:
|
||||
```json
|
||||
{
|
||||
"processInfo": {
|
||||
"id": "process-uuid",
|
||||
"name": "Customer Onboarding",
|
||||
"description": "Complete customer onboarding workflow",
|
||||
"priority": "high",
|
||||
"category": "Sales",
|
||||
"owner": "Sales Manager"
|
||||
},
|
||||
"settings": {
|
||||
"processType": "approval",
|
||||
"maxExecutionTime": 480,
|
||||
"allowParallel": true,
|
||||
"enableErrorRecovery": true
|
||||
},
|
||||
"workflow": {
|
||||
"nodes": [...],
|
||||
"edges": [...]
|
||||
},
|
||||
"variables": {...},
|
||||
"permissions": {...},
|
||||
"metadata": {
|
||||
"exportedAt": "2024-12-20T10:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common Process Settings Scenarios
|
||||
|
||||
#### Scenario 1: HR Onboarding Process
|
||||
```
|
||||
Process Type: Standard Process
|
||||
Priority: High
|
||||
Max Execution Time: 1440 minutes (24 hours)
|
||||
Data Persistence: Long Term (30 days)
|
||||
Execution Permission: HR Managers
|
||||
Audit Trail: Enabled
|
||||
```
|
||||
|
||||
#### Scenario 2: Financial Approval Workflow
|
||||
```
|
||||
Process Type: Approval Workflow
|
||||
Priority: Critical
|
||||
Auto-Timeout: 48 hours
|
||||
Data Persistence: Permanent
|
||||
Execution Permission: Finance Team
|
||||
Require Approval: Enabled for changes
|
||||
```
|
||||
|
||||
#### Scenario 3: Customer Support Automation
|
||||
```
|
||||
Process Type: Automated Task
|
||||
Priority: Normal
|
||||
Parallel Execution: Enabled
|
||||
Data Persistence: Short Term (7 days)
|
||||
Execution Permission: Support Staff
|
||||
Error Recovery: Enabled
|
||||
```
|
||||
|
||||
### Process Settings Best Practices
|
||||
|
||||
#### Planning and Design
|
||||
1. **Define Clear Ownership**
|
||||
- Assign process owners for accountability
|
||||
- Document roles and responsibilities
|
||||
- Regular review and updates
|
||||
|
||||
2. **Set Appropriate Permissions**
|
||||
- Follow principle of least privilege
|
||||
- Use role-based access control
|
||||
- Regular permission audits
|
||||
|
||||
3. **Configure Realistic Timeouts**
|
||||
- Consider business requirements
|
||||
- Account for user availability
|
||||
- Set appropriate escalation procedures
|
||||
|
||||
#### Data Management
|
||||
1. **Choose Appropriate Persistence**
|
||||
- Balance performance with compliance needs
|
||||
- Consider storage costs
|
||||
- Document retention policies
|
||||
|
||||
2. **Implement Security Measures**
|
||||
- Enable audit trails for sensitive processes
|
||||
- Use encryption for PII data
|
||||
- Regular security reviews
|
||||
|
||||
#### Monitoring and Maintenance
|
||||
1. **Regular Settings Review**
|
||||
- Quarterly configuration reviews
|
||||
- Update settings as requirements change
|
||||
- Monitor performance and adjust timeouts
|
||||
|
||||
2. **Documentation Updates**
|
||||
- Keep process descriptions current
|
||||
- Document any changes made
|
||||
- Maintain version history
|
||||
|
||||
### Troubleshooting Process Settings
|
||||
|
||||
#### Common Issues
|
||||
1. **Settings Not Saving**
|
||||
- Verify all required fields are completed
|
||||
- Check permission levels
|
||||
- Ensure network connectivity
|
||||
|
||||
2. **Permission Errors**
|
||||
- Verify user roles and permissions
|
||||
- Check with administrator if needed
|
||||
- Review audit trail for access attempts
|
||||
|
||||
3. **Export/Import Issues**
|
||||
- Validate JSON format
|
||||
- Check file size limits
|
||||
- Verify browser clipboard permissions
|
||||
|
||||
#### Getting Help
|
||||
- Review error messages for specific guidance
|
||||
- Check the audit trail for permission issues
|
||||
- Contact administrators for role-related problems
|
||||
- Use the JSON export for debugging configuration issues
|
||||
|
||||
## Advanced Form Integration
|
||||
|
||||
The Process Builder features sophisticated integration with the Form Builder, enabling complex data flows and dynamic form behavior.
|
||||
|
||||
### Adding and Configuring Form Tasks
|
||||
|
||||
1. **Adding a Form Task**
|
||||
- Drag a Form Task component (emerald icon) onto the canvas
|
||||
- Connect it to your process flow using the 4-point connection system
|
||||
- Click "Configure Form Task" to open the configuration modal
|
||||
|
||||
2. **Step 1: Form Selection**
|
||||
- **Choose Existing Form**: Browse and select from available forms
|
||||
- **Form Preview**: See form structure and field details
|
||||
- **Create New Form**: Direct integration with Form Builder
|
||||
- The selected form determines available fields for mapping
|
||||
|
||||
3. **Step 2: Form Data Mapping**
|
||||
Configure bidirectional data flow between your process and the form:
|
||||
|
||||
**Input Variables (Process → Form)**
|
||||
- Map process variables to pre-fill form fields
|
||||
- Users see pre-populated data when the form loads
|
||||
- Useful for displaying existing customer data, calculated values, or previous submissions
|
||||
- Example: Map `customer.email` process variable to "Email Address" form field
|
||||
|
||||
**Output Variables (Form → Process)**
|
||||
- Capture form submission data in process variables
|
||||
- Create new variables automatically from form fields
|
||||
- Use captured data in subsequent process steps
|
||||
- Example: Map "Order Total" form field to `order.total` process variable
|
||||
|
||||
4. **Step 3: Field Conditions**
|
||||
Create dynamic form behavior based on process state:
|
||||
|
||||
**Condition Components**:
|
||||
- **Process Variable**: The variable to evaluate
|
||||
- **Operator**: Comparison type (equals, not equals, greater than, etc.)
|
||||
- **Value**: Comparison value (when applicable)
|
||||
- **Target Field**: The form field to control
|
||||
- **Action**: What to do when condition is met
|
||||
|
||||
**Available Actions**:
|
||||
- **Make Readonly**: Field displays value but cannot be edited
|
||||
- **Hide Field**: Field is completely hidden from user
|
||||
- **Make Required**: Field becomes mandatory for submission
|
||||
- **Make Optional**: Field becomes optional
|
||||
- **Show Field**: Field becomes visible (opposite of hide)
|
||||
- **Enable Field**: Field becomes editable (opposite of readonly)
|
||||
|
||||
**Operators**:
|
||||
- **Equals/Not Equals**: Exact value comparison
|
||||
- **Is True/Is False**: Boolean value checks
|
||||
- **Is Empty/Is Not Empty**: Check for presence of data
|
||||
- **Contains**: Text substring matching
|
||||
- **Greater Than/Less Than**: Numeric comparisons
|
||||
|
||||
### Example Use Cases
|
||||
|
||||
**Customer Onboarding Process**:
|
||||
```
|
||||
Input Mapping:
|
||||
- customer.type → "Customer Type" field (pre-select existing customers)
|
||||
|
||||
Output Mapping:
|
||||
- "Company Name" field → customer.company
|
||||
- "Industry" field → customer.industry
|
||||
|
||||
Field Conditions:
|
||||
- IF customer.type = "Existing" THEN "Company Name" = Readonly
|
||||
- IF customer.type = "Enterprise" THEN "Dedicated Rep" = Required
|
||||
```
|
||||
|
||||
**Expense Approval Process**:
|
||||
```
|
||||
Input Mapping:
|
||||
- employee.name → "Employee Name" field
|
||||
- expense.amount → "Amount" field
|
||||
|
||||
Output Mapping:
|
||||
- "Justification" field → expense.justification
|
||||
- "Manager Approval" field → expense.approved
|
||||
|
||||
Field Conditions:
|
||||
- IF expense.amount > 1000 THEN "Manager Approval" = Required
|
||||
- IF employee.level = "Executive" THEN "Amount" = Readonly
|
||||
```
|
||||
|
||||
**Support Ticket Process**:
|
||||
```
|
||||
Input Mapping:
|
||||
- ticket.priority → "Priority" field
|
||||
- customer.tier → "Customer Tier" field
|
||||
|
||||
Output Mapping:
|
||||
- "Resolution" field → ticket.resolution
|
||||
- "Satisfaction Rating" field → ticket.rating
|
||||
|
||||
Field Conditions:
|
||||
- IF customer.tier = "Premium" THEN "Priority" = Readonly AND "Escalation" = Show
|
||||
- IF ticket.priority = "Critical" THEN "Manager Review" = Required
|
||||
```
|
||||
|
||||
### Best Practices for Form Integration
|
||||
|
||||
1. **Data Flow Planning**
|
||||
- Map the complete data journey from process start to end
|
||||
- Identify which data needs to be captured vs. displayed
|
||||
- Plan for data validation and transformation needs
|
||||
|
||||
2. **Variable Naming**
|
||||
- Use consistent, descriptive variable names
|
||||
- Follow a naming convention (e.g., `customer.email`, `order.total`)
|
||||
- Create variables in the Variable Manager before mapping
|
||||
|
||||
3. **Field Condition Design**
|
||||
- Start with simple conditions and build complexity gradually
|
||||
- Test conditions with various scenarios
|
||||
- Document complex logic for future maintenance
|
||||
|
||||
4. **User Experience**
|
||||
- Pre-fill forms whenever possible to reduce user effort
|
||||
- Use conditional logic to show only relevant fields
|
||||
- Provide clear feedback when fields become readonly or required
|
||||
|
||||
5. **Performance Considerations**
|
||||
- Avoid excessive field conditions that could slow form rendering
|
||||
- Test forms with realistic data volumes
|
||||
- Consider caching for frequently accessed reference data
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Process Design
|
||||
1. Start with a Start event and end with End event(s)
|
||||
2. Use clear, descriptive labels for all elements
|
||||
3. Connect nodes in a logical flow
|
||||
4. Use gateways to manage decision points
|
||||
5. Keep the process layout organized and clear
|
||||
|
||||
### Flow Control
|
||||
1. Ensure all paths lead to an End event
|
||||
2. Validate connections make logical sense
|
||||
3. Use appropriate node types for each step
|
||||
4. Consider the process flow direction (typically top to bottom)
|
||||
|
||||
### Visual Organization
|
||||
1. Maintain consistent spacing between nodes
|
||||
2. Align nodes for better readability
|
||||
3. Use the auto-arrange feature when available
|
||||
4. Keep related elements grouped together
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
1. **Can't Create Connection**
|
||||
- Verify you're dragging from a handle
|
||||
- Check that source and target are compatible
|
||||
- Ensure you're connecting to a handle, not the node body
|
||||
|
||||
2. **Node Won't Delete**
|
||||
- Make sure the node is selected
|
||||
- Try using the Delete key
|
||||
- Use the Delete button in the properties panel
|
||||
- Double-click the node to remove it
|
||||
|
||||
3. **Connection Looks Wrong**
|
||||
- Try repositioning nodes for better flow
|
||||
- Check that connections are made to correct handles
|
||||
- Consider using different connection types
|
||||
|
||||
4. **Form Not Showing in Selector**
|
||||
- Verify the form was saved in the Form Builder
|
||||
- Check that you have permission to access the form
|
||||
- Refresh the page to update the form list
|
||||
|
||||
### Getting Help
|
||||
- Use the control panel hints in top-right
|
||||
- Review this documentation
|
||||
- Contact support team for additional assistance
|
||||
|
||||
---
|
||||
|
||||
For technical details about implementation and integration, please refer to the [Process Builder Technical Documentation](TECHNICAL_GUIDE.md).
|
||||
|
||||
Last updated: December 2024
|
||||
|
||||
## Working with Business Rules
|
||||
|
||||
Business Rules allow you to implement conditional logic in your process flows without writing code. They are essential for creating dynamic, data-driven processes.
|
||||
|
||||
### Creating Business Rules
|
||||
|
||||
To add business rule logic to your process:
|
||||
|
||||
1. **Add a Business Rule Node**
|
||||
- Drag the Business Rule component (purple icon) from the palette onto the canvas
|
||||
- Connect it to the appropriate nodes in your process flow
|
||||
|
||||
2. **Configure the Business Rule**
|
||||
- Click on the Business Rule node to open its configuration panel
|
||||
- Follow the three-step configuration process:
|
||||
|
||||
#### Step 1: Select Variables
|
||||
- Choose which process variables will be used in your rule conditions
|
||||
- These variables must be defined in the Variable Manager before use
|
||||
- You can filter and search for variables by name or type
|
||||
- If you need a new variable, you can create one directly from this screen
|
||||
|
||||
#### Step 2: Create Rule Conditions
|
||||
- Add one or more rules, each with its own set of conditions
|
||||
- For each condition, specify:
|
||||
- The variable to evaluate
|
||||
- The operator (equals, not equals, greater than, etc.)
|
||||
- The comparison value
|
||||
- Multiple conditions within a rule are combined with AND logic
|
||||
- Multiple rules are evaluated independently (OR logic between rules)
|
||||
|
||||
#### Step 3: Define Actions
|
||||
- For each rule, specify the action to take when its conditions are met
|
||||
- Action types include:
|
||||
- Setting variable values
|
||||
- Executing predefined functions
|
||||
- Triggering process events
|
||||
- Configure action parameters based on the selected action type
|
||||
- Actions execute in the order they are defined
|
||||
|
||||
### Testing Business Rules
|
||||
|
||||
Before saving your business rules, you can test them with sample data:
|
||||
|
||||
1. Click the "Test Rules" button at the bottom of the configuration panel
|
||||
2. Enter test values for each variable used in your conditions
|
||||
3. See which rules would trigger with the provided values
|
||||
4. Review the actions that would execute
|
||||
|
||||
### Best Practices for Business Rules
|
||||
|
||||
1. **Keep Rules Simple**
|
||||
- Create multiple simple rules instead of one complex rule
|
||||
- Name rules clearly to describe their purpose
|
||||
|
||||
2. **Use Consistent Naming**
|
||||
- Follow a naming convention for your variables
|
||||
- Use descriptive names that indicate the variable's purpose
|
||||
|
||||
3. **Document Your Logic**
|
||||
- Add descriptions to rules to explain their business purpose
|
||||
- Comment on complex conditions to clarify the logic
|
||||
|
||||
4. **Test Thoroughly**
|
||||
- Validate rules with various test cases
|
||||
- Check edge cases and boundary conditions
|
||||
|
||||
5. **Consider Performance**
|
||||
- Rules are evaluated in the order they appear
|
||||
- Place frequently triggered rules at the top for efficiency
|
||||
|
||||
### Example Use Cases
|
||||
|
||||
Business rules are useful in many scenarios, including:
|
||||
|
||||
1. **Customer Segmentation**
|
||||
- Route customers to different paths based on attributes like account size or loyalty status
|
||||
|
||||
2. **Data Validation**
|
||||
- Verify that data meets specific criteria before proceeding
|
||||
|
||||
3. **Dynamic Assignment**
|
||||
- Assign tasks to different teams based on request category, priority, or workload
|
||||
|
||||
4. **Automated Decisions**
|
||||
- Automatically approve or reject requests based on predefined criteria
|
||||
|
||||
5. **Conditional Notifications**
|
||||
- Send notifications only when specific conditions are met
|
@ -1,278 +0,0 @@
|
||||
# Process Execution - Technical Guide
|
||||
|
||||
## Introduction
|
||||
|
||||
The Process Execution module forms the user-facing interface of the workflow system, enabling end-users to interact with processes designed in the Process Builder. This document outlines the technical implementation of the Process Execution module, aimed at developers who need to understand, maintain, or extend the system.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The Process Execution module is built with the following technologies:
|
||||
|
||||
- **Frontend**: Vue 3 components with Nuxt 3 for routing and SSR
|
||||
- **State Management**: Pinia stores for managing process and task data
|
||||
- **UI Components**: Rose UI components (rs-prefixed) for consistent styling
|
||||
- **Form Handling**: FormKit for dynamic form rendering and validation
|
||||
- **Data Fetching**: Fetch composables with caching for API interactions
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Process Execution Module │
|
||||
├─────────────┬─────────────┬─────────────┬──────────────────┤
|
||||
│ Dashboard │ New Case │ Task Inbox │ Case History │
|
||||
├─────────────┴─────────────┴─────────────┴──────────────────┤
|
||||
│ Task Detail & Form Rendering Engine │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ API Layer │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Module Components
|
||||
|
||||
### Pages
|
||||
|
||||
The module consists of five main pages:
|
||||
|
||||
1. **Execution Dashboard** (`pages/execution/index.vue`)
|
||||
- Aggregates process and task data
|
||||
- Displays statistics and recent items
|
||||
- Provides navigation to other execution pages
|
||||
|
||||
2. **New Case Page** (`pages/execution/new-case.vue`)
|
||||
- Lists available processes that can be initiated
|
||||
- Includes filtering and search functionality
|
||||
- Handles process instantiation
|
||||
|
||||
3. **Task Inbox** (`pages/execution/inbox.vue`)
|
||||
- Lists tasks assigned to the current user
|
||||
- Provides advanced filtering options
|
||||
- Handles task status updates
|
||||
|
||||
4. **Case History** (`pages/execution/history.vue`)
|
||||
- Lists process instances and their statuses
|
||||
- Provides filtering by multiple criteria
|
||||
- Offers detailed case information access
|
||||
|
||||
5. **Task Detail** (`pages/execution/task/[id].vue`)
|
||||
- Renders task-specific forms
|
||||
- Displays case variables and process timeline
|
||||
- Handles task completion actions
|
||||
|
||||
### Components
|
||||
|
||||
Key components used throughout the module:
|
||||
|
||||
1. **TaskForm.vue**
|
||||
- Renders dynamic task forms based on task definition
|
||||
- Handles validation and submission
|
||||
- Uses FormKit for form element rendering
|
||||
|
||||
### Data Models
|
||||
|
||||
The module interacts with several data models:
|
||||
|
||||
1. **Process Instance**
|
||||
```typescript
|
||||
interface ProcessInstance {
|
||||
id: string;
|
||||
processDefinitionId: string;
|
||||
name: string;
|
||||
status: 'ACTIVE' | 'COMPLETED' | 'CANCELLED' | 'ERROR';
|
||||
startDate: string;
|
||||
endDate?: string;
|
||||
variables: Record<string, any>;
|
||||
tasks: Task[];
|
||||
}
|
||||
```
|
||||
|
||||
2. **Task**
|
||||
```typescript
|
||||
interface Task {
|
||||
id: string;
|
||||
name: string;
|
||||
processInstanceId: string;
|
||||
status: 'PENDING' | 'COMPLETED' | 'OVERDUE';
|
||||
assignee: string;
|
||||
dueDate?: string;
|
||||
formKey?: string;
|
||||
formData?: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
The module interacts with the following API endpoints:
|
||||
|
||||
### Processes
|
||||
|
||||
- `GET /api/processes` - Retrieve available process definitions
|
||||
- `POST /api/processes/{processId}/start` - Start a new process instance
|
||||
- `GET /api/processes/instances` - Retrieve process instances
|
||||
- `GET /api/processes/instances/{instanceId}` - Get a specific process instance
|
||||
|
||||
### Tasks
|
||||
|
||||
- `GET /api/tasks` - Retrieve tasks assigned to the current user
|
||||
- `GET /api/tasks/{taskId}` - Get a specific task
|
||||
- `POST /api/tasks/{taskId}/complete` - Complete a task with form data
|
||||
- `POST /api/tasks/{taskId}/claim` - Claim a task assignment
|
||||
- `POST /api/tasks/{taskId}/unclaim` - Release a task assignment
|
||||
|
||||
## Authentication and Authorization
|
||||
|
||||
The module relies on the application's authentication system with specific considerations:
|
||||
|
||||
- All execution pages require authentication (using the `auth` middleware)
|
||||
- Task accessibility is determined by task assignment
|
||||
- Process start permissions are controlled by user roles
|
||||
- Case history visibility respects process instance permissions
|
||||
|
||||
## State Management
|
||||
|
||||
Process Execution uses Pinia stores to manage state:
|
||||
|
||||
```typescript
|
||||
// Store for task management
|
||||
export const useTaskStore = defineStore('task', {
|
||||
state: () => ({
|
||||
tasks: [],
|
||||
currentTask: null,
|
||||
loading: false,
|
||||
error: null
|
||||
}),
|
||||
actions: {
|
||||
async fetchTasks() {
|
||||
// Implementation
|
||||
},
|
||||
async fetchTask(taskId) {
|
||||
// Implementation
|
||||
},
|
||||
async completeTask(taskId, formData) {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Store for process management
|
||||
export const useProcessStore = defineStore('process', {
|
||||
state: () => ({
|
||||
processDefinitions: [],
|
||||
processInstances: [],
|
||||
currentInstance: null,
|
||||
loading: false,
|
||||
error: null
|
||||
}),
|
||||
actions: {
|
||||
async fetchProcessDefinitions() {
|
||||
// Implementation
|
||||
},
|
||||
async startProcess(processId, initialData) {
|
||||
// Implementation
|
||||
},
|
||||
async fetchProcessInstances() {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Form Rendering
|
||||
|
||||
Process Execution dynamically renders forms based on form definitions:
|
||||
|
||||
1. Forms are retrieved from the Form Builder's repository
|
||||
2. FormKit schema is generated from the form definition
|
||||
3. The schema is rendered using FormKit components
|
||||
4. Validation rules are applied based on form configuration
|
||||
5. Submission data is processed and sent to the API
|
||||
|
||||
```javascript
|
||||
// Example of form rendering
|
||||
function renderTaskForm(formDefinition) {
|
||||
const schema = convertToFormKitSchema(formDefinition);
|
||||
return createForm({
|
||||
schema,
|
||||
onSubmit: async (formData) => {
|
||||
await completeTask(currentTaskId.value, formData);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Status Tracking and Visualization
|
||||
|
||||
Process status visualization is implemented using:
|
||||
|
||||
1. **Status Badges**
|
||||
- Color-coded status indicators (success, warning, danger)
|
||||
- Text labels for clear status identification
|
||||
|
||||
2. **Process Timeline**
|
||||
- Sequential visualization of process steps
|
||||
- Status indicators for each step
|
||||
- Current position indicator in the flow
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Data Caching**
|
||||
- Task and process data is cached to minimize API calls
|
||||
- Caching invalidated on relevant state changes
|
||||
|
||||
2. **Pagination**
|
||||
- Task lists and process history implement pagination
|
||||
- Configurable page sizes to optimize data loading
|
||||
|
||||
3. **Lazy Loading**
|
||||
- Task details and forms are lazy-loaded
|
||||
- Expandable sections to reduce initial load time
|
||||
|
||||
## Error Handling
|
||||
|
||||
1. **API Error Handling**
|
||||
- Centralized error handling for API responses
|
||||
- User-friendly error messages for common issues
|
||||
- Detailed logging for debugging
|
||||
|
||||
2. **Form Validation**
|
||||
- Client-side validation to reduce server load
|
||||
- Consistent error presentation for form fields
|
||||
- Submission validation to prevent data loss
|
||||
|
||||
## Future Improvements
|
||||
|
||||
Planned technical enhancements for the Process Execution module:
|
||||
|
||||
1. **Real-time Updates**
|
||||
- WebSocket integration for task list updates
|
||||
- Notifications for new task assignments
|
||||
|
||||
2. **Offline Support**
|
||||
- Task caching for offline access
|
||||
- Offline form submission queuing
|
||||
|
||||
3. **Performance Optimization**
|
||||
- Virtual scrolling for large task lists
|
||||
- Optimized form rendering for complex forms
|
||||
|
||||
4. **Integration Enhancements**
|
||||
- Enhanced document handling capabilities
|
||||
- External system integrations for task actions
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
When extending the Process Execution module:
|
||||
|
||||
1. **Component Reuse**
|
||||
- Utilize existing Rose UI components
|
||||
- Follow established patterns for new components
|
||||
|
||||
2. **State Management**
|
||||
- Use appropriate stores for data management
|
||||
- Maintain clear actions and mutations
|
||||
|
||||
3. **Form Handling**
|
||||
- Follow FormKit patterns for form implementation
|
||||
- Use validation utilities consistently
|
||||
|
||||
4. **API Interactions**
|
||||
- Use the established composables for API calls
|
||||
- Maintain proper error handling
|
@ -1,152 +0,0 @@
|
||||
# Process Execution - User Guide
|
||||
|
||||
## Introduction
|
||||
|
||||
The Process Execution module is the end-user interface of Corrad ProcessMaker, allowing users to interact with workflow processes that have been designed in the Process Builder. This module enables users to:
|
||||
|
||||
- Start new process instances
|
||||
- View and complete assigned tasks
|
||||
- Track the status and history of process instances
|
||||
- Access process-related information and context
|
||||
|
||||
This guide provides a comprehensive overview of how to use the Process Execution module effectively.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Access the Process Execution module by navigating to the "Process Execution" section in the main navigation menu. This section contains the following pages:
|
||||
|
||||
- **Execution Dashboard** - Overview of processes and tasks
|
||||
- **New Case** - Start new process instances
|
||||
- **Task Inbox** - View and manage assigned tasks
|
||||
- **Case History** - View historical process instances
|
||||
|
||||
## Execution Dashboard
|
||||
|
||||
The Execution Dashboard provides a high-level overview of your process-related activities.
|
||||
|
||||

|
||||
|
||||
### Dashboard Components
|
||||
|
||||
1. **Statistics Cards**
|
||||
- **Pending Tasks** - Number of tasks awaiting your action
|
||||
- **Active Cases** - Number of process instances currently running
|
||||
- **Completed Cases** - Number of process instances that have completed
|
||||
- **Overdue Tasks** - Number of tasks that have passed their due date
|
||||
|
||||
2. **Recent Tasks**
|
||||
- Displays a list of your most recent tasks
|
||||
- Shows task name, associated process, due date, and status
|
||||
- Click on the "Open" button to go directly to the task
|
||||
- Click on "View All Tasks" to navigate to the Task Inbox
|
||||
|
||||
3. **Recent Processes**
|
||||
- Displays a list of recently started processes
|
||||
- Shows process name, start date, and current status
|
||||
- Click on "View" to see detailed information about the process
|
||||
- Click on "View All Processes" to navigate to the Case History
|
||||
|
||||
## Starting a New Case
|
||||
|
||||
To initiate a new process instance, navigate to the "New Case" page from the Process Execution section.
|
||||
|
||||

|
||||
|
||||
### Steps to Start a New Case
|
||||
|
||||
1. **Browse Available Processes**
|
||||
- All available processes are displayed as cards
|
||||
- Each card shows the process name, description, category, average duration, and number of steps
|
||||
- Processes are color-coded by category
|
||||
|
||||
2. **Filter and Search**
|
||||
- Use the search box to find a specific process by name or description
|
||||
- Use the category dropdown to filter processes by department (HR, Finance, Operations, etc.)
|
||||
|
||||
3. **Start a Process**
|
||||
- Click the "Start Process" button on the desired process card
|
||||
- You will be directed to the first form or task in the process
|
||||
- Complete the form and submit to continue the process
|
||||
|
||||
## Managing Tasks in the Inbox
|
||||
|
||||
The Task Inbox displays all tasks assigned to you that require your action.
|
||||
|
||||

|
||||
|
||||
### Task Inbox Features
|
||||
|
||||
1. **Filtering and Searching**
|
||||
- Search for tasks by name, process, or case ID
|
||||
- Filter tasks by process type
|
||||
- Filter tasks by status (Pending, Overdue, Completed)
|
||||
|
||||
2. **Task Information**
|
||||
- Each task displays its name, associated process, case ID, and due date
|
||||
- Status indicators show whether a task is pending, overdue, or completed
|
||||
- Click on "Open Task" to view and work on the task
|
||||
|
||||
3. **Refreshing the Inbox**
|
||||
- Click the refresh button to update the task list
|
||||
- Tasks are automatically updated when you complete an action
|
||||
|
||||
## Completing Tasks
|
||||
|
||||
When you open a task from the Inbox, you will see the Task Detail page.
|
||||
|
||||

|
||||
|
||||
### Task Detail Components
|
||||
|
||||
1. **Task Information**
|
||||
- Task name and description
|
||||
- Process information and case ID
|
||||
- Due date and other task metadata
|
||||
|
||||
2. **Task Form**
|
||||
- Form fields required for task completion
|
||||
- Field validation ensures correct data entry
|
||||
- Submit button to complete the task
|
||||
|
||||
3. **Case Information**
|
||||
- Expandable panel showing case variables
|
||||
- Process timeline with status of each step
|
||||
- Links to related documents or information
|
||||
|
||||
4. **Actions**
|
||||
- Submit the form to complete the task
|
||||
- Save draft to continue later
|
||||
- View the case or process diagram
|
||||
|
||||
## Viewing Case History
|
||||
|
||||
The Case History page allows you to view all process instances (cases) and their current status.
|
||||
|
||||

|
||||
|
||||
### Case History Features
|
||||
|
||||
1. **Filtering and Searching**
|
||||
- Search for cases by ID or process name
|
||||
- Filter by process type
|
||||
- Filter by status (Completed, Cancelled, Error)
|
||||
- Filter by timeframe (Past Week, Past Month, Past Quarter, Past Year)
|
||||
|
||||
2. **Case Information**
|
||||
- Each case displays its ID, process name, start date, completion date, and duration
|
||||
- Status indicators show whether a case is completed, cancelled, or has encountered an error
|
||||
- Click on "View Details" to see the complete case information
|
||||
|
||||
3. **Case Details**
|
||||
- Timeline of all tasks and events in the case
|
||||
- Case variables and their values
|
||||
- Audit trail of user actions
|
||||
- Related documents and notes
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Regular Check-ins**: Review your Task Inbox regularly to ensure timely completion of tasks
|
||||
- **Proper Documentation**: When completing forms, provide thorough and accurate information
|
||||
- **Process Awareness**: Familiarize yourself with the processes you participate in to understand your role
|
||||
- **Communication**: Use comments or notes when available to communicate with other process participants
|
||||
- **Organized Workflow**: Complete tasks in order of priority, focusing on overdue tasks first
|
@ -243,6 +243,10 @@ const viewProcessAnalytics = (processId) => {
|
||||
router.push(`/process-builder/analytics/${processId}`);
|
||||
};
|
||||
|
||||
const viewProcessWorkflow = (processId) => {
|
||||
router.push(`/workflow/${processId}`);
|
||||
};
|
||||
|
||||
const duplicateProcess = async (process) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
@ -647,6 +651,16 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 ml-4">
|
||||
<!-- Run Workflow Button -->
|
||||
<button
|
||||
@click="viewProcessWorkflow(process.id)"
|
||||
class="p-2 text-green-600 hover:text-green-800 hover:bg-green-50 rounded-lg transition-colors"
|
||||
title="Run Workflow"
|
||||
:disabled="loading || process.status !== 'published'"
|
||||
>
|
||||
<Icon name="material-symbols:play-arrow" class="text-lg" />
|
||||
</button>
|
||||
|
||||
<!-- Analytics Button -->
|
||||
<button
|
||||
@click="viewProcessAnalytics(process.id)"
|
||||
|
614
pages/workflow/[id].vue
Normal file
614
pages/workflow/[id].vue
Normal file
@ -0,0 +1,614 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import FormScriptEngine from '~/components/FormScriptEngine.vue';
|
||||
import ConditionalLogicEngine from '~/components/ConditionalLogicEngine.vue';
|
||||
import ComponentPreview from '~/components/ComponentPreview.vue';
|
||||
import { useFormBuilderStore } from '~/stores/formBuilder';
|
||||
import { FormKit } from '@formkit/vue';
|
||||
|
||||
// Define page meta
|
||||
definePageMeta({
|
||||
title: "Process Execution",
|
||||
description: "Execute and run through a business process workflow",
|
||||
layout: "empty",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
|
||||
// Get route and router
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// Get form builder store for ComponentPreview data sharing
|
||||
const formStore = useFormBuilderStore();
|
||||
|
||||
// State
|
||||
const loading = ref(true);
|
||||
const process = ref(null);
|
||||
const currentStep = ref(0);
|
||||
const caseInstance = ref(null);
|
||||
const formData = ref({});
|
||||
const processVariables = ref({});
|
||||
const error = ref(null);
|
||||
const stepLoading = ref(false);
|
||||
const currentForm = ref(null);
|
||||
const tasks = ref([]);
|
||||
const conditionalLogicScript = ref('');
|
||||
const combinedScript = ref('');
|
||||
|
||||
// Get process ID from route
|
||||
const processId = computed(() => route.params.id);
|
||||
|
||||
// Get current workflow definition
|
||||
const workflowData = computed(() => {
|
||||
if (!process.value?.processDefinition) return null;
|
||||
return process.value.processDefinition;
|
||||
});
|
||||
|
||||
// Get current step node
|
||||
const currentNode = computed(() => {
|
||||
if (!workflowData.value?.nodes || currentStep.value >= workflowData.value.nodes.length) {
|
||||
return null;
|
||||
}
|
||||
return workflowData.value.nodes[currentStep.value];
|
||||
});
|
||||
|
||||
// Get next step
|
||||
const nextNode = computed(() => {
|
||||
if (!workflowData.value?.nodes || currentStep.value + 1 >= workflowData.value.nodes.length) {
|
||||
return null;
|
||||
}
|
||||
return workflowData.value.nodes[currentStep.value + 1];
|
||||
});
|
||||
|
||||
// Check if process is complete
|
||||
const isProcessComplete = computed(() => {
|
||||
return currentNode.value?.type === 'end' || currentStep.value >= (workflowData.value?.nodes?.length || 0);
|
||||
});
|
||||
|
||||
// Load process data
|
||||
const loadProcess = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
console.log('[Workflow] Loading process definition...');
|
||||
const response = await $fetch(`/api/process/${processId.value}`);
|
||||
|
||||
if (response.success) {
|
||||
process.value = response.process; // includes processDefinition
|
||||
console.log('[Workflow] Process loaded:', process.value.processName, process.value.processDefinition);
|
||||
|
||||
// Check if process is published
|
||||
const status = process.value.processStatus || process.value.status || 'draft';
|
||||
if (status !== 'published') {
|
||||
error.value = 'Process must be published before execution';
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize process variables from DB (process.processVariables)
|
||||
processVariables.value = process.value.processVariables ? { ...process.value.processVariables } : {};
|
||||
|
||||
// Start the process execution (case instance)
|
||||
await startProcessExecution();
|
||||
|
||||
} else {
|
||||
error.value = response.message || 'Failed to load process';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Workflow] Error loading process:', err);
|
||||
error.value = 'Failed to load process data';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Start process execution (create case instance)
|
||||
const startProcessExecution = async () => {
|
||||
try {
|
||||
console.log('[Workflow] Starting process execution (creating case instance)...');
|
||||
const response = await $fetch(`/api/process/${processId.value}/start`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
caseInstance.value = response.data.case;
|
||||
tasks.value = response.data.tasks;
|
||||
console.log('[Workflow] Case instance created:', caseInstance.value);
|
||||
// Find the start node
|
||||
const startNodeIndex = workflowData.value.nodes.findIndex(node => node.type === 'start');
|
||||
currentStep.value = startNodeIndex >= 0 ? startNodeIndex : 0;
|
||||
console.log('[Workflow] Starting at node index:', currentStep.value, workflowData.value.nodes[currentStep.value]);
|
||||
moveToNextStep();
|
||||
} else {
|
||||
throw new Error(response.error || 'Failed to start process');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Workflow] Error starting process execution:', err);
|
||||
error.value = 'Failed to start process execution';
|
||||
}
|
||||
};
|
||||
|
||||
// Helper: Get next node ID by following edges
|
||||
function getNextNodeId(currentNodeId) {
|
||||
const edge = workflowData.value.edges.find(e => e.source === currentNodeId);
|
||||
return edge ? edge.target : null;
|
||||
}
|
||||
|
||||
// Move to next step in workflow (edge-based)
|
||||
const moveToNextStep = () => {
|
||||
const currentNode = workflowData.value.nodes[currentStep.value];
|
||||
if (!currentNode) return;
|
||||
const nextNodeId = getNextNodeId(currentNode.id);
|
||||
if (nextNodeId) {
|
||||
const nextIndex = workflowData.value.nodes.findIndex(n => n.id === nextNodeId);
|
||||
if (nextIndex !== -1) {
|
||||
currentStep.value = nextIndex;
|
||||
const node = workflowData.value.nodes[currentStep.value];
|
||||
console.log(`[Workflow] Entered node: ${node.type} - ${node.data?.label || node.label}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle form submission
|
||||
const handleFormSubmit = async () => {
|
||||
try {
|
||||
stepLoading.value = true;
|
||||
|
||||
console.log('[Workflow] Form submitted. Data:', formData.value);
|
||||
// Save form data to process variables
|
||||
Object.assign(processVariables.value, formData.value);
|
||||
|
||||
// Move to next step
|
||||
moveToNextStep();
|
||||
|
||||
console.log('[Workflow] After form submit, current node:', currentNode.value);
|
||||
|
||||
// If next step is API or script, execute it automatically
|
||||
if (currentNode.value && ['api', 'script'].includes(currentNode.value.type)) {
|
||||
await executeCurrentStep();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Workflow] Error submitting form:', err);
|
||||
error.value = 'Failed to submit form';
|
||||
} finally {
|
||||
stepLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Execute current step (for API/script nodes)
|
||||
const executeCurrentStep = async () => {
|
||||
try {
|
||||
stepLoading.value = true;
|
||||
|
||||
console.log('[Workflow] Executing current step:', currentNode.value);
|
||||
|
||||
if (currentNode.value?.type === 'api') {
|
||||
console.log(`[Workflow] Executing API node: ${currentNode.value.data?.label || currentNode.value.label}`);
|
||||
// Enhanced API node execution
|
||||
const {
|
||||
apiUrl,
|
||||
apiMethod = 'GET',
|
||||
headers = '{}',
|
||||
requestBody = '',
|
||||
outputVariable = 'apiResponse',
|
||||
errorVariable = 'apiError',
|
||||
continueOnError = false
|
||||
} = currentNode.value.data || {};
|
||||
try {
|
||||
const response = await $fetch(apiUrl, {
|
||||
method: apiMethod,
|
||||
headers: headers ? JSON.parse(headers) : {},
|
||||
body: requestBody ? JSON.parse(requestBody) : undefined,
|
||||
});
|
||||
processVariables.value[outputVariable] = response;
|
||||
processVariables.value[errorVariable] = null;
|
||||
console.log('[Workflow] API call success. Output variable set:', outputVariable, response);
|
||||
moveToNextStep();
|
||||
} catch (err) {
|
||||
processVariables.value[errorVariable] = err;
|
||||
console.error('[Workflow] API call failed:', err);
|
||||
if (continueOnError) {
|
||||
moveToNextStep();
|
||||
} else {
|
||||
error.value = 'API call failed: ' + (err.message || err);
|
||||
}
|
||||
}
|
||||
} else if (currentNode.value?.type === 'script') {
|
||||
console.log(`[Workflow] Executing script node: ${currentNode.value.data?.label || currentNode.value.label}`);
|
||||
// Simulate script execution
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
moveToNextStep();
|
||||
}
|
||||
// Add more node types as needed
|
||||
} catch (err) {
|
||||
console.error('[Workflow] Error executing step:', err);
|
||||
error.value = 'Failed to execute step';
|
||||
} finally {
|
||||
stepLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get step type display info
|
||||
const getStepInfo = (node) => {
|
||||
const stepTypes = {
|
||||
'start': { label: 'Process Started', icon: 'material-symbols:play-circle', color: 'green' },
|
||||
'form': { label: 'User Form', icon: 'material-symbols:description', color: 'blue' },
|
||||
'api': { label: 'API Call', icon: 'material-symbols:api', color: 'purple' },
|
||||
'script': { label: 'Script Execution', icon: 'material-symbols:code', color: 'orange' },
|
||||
'decision': { label: 'Decision Point', icon: 'material-symbols:alt-route', color: 'yellow' },
|
||||
'end': { label: 'Process Complete', icon: 'material-symbols:check-circle', color: 'green' }
|
||||
};
|
||||
|
||||
return stepTypes[node?.type] || { label: 'Unknown Step', icon: 'material-symbols:help', color: 'gray' };
|
||||
};
|
||||
|
||||
// Load form data from database
|
||||
const loadFormData = async (formId) => {
|
||||
try {
|
||||
if (!formId) return null;
|
||||
|
||||
const response = await $fetch(`/api/forms/${formId}`);
|
||||
if (response.success) {
|
||||
return response.form;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Error loading form:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Note: isInputType function removed as ComponentPreview handles all component types
|
||||
|
||||
// Get truncated custom script for display
|
||||
const customScriptPreview = computed(() => {
|
||||
if (!currentForm.value?.customScript) return '';
|
||||
return currentForm.value.customScript.substring(0, 200) + '...';
|
||||
});
|
||||
|
||||
// Handle script-driven field changes
|
||||
const handleScriptFieldChange = ({ fieldName, value }) => {
|
||||
console.log('[WorkflowExecution] Script field change:', fieldName, '=', value);
|
||||
// Update form data with script changes
|
||||
formData.value[fieldName] = value;
|
||||
// Also update form store for ComponentPreview
|
||||
formStore.updatePreviewFormData(formData.value);
|
||||
};
|
||||
|
||||
// Handle conditional logic script generation
|
||||
const handleConditionalLogicGenerated = (generatedScript) => {
|
||||
console.log('[WorkflowExecution] Conditional logic script generated');
|
||||
conditionalLogicScript.value = generatedScript;
|
||||
|
||||
// Combine conditional logic with custom script
|
||||
const customScript = currentForm.value?.customScript || '';
|
||||
combinedScript.value = [conditionalLogicScript.value, customScript].filter(Boolean).join('\n\n');
|
||||
};
|
||||
|
||||
// Navigation functions
|
||||
const goHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
|
||||
// Load process on mount
|
||||
onMounted(() => {
|
||||
loadProcess();
|
||||
});
|
||||
|
||||
// Watch for step changes to auto-execute non-form steps or load form data
|
||||
watch(currentStep, async (newStep) => {
|
||||
if (currentNode.value) {
|
||||
if (['api', 'script'].includes(currentNode.value.type)) {
|
||||
await executeCurrentStep();
|
||||
} else if (currentNode.value.type === 'form') {
|
||||
// Load form data for form nodes
|
||||
const formId = currentNode.value.data?.formId;
|
||||
if (formId) {
|
||||
currentForm.value = await loadFormData(formId);
|
||||
// Update form store with form components and data for ComponentPreview
|
||||
if (currentForm.value?.formComponents) {
|
||||
formStore.formComponents = currentForm.value.formComponents;
|
||||
formStore.updatePreviewFormData(formData.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Watch formData changes and sync with form store
|
||||
watch(formData, (newData) => {
|
||||
formStore.updatePreviewFormData(newData);
|
||||
}, { deep: true });
|
||||
|
||||
// Add FormKit form ref
|
||||
const formRef = ref(null);
|
||||
|
||||
// New: handle FormKit form submit
|
||||
const onFormKitSubmit = () => {
|
||||
handleFormSubmit();
|
||||
};
|
||||
|
||||
// New: validate and submit handler for button
|
||||
const validateAndSubmit = () => {
|
||||
if (formRef.value && formRef.value.node && typeof formRef.value.node.submit === 'function') {
|
||||
formRef.value.node.submit();
|
||||
}
|
||||
};
|
||||
|
||||
// Compute the workflow path by following edges from start to end
|
||||
function computeWorkflowPath() {
|
||||
const path = [];
|
||||
const nodes = workflowData.value?.nodes || [];
|
||||
const edges = workflowData.value?.edges || [];
|
||||
if (!nodes.length) return path;
|
||||
let node = nodes.find(n => n.type === 'start');
|
||||
while (node) {
|
||||
path.push(node.id);
|
||||
const outgoingEdges = edges.filter(e => e.source === node.id);
|
||||
if (outgoingEdges.length > 1) {
|
||||
console.warn('[Workflow] Multiple outgoing edges found for node', node.id, outgoingEdges);
|
||||
// For progress, just follow the first edge for now
|
||||
}
|
||||
const edge = outgoingEdges[0];
|
||||
node = edge ? nodes.find(n => n.id === edge.target) : null;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
const workflowPath = ref([]);
|
||||
|
||||
watch(
|
||||
() => workflowData.value,
|
||||
() => {
|
||||
workflowPath.value = computeWorkflowPath();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const currentStepIndex = computed(() => {
|
||||
return workflowPath.value.indexOf(currentNode.value?.id) + 1;
|
||||
});
|
||||
const totalSteps = computed(() => workflowPath.value.length);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Header -->
|
||||
<header class="bg-white border-b border-gray-200 px-6 py-4 shadow-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<Icon
|
||||
@click="goHome"
|
||||
name="ph:arrow-circle-left-duotone"
|
||||
class="cursor-pointer w-6 h-6 hover:text-gray-600 text-gray-500"
|
||||
/>
|
||||
<div class="flex items-center gap-3">
|
||||
<img
|
||||
src="@/assets/img/logo/logo-word-black.svg"
|
||||
alt="Corrad Logo"
|
||||
class="h-8"
|
||||
/>
|
||||
<div class="border-l border-gray-300 pl-3">
|
||||
<h1 class="text-xl font-semibold text-gray-900">Process Execution</h1>
|
||||
<p class="text-sm text-gray-500">
|
||||
{{ process?.processName || 'Loading...' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress indicator -->
|
||||
<div v-if="!loading && !error" class="flex items-center gap-2 text-sm text-gray-600">
|
||||
<span>Step {{ currentStepIndex }} of {{ totalSteps }}</span>
|
||||
<div class="w-32 bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
class="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||
:style="{ width: `${((currentStepIndex) / (totalSteps || 1)) * 100}%` }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="container mx-auto px-6 py-8 max-w-4xl">
|
||||
<!-- Loading State -->
|
||||
<div v-if="loading" class="flex justify-center items-center py-12">
|
||||
<div class="text-center">
|
||||
<Icon name="material-symbols:progress-activity" class="w-8 h-8 animate-spin text-blue-500 mx-auto mb-2" />
|
||||
<p class="text-gray-500">Loading process...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div v-else-if="error" class="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" />
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Error</h3>
|
||||
<p class="text-gray-600 mb-6">{{ error }}</p>
|
||||
<RsButton @click="goHome" variant="primary">
|
||||
<Icon name="material-symbols:home" class="mr-2" />
|
||||
Go Home
|
||||
</RsButton>
|
||||
</div>
|
||||
|
||||
<!-- Process Complete -->
|
||||
<div v-else-if="isProcessComplete" class="bg-white rounded-xl shadow-sm border border-gray-200 p-12 text-center">
|
||||
<Icon name="material-symbols:check-circle" class="w-16 h-16 text-green-500 mx-auto mb-4" />
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-2">Process Complete!</h2>
|
||||
<p class="text-gray-600 mb-6">
|
||||
The workflow "{{ process.processName }}" has been completed successfully.
|
||||
</p>
|
||||
<div class="flex justify-center gap-3">
|
||||
<RsButton @click="loadProcess" variant="secondary">
|
||||
<Icon name="material-symbols:refresh" class="mr-2" />
|
||||
Run Again
|
||||
</RsButton>
|
||||
<RsButton @click="goHome" variant="primary">
|
||||
<Icon name="material-symbols:home" class="mr-2" />
|
||||
Go Home
|
||||
</RsButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Step -->
|
||||
<div v-else-if="currentNode" class="space-y-6">
|
||||
<!-- Step Info -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div :class="[
|
||||
'p-3 rounded-lg',
|
||||
getStepInfo(currentNode).color === 'blue' ? 'bg-blue-100 text-blue-600' :
|
||||
getStepInfo(currentNode).color === 'green' ? 'bg-green-100 text-green-600' :
|
||||
getStepInfo(currentNode).color === 'purple' ? 'bg-purple-100 text-purple-600' :
|
||||
getStepInfo(currentNode).color === 'orange' ? 'bg-orange-100 text-orange-600' :
|
||||
'bg-gray-100 text-gray-600'
|
||||
]">
|
||||
<Icon :name="getStepInfo(currentNode).icon" class="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900">{{ getStepInfo(currentNode).label }}</h2>
|
||||
<p class="text-gray-600">{{ currentNode.data?.label || currentNode.data?.name || `Step ${currentStepIndex}` }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Step -->
|
||||
<div v-if="currentNode.type === 'form'" class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">
|
||||
{{ currentNode.data?.formName || currentNode.data?.label || 'Please fill out the form' }}
|
||||
</h3>
|
||||
<p v-if="currentForm?.formDescription" class="text-gray-600 text-sm">
|
||||
{{ currentForm.formDescription }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Loading form data -->
|
||||
<div v-if="!currentForm && currentNode.data?.formId" class="flex justify-center py-8">
|
||||
<div class="text-center">
|
||||
<Icon name="material-symbols:progress-activity" class="w-6 h-6 animate-spin text-blue-500 mx-auto mb-2" />
|
||||
<p class="text-gray-500">Loading form...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form rendering -->
|
||||
<div v-else-if="currentForm && currentForm.formComponents" class="form-container">
|
||||
<!-- Conditional Logic Engine for FormKit conditional logic -->
|
||||
<ConditionalLogicEngine
|
||||
:form-components="currentForm.formComponents"
|
||||
:form-data="formData"
|
||||
@script-generated="handleConditionalLogicGenerated"
|
||||
/>
|
||||
|
||||
<!-- Form Script Engine for conditional logic + custom script -->
|
||||
<FormScriptEngine
|
||||
v-if="combinedScript"
|
||||
:form-data="formData"
|
||||
:custom-script="combinedScript"
|
||||
:custom-css="currentForm.customCSS"
|
||||
:form-events="currentForm.formEvents || { onLoad: true, onFieldChange: true }"
|
||||
:script-mode="currentForm.scriptMode || 'safe'"
|
||||
@field-change="handleScriptFieldChange"
|
||||
/>
|
||||
|
||||
<!-- FormKit form wrapper -->
|
||||
<FormKit
|
||||
type="form"
|
||||
v-model="formData"
|
||||
ref="formRef"
|
||||
@submit="onFormKitSubmit"
|
||||
:actions="false"
|
||||
:incomplete-message="false"
|
||||
validation-visibility="submit"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<template v-for="(component, index) in currentForm.formComponents" :key="index">
|
||||
<ComponentPreview
|
||||
:component="component"
|
||||
:is-preview="false"
|
||||
/>
|
||||
</template>
|
||||
<div class="flex justify-end pt-6 border-t border-gray-200">
|
||||
<RsButton
|
||||
@click="validateAndSubmit"
|
||||
:disabled="stepLoading"
|
||||
variant="primary"
|
||||
>
|
||||
<Icon v-if="stepLoading" name="material-symbols:progress-activity" class="w-4 h-4 animate-spin mr-2" />
|
||||
{{ stepLoading ? 'Processing...' : 'Submit & Continue' }}
|
||||
</RsButton>
|
||||
</div>
|
||||
</div>
|
||||
</FormKit>
|
||||
|
||||
<!-- Script Info (for debugging) -->
|
||||
<div v-if="combinedScript" class="mt-4 p-3 bg-gray-50 rounded-lg border">
|
||||
<details>
|
||||
<summary class="text-sm font-medium text-gray-700 cursor-pointer">Form Logic Available (Development)</summary>
|
||||
<div class="mt-2 space-y-2">
|
||||
<div v-if="conditionalLogicScript">
|
||||
<p class="text-xs font-medium text-gray-600">Conditional Logic:</p>
|
||||
<pre class="text-xs text-gray-600 bg-white p-2 rounded border overflow-auto max-h-32">{{ conditionalLogicScript.substring(0, 200) }}...</pre>
|
||||
</div>
|
||||
<div v-if="currentForm?.customScript">
|
||||
<p class="text-xs font-medium text-gray-600">Custom Script:</p>
|
||||
<pre class="text-xs text-gray-600 bg-white p-2 rounded border overflow-auto max-h-32">{{ customScriptPreview }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fallback if no form data -->
|
||||
<div v-else class="text-center py-8">
|
||||
<Icon name="material-symbols:description-outline" class="w-12 h-12 text-gray-300 mx-auto mb-4" />
|
||||
<h4 class="text-lg font-medium text-gray-900 mb-2">Form Not Found</h4>
|
||||
<p class="text-gray-600 mb-4">The form associated with this step could not be loaded.</p>
|
||||
<RsButton @click="moveToNextStep" variant="primary">
|
||||
<Icon name="material-symbols:skip-next" class="mr-2" />
|
||||
Skip to Next Step
|
||||
</RsButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- API/Script Step -->
|
||||
<div v-else-if="['api', 'script'].includes(currentNode.type)" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 text-center">
|
||||
<Icon name="material-symbols:progress-activity" class="w-8 h-8 animate-spin text-blue-500 mx-auto mb-4" />
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">
|
||||
{{ currentNode.type === 'api' ? 'Calling API...' : 'Executing Script...' }}
|
||||
</h3>
|
||||
<p class="text-gray-600">Please wait while we process this step</p>
|
||||
</div>
|
||||
|
||||
<!-- Other Step Types -->
|
||||
<div v-else class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 text-center">
|
||||
<Icon name="material-symbols:info" class="w-8 h-8 text-blue-500 mx-auto mb-4" />
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">{{ getStepInfo(currentNode).label }}</h3>
|
||||
<p class="text-gray-600 mb-6">This step type is not yet implemented</p>
|
||||
<RsButton @click="moveToNextStep" variant="primary">
|
||||
<Icon name="material-symbols:skip-next" class="mr-2" />
|
||||
Skip to Next Step
|
||||
</RsButton>
|
||||
</div>
|
||||
|
||||
<!-- Process Variables Debug (only in development) -->
|
||||
<div v-if="Object.keys(processVariables).length > 0" class="bg-gray-100 rounded-lg p-4">
|
||||
<details>
|
||||
<summary class="font-medium text-gray-700 cursor-pointer mb-2">Process Variables (Debug)</summary>
|
||||
<pre class="text-xs text-gray-600 bg-white p-2 rounded border overflow-auto">{{ JSON.stringify(processVariables, null, 2) }}</pre>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.transition-all {
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user