From eadf3443de4db14840ad36d333445fe15b704ba9 Mon Sep 17 00:00:00 2001 From: Md Afiq Iskandar Date: Tue, 15 Jul 2025 12:52:54 +0800 Subject: [PATCH] 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. --- .claude/settings.local.json | 8 + docs/BR/business-rules.md | 111 - docs/form-builder/CHANGELOG.md | 303 -- docs/form-builder/FORM_HISTORY_SYSTEM.md | 134 - docs/form-builder/JAVASCRIPT_API.md | 767 ----- docs/form-builder/ROADMAP.md | 405 --- docs/form-builder/TECHNICAL_GUIDE.md | 1918 ------------ docs/form-builder/USER_GUIDE.md | 672 ----- docs/form-builder/grid-system.md | 587 ---- docs/json/form/customScript.js | 580 ---- docs/json/form/formComponents.json | 732 ++--- .../process-builder/processDefinition.json | 2238 +------------- .../process-builder/processVariables.json | 1368 +-------- docs/overview/ARCHITECTURE.md | 263 -- docs/overview/DEVELOPMENT_GUIDE.md | 217 -- docs/overview/PROJECT_OVERVIEW.md | 158 - .../BUSINESS_RULES_TROUBLESHOOTING.md | 383 --- .../NODE_COLOR_CUSTOMIZATION.md | 121 - .../PROCESS_FLOW_CREATION_GUIDE.md | 833 ------ docs/process-builder/ROADMAP.md | 347 --- docs/process-builder/TECHNICAL_GUIDE.md | 2619 ----------------- docs/process-builder/USER_GUIDE.md | 782 ----- docs/process-execution/TECHNICAL_GUIDE.md | 278 -- docs/process-execution/USER_GUIDE.md | 152 - pages/process-builder/manage.vue | 14 + pages/workflow/[id].vue | 614 ++++ 26 files changed, 893 insertions(+), 15711 deletions(-) create mode 100644 .claude/settings.local.json delete mode 100644 docs/BR/business-rules.md delete mode 100644 docs/form-builder/CHANGELOG.md delete mode 100644 docs/form-builder/FORM_HISTORY_SYSTEM.md delete mode 100644 docs/form-builder/JAVASCRIPT_API.md delete mode 100644 docs/form-builder/ROADMAP.md delete mode 100644 docs/form-builder/TECHNICAL_GUIDE.md delete mode 100644 docs/form-builder/USER_GUIDE.md delete mode 100644 docs/form-builder/grid-system.md delete mode 100644 docs/overview/ARCHITECTURE.md delete mode 100644 docs/overview/DEVELOPMENT_GUIDE.md delete mode 100644 docs/overview/PROJECT_OVERVIEW.md delete mode 100644 docs/process-builder/BUSINESS_RULES_TROUBLESHOOTING.md delete mode 100644 docs/process-builder/NODE_COLOR_CUSTOMIZATION.md delete mode 100644 docs/process-builder/PROCESS_FLOW_CREATION_GUIDE.md delete mode 100644 docs/process-builder/ROADMAP.md delete mode 100644 docs/process-builder/TECHNICAL_GUIDE.md delete mode 100644 docs/process-builder/USER_GUIDE.md delete mode 100644 docs/process-execution/TECHNICAL_GUIDE.md delete mode 100644 docs/process-execution/USER_GUIDE.md create mode 100644 pages/workflow/[id].vue diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..a4af320 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(mkdir:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/docs/BR/business-rules.md b/docs/BR/business-rules.md deleted file mode 100644 index c63fab9..0000000 --- a/docs/BR/business-rules.md +++ /dev/null @@ -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. diff --git a/docs/form-builder/CHANGELOG.md b/docs/form-builder/CHANGELOG.md deleted file mode 100644 index f966a9b..0000000 --- a/docs/form-builder/CHANGELOG.md +++ /dev/null @@ -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 - -``` - ---- - -### 📊 **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.* \ No newline at end of file diff --git a/docs/form-builder/FORM_HISTORY_SYSTEM.md b/docs/form-builder/FORM_HISTORY_SYSTEM.md deleted file mode 100644 index 676571a..0000000 --- a/docs/form-builder/FORM_HISTORY_SYSTEM.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/form-builder/JAVASCRIPT_API.md b/docs/form-builder/JAVASCRIPT_API.md deleted file mode 100644 index 93ddfc8..0000000 --- a/docs/form-builder/JAVASCRIPT_API.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/form-builder/ROADMAP.md b/docs/form-builder/ROADMAP.md deleted file mode 100644 index 4c0cf24..0000000 --- a/docs/form-builder/ROADMAP.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/form-builder/TECHNICAL_GUIDE.md b/docs/form-builder/TECHNICAL_GUIDE.md deleted file mode 100644 index 1f41d1d..0000000 --- a/docs/form-builder/TECHNICAL_GUIDE.md +++ /dev/null @@ -1,1918 +0,0 @@ -# Form Builder Technical Guide - -This document provides comprehensive technical implementation details for developers working with the Form Builder system, including the new smart collapsible panel, enhanced grid system, and advanced UX features. - -> For user documentation and usage guidelines, see the [User Guide](USER_GUIDE.md) - -## Recent Development Updates (December 2024) - -### 🔍 Critical Discovery: Dual Configuration System - -During recent development work, a critical architectural issue was discovered: **two separate configuration systems** exist for form components, causing confusion and preventing new component settings from appearing in the form builder interface. - -#### Configuration Systems Identified: - -**1. FormBuilderConfiguration.vue** (Enhanced but not in use) -- Location: `components/FormBuilderConfiguration.vue` -- Status: Extensively enhanced with new component settings -- Issue: Not actually used by the form builder interface -- Features: Comprehensive configuration panels for all component types - -**2. FormBuilderFieldSettingsModal.vue** (Actually used) -- Location: `components/FormBuilderFieldSettingsModal.vue` -- Status: This is the configuration system actually used by the form builder -- Current State: Limited component support, missing new enhancements -- Integration: Direct integration with form builder drag-and-drop system - -#### Resolution Strategy: -1. **Short-term**: Migrate enhanced configurations to FormBuilderFieldSettingsModal.vue -2. **Long-term**: Consider unifying both systems or deprecating unused system -3. **Current Progress**: Dynamic List Component migration 80% complete - -### 🚀 Dynamic List Component Enhancement - -The Dynamic List Component has been comprehensively enhanced with professional-grade features: - -#### New Properties Added (FormBuilderComponents.vue): -```javascript -{ - name: 'dynamic-list', - settings: [ - 'label', 'name', 'help', 'placeholder', 'buttonText', - 'minItems', 'maxItems', 'defaultItems', 'width', - // NEW ENHANCED PROPERTIES: - 'itemValidation', // Individual item validation rules - 'allowDuplicates', // Prevent duplicate entries - 'enableSorting', // Visual sorting indicators - 'enableSearch', // Search/filter functionality - 'itemType', // Type validation (text/number/email/url) - 'showItemCounter', // Display item count - 'confirmDelete', // Confirmation before deletion - 'bulkOperations', // Bulk select/delete operations - 'exportFormat', // Export data format (JSON/CSV/TXT) - 'importEnabled' // Enable data import functionality - ] -} -``` - -#### Implementation Details: - -**Validation Engine** (ComponentPreview.vue): -```javascript -const validateItem = (item, index) => { - const errors = [] - - // Type validation - if (itemType.value === 'email' && !isValidEmail(item)) { - errors.push('Invalid email format') - } - - // Uniqueness validation - 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**: -```javascript -const filteredItems = computed(() => { - if (!searchQuery.value) return modelValue.value || [] - return (modelValue.value || []).filter(item => - item.toLowerCase().includes(searchQuery.value.toLowerCase()) - ) -}) -``` - -**Bulk Operations**: -```javascript -const bulkDelete = () => { - if (selectedItems.value.length === 0) return - - const newItems = (modelValue.value || []).filter((_, index) => - !selectedItems.value.includes(index) - ) - - emit('update:modelValue', newItems) - selectedItems.value = [] -} -``` - -**Import/Export System**: -```javascript -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') - } -} -``` - -#### Files Modified: -- ✅ `components/FormBuilderComponents.vue` - Added 10 new properties -- ✅ `components/FormBuilderConfiguration.vue` - Comprehensive configuration UI (not in use) -- ✅ `components/ComponentPreview.vue` - Full feature implementation -- 🟡 `components/FormBuilderFieldSettingsModal.vue` - Partial migration (in progress) - -### 🔧 Configuration Panel Integration Status - -**Current Issue**: Enhanced Dynamic List settings not appearing in form builder -**Root Cause**: Settings added to wrong configuration system -**Solution Progress**: 80% complete - -**Completed Steps**: -1. ✅ Added dynamic-list to field configurations (label, name, placeholder, help, width) -2. ✅ Added dynamic-list to hasSpecificSettings array -3. ✅ Added icon, type name, and description -4. 🟡 **IN PROGRESS**: Template section for dynamic-list specific settings - -**Remaining Work**: -- [ ] Complete template section in FormBuilderFieldSettingsModal.vue -- [ ] Test configuration panel functionality -- [ ] Migrate other enhanced components - ---- - -## Architecture Overview - -### Technology Stack -- **Frontend Framework**: Nuxt 3 / Vue 3 -- **State Management**: Pinia -- **Form Library**: FormKit -- **UI Framework**: Tailwind CSS + Custom Components -- **Icons**: Heroicons -- **Drag & Drop**: vuedraggable -- **Grid System**: CSS Grid (12-column) - -### Key Dependencies -```json -{ - "@formkit/nuxt": "^1.0.0", - "@pinia/nuxt": "^0.4.11", - "vuedraggable": "^4.1.0", - "@vueuse/core": "^10.1.2", - "tailwindcss": "^3.3.2", - "@heroicons/vue": "^2.0.18" -} -``` - -## Project Structure - -``` -pages/ -├── form-builder/ -│ ├── index.vue # Main builder interface with collapsible panel -│ └── manage.vue # Form management -components/ -├── FormBuilderComponents.vue # Enhanced component library -├── FormBuilderCanvas.vue # Grid-based canvas with resize handles -├── FormBuilderFieldSettingsModal.vue # Full settings modal with visual grid -├── ComponentPreview.vue # Improved component rendering -└── RsCollapseItem.vue # Collapsible components -stores/ -├── formBuilder.js # Enhanced state management with grid logic -└── toast.js # Toast notifications -composables/ -└── useToast.js # Toast composition -types/ -└── form-builder.d.ts # TypeScript definitions -server/ -└── api/ - └── forms/ - ├── index.js # Form API endpoints - └── [id].js # Form by ID endpoints -docs/ -└── grid-system.md # Grid system documentation -``` - -## Enhanced UI Architecture - -### Smart Collapsible Panel System - -The right panel system provides two interaction modes: - -#### Panel States -```vue - - - -``` - -#### Quick Settings Implementation -```vue - -``` - -### Enhanced Grid System - -#### CSS Grid Implementation -```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%; -} - -.form-component { - grid-column: span 12; /* Default to full width */ - width: 100% !important; /* Force width within grid cell */ - transition: all 0.2s ease; -} -``` - -#### Grid Column Mapping -```javascript -// Precise percentage to grid column mapping -const widthToGridColumns = { - 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% -} - -const setComponentWidth = (percentage, gridColumns) => { - configModel.value.width = `${percentage}%` - configModel.value.gridColumn = `span ${gridColumns}` -} -``` - -#### Smart Grid Placement Algorithm -```javascript -// Optimal grid placement for new components -findOptimalGridPlacement() { - if (this.formComponents.length === 0) { - return { - gridColumn: 'span 12', - rowIndex: 0, - width: '100%' - } - } - - // Group components by rows and find available space - 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 - currentRowSpace = 12 - columnSpan - } - }) - - // Return optimal placement - return { - gridColumn: `span ${Math.min(currentRowSpace, 12)}`, - width: `${Math.round((currentRowSpace / 12) * 100)}%` - } -} -``` - -## Component Architecture - -### Enhanced FormBuilderFieldSettingsModal - -The modal now includes visual grid selection: - -```vue - - - -``` - -### Enhanced Canvas with Resize Handles - -```vue - - - -``` - -## State Management Enhancements - -### Enhanced Pinia Store - -```typescript -interface FormState { - formComponents: FormComponent[] - selectedComponentId: string | null - formName: string - formDescription: string - formId: string | null - formUUID: string | null - isDraggingOver: boolean - savedForms: SavedForm[] - showFieldSettingsPanel: boolean // New panel state -} - -export const useFormBuilderStore = defineStore('formBuilder', { - state: (): FormState => ({ - formComponents: [], - selectedComponentId: null, - formName: 'New Form', - formDescription: '', - formId: null, - formUUID: null, - isDraggingOver: false, - savedForms: [], - showFieldSettingsPanel: true - }), - - getters: { - selectedComponent: (state) => { - if (!state.selectedComponentId) return null - return state.formComponents.find(c => c.id === state.selectedComponentId) - }, - - formConfig: (state) => ({ - id: state.formId, - uuid: state.formUUID, - name: state.formName, - description: state.formDescription, - components: state.formComponents - }) - }, - - actions: { - // Enhanced component addition with smart grid placement - addComponent(component: FormComponent) { - const { gridColumn, rowIndex, width } = this.findOptimalGridPlacement() - - const newComponent = { - ...component, - id: component.id || uuidv4(), - props: { - ...component.defaultProps, - width: width, - gridColumn: gridColumn, - name: component.defaultProps?.name || `${component.type}_${this.formComponents.length + 1}`, - label: component.defaultProps?.label || `${component.name} ${this.formComponents.length + 1}` - } - } - - this.formComponents.push(newComponent) - this.selectComponent(newComponent.id) - }, - - // Grid layout optimization - optimizeGridLayout() { - const rows = this.groupComponentsByRows() - - rows.forEach(row => { - if (row.remainingSpace > 0) { - this.distributeRemainingSpace(row) - } - }) - }, - - // Helper methods for grid management - groupComponentsByRows() { - 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) { - currentRowSpace -= columnSpan - if (!rows[rows.length - 1]) { - rows.push({ components: [], remainingSpace: 0 }) - } - rows[rows.length - 1].components.push(component) - rows[rows.length - 1].remainingSpace = currentRowSpace - } else { - rows.push({ - components: [component], - remainingSpace: 12 - columnSpan - }) - currentRowSpace = 12 - columnSpan - } - }) - - return rows - } - } -}) -``` - -## Component Type System - -### Enhanced Component Definitions - -```javascript -const availableComponents = [ - { - type: 'text', - name: 'Text Field', - category: 'Basic Inputs', - icon: 'heroicons:document-text', - recommendedWidth: 'small', // New recommendation system - defaultProps: { - type: 'text', - label: 'Text Field', - name: '', - placeholder: '', - help: '', - validation: '', - width: '33%', // Smart default - gridColumn: 'span 4' - } - }, - { - type: 'textarea', - name: 'Text Area', - category: 'Basic Inputs', - icon: 'heroicons:document-text', - recommendedWidth: 'full', - defaultProps: { - type: 'textarea', - label: 'Text Area', - name: '', - placeholder: '', - help: '', - rows: 4, - validation: '', - width: '100%', - gridColumn: 'span 12' - } - }, - // ... additional components with smart defaults -] -``` - -### Component Icon and Name Helpers - -```javascript -const getComponentIcon = (type) => { - const iconMap = { - text: 'heroicons:document-text', - textarea: 'heroicons:document-text', - number: 'heroicons:hashtag', - email: 'heroicons:envelope', - password: 'heroicons:key', - url: 'heroicons:link', - tel: 'heroicons:device-phone-mobile', - mask: 'heroicons:pencil-square', - hidden: 'heroicons:eye-slash', - select: 'heroicons:chevron-down', - checkbox: 'heroicons:check-badge', - radio: 'heroicons:radio', - switch: 'material-symbols:toggle-on', - date: 'heroicons:calendar-days', - time: 'heroicons:clock', - 'datetime-local': 'heroicons:calendar', - range: 'heroicons:adjustments-horizontal', - color: 'heroicons:swatch', - file: 'heroicons:document-arrow-up', - otp: 'heroicons:key', - dropzone: 'heroicons:cloud-arrow-up', - button: 'heroicons:cursor-arrow-rays', - heading: 'heroicons:h1', - paragraph: 'heroicons:document-text', - divider: 'heroicons:minus', - 'info-display': 'heroicons:information-circle' - } - return iconMap[type] || 'heroicons:square-3-stack-3d' -} - -// Short names for collapsed panel -const getComponentTypeShort = (type) => { - const shortNames = { - text: 'TXT', - textarea: 'TXT', - number: 'NUM', - email: 'EML', - password: 'PWD', - url: 'URL', - tel: 'TEL', - mask: 'MSK', - hidden: 'HID', - select: 'SEL', - checkbox: 'CHK', - radio: 'RAD', - switch: 'SWT', - date: 'DTE', - time: 'TME', - 'datetime-local': 'DTM', - range: 'RNG', - color: 'CLR', - file: 'FIL', - otp: 'OTP', - dropzone: 'DRP', - button: 'BTN', - heading: 'H' + (component?.props?.level || '2'), - paragraph: 'TXT', - divider: 'DIV', - 'info-display': 'INF' - } - return shortNames[type] || 'FLD' -} -``` - -## CSS Architecture - -### Panel System Styles - -```css -/* Field Settings Panel */ -.field-settings-panel { - @apply relative bg-white border-l border-gray-200 flex flex-col overflow-hidden transition-all duration-300 ease-in-out; -} - -.panel-expanded { - @apply w-80; /* 320px expanded */ -} - -.panel-collapsed { - @apply w-12; /* 48px collapsed */ -} - -/* Panel content areas */ -.panel-content { - @apply flex-1 flex flex-col overflow-hidden; -} - -.panel-header { - @apply px-4 py-3 border-b border-gray-200 bg-gray-50; -} - -.panel-body { - @apply flex-1 overflow-y-auto; -} - -/* Collapsed state */ -.collapsed-info { - @apply h-full flex flex-col px-2 py-3; -} - -.collapsed-header { - @apply mb-4 flex justify-center; -} - -.collapsed-content { - @apply text-center w-full flex-1 flex flex-col justify-center items-center; -} -``` - -### Grid System Styles - -```css -/* Grid container */ -.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; -} - -/* Grid preview in modal */ -.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; -} - -/* Width selection */ -.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; -} -``` - -### Quick Settings Styles - -```css -/* Compact width buttons */ -.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; -} - -/* Toggle switches */ -.setting-toggle { - @apply flex items-center cursor-pointer; -} - -.toggle-slider { - @apply relative inline-block w-8 h-4 mr-2 bg-gray-200 rounded-full transition-colors duration-200 ease-in-out; -} - -.setting-toggle input:checked + .toggle-slider { - @apply bg-blue-600; -} - -.toggle-slider::before { - @apply absolute top-0.5 left-0.5 w-3 h-3 bg-white rounded-full transition-transform duration-200 ease-in-out; - content: ''; -} - -.setting-toggle input:checked + .toggle-slider::before { - @apply transform translate-x-4; -} -``` - -## API Integration - -### Form Data Structure - -```typescript -interface FormComponent { - id: string - type: string - name: string - category: string - icon: string - defaultProps: Record - props: { - label?: string - name?: string - placeholder?: string - help?: string - validation?: string - width: string // e.g., "50%" - gridColumn: string // e.g., "span 6" - [key: string]: any - } -} - -interface FormConfig { - id: string | null - uuid: string | null - name: string - description: string - components: FormComponent[] - settings: { - submitUrl?: string - successMessage?: string - errorMessage?: string - redirectUrl?: string - } -} -``` - -### Save/Load Operations - -```javascript -// Enhanced save with grid validation -const saveForm = async (formConfig) => { - // Validate grid layout - const validatedComponents = formConfig.components.map(component => ({ - ...component, - props: { - ...component.props, - // Ensure grid properties are valid - width: component.props.width || '100%', - gridColumn: component.props.gridColumn || 'span 12' - } - })) - - const validatedForm = { - ...formConfig, - components: validatedComponents - } - - try { - const response = await $fetch('/api/forms', { - method: 'POST', - body: validatedForm - }) - return response - } catch (error) { - throw new Error(`Save failed: ${error.message}`) - } -} - -// Load with grid property migration -const loadForm = async (formId) => { - try { - const form = await $fetch(`/api/forms/${formId}`) - - // Migrate old forms without grid properties - const migratedComponents = form.components.map(component => ({ - ...component, - props: { - ...component.props, - width: component.props.width || '100%', - gridColumn: component.props.gridColumn || 'span 12' - } - })) - - return { - ...form, - components: migratedComponents - } - } catch (error) { - throw new Error(`Load failed: ${error.message}`) - } -} -``` - -## Performance Optimizations - -### Component Rendering - -```vue - -``` - -### Grid Layout Optimization - -```javascript -// Efficient grid layout calculation -const optimizeGridLayout = () => { - // Use requestAnimationFrame for smooth updates - requestAnimationFrame(() => { - const rows = groupComponentsByRows() - - rows.forEach(row => { - if (row.remainingSpace > 3) { // Only optimize significant gaps - distributeSpace(row) - } - }) - }) -} - -// Virtual scrolling for large forms -const useVirtualScrolling = (components, containerHeight) => { - const itemHeight = 120 // Average component height - const visibleCount = Math.ceil(containerHeight / itemHeight) + 2 - - return computed(() => { - const startIndex = Math.max(0, scrollTop.value / itemHeight - 1) - const endIndex = Math.min(components.length, startIndex + visibleCount) - - return components.slice(startIndex, endIndex) - }) -} -``` - -## JavaScript Execution Engine (FormScriptEngine) - -### Architecture Overview - -The FormScriptEngine component provides real-time JavaScript execution for dynamic form behavior, calculations, and conditional logic. It monitors form data changes and executes user-defined event handlers to create interactive forms. - -#### Core Components -``` -components/ -├── FormScriptEngine.vue # Main execution engine -└── FormBuilderCanvas.vue # Integration point for script execution -pages/ -└── form-builder/ - └── index.vue # Form data handling and script triggering -``` - -### FormScriptEngine.vue Implementation - -```vue - - - -``` - -### Integration with Form Builder - -#### Form Builder Canvas Integration -```vue - - - - -``` - -#### Main Form Builder Integration -```vue - - - - -``` - -### Event Handler API Reference - -#### onLoad Handler -Executes once when the form loads. Use for initialization. - -```javascript -onLoad: function() { - // Initialize default values - setField('status', 'active'); - setField('created_date', new Date().toISOString().split('T')[0]); - - // Perform initial calculations - calculateInitialValues(); -} -``` - -#### onFieldChange Handler -Executes when any field value changes. - -```javascript -onFieldChange: function(fieldName, value) { - console.log('Field changed:', fieldName, 'New value:', value); - - // Handle specific field changes - switch(fieldName) { - case 'quantity': - case 'price': - updateTotal(); - break; - - case 'country': - updateTaxRate(value); - break; - } -} -``` - -### Error Handling and Debugging - -#### Script Execution Errors -```javascript -// Comprehensive error handling in script execution -const executeHandler = async (handler, ...args) => { - try { - if (typeof handler === 'function') { - const result = await handler(...args) - return { success: true, result } - } - return { success: false, error: 'Handler is not a function' } - } catch (error) { - console.error('Script execution error:', error) - - // Emit error event for debugging - emit('scriptError', { - error: error.message, - stack: error.stack, - handler: handler.name || 'anonymous', - args - }) - - return { success: false, error: error.message } - } -} -``` - -#### Debug Mode Features -```javascript -// Enhanced logging in development mode -const debugLog = (message, data) => { - if (isDev) { - console.log(`[FormScriptEngine Debug] ${message}`, data) - } -} - -// Field change tracing -const traceFieldChange = (fieldName, oldValue, newValue) => { - if (isDev) { - console.group(`Field Change: ${fieldName}`) - console.log('Old Value:', oldValue) - console.log('New Value:', newValue) - console.log('Timestamp:', new Date().toISOString()) - console.groupEnd() - } -} -``` - -### Performance Optimization - -#### Change Detection Optimization -```javascript -// Efficient change detection using shallow comparison -const hasDataChanged = (newData, oldData) => { - const newKeys = Object.keys(newData) - const oldKeys = Object.keys(oldData) - - // Quick length check - if (newKeys.length !== oldKeys.length) return true - - // Value comparison - return newKeys.some(key => newData[key] !== oldData[key]) -} - -// Debounced change detection for rapid updates -const debouncedChangeDetection = useDebounceFn( - triggerFieldChangeDetection, - 100 // 100ms debounce -) -``` - -#### Memory Management -```javascript -// Cleanup when component unmounts -onUnmounted(() => { - console.log('FormScriptEngine: Cleaning up...') - - // Clear references - handlers.value = null - scriptContext.value = null - previousFormData.value = null - - // Remove event listeners if any - cleanupEventListeners() -}) -``` - -### Security Considerations - -#### Script Sandboxing -```javascript -// Restricted execution context - only safe functions exposed -const createSecureContext = () => { - return { - // Form manipulation functions - setField, - getField, - - // Safe global functions - console: { - log: console.log, - warn: console.warn, - error: console.error - }, - - // Math and date functions - Math, - Date, - parseFloat, - parseInt, - isNaN, - - // String functions - String, - - // No access to: - // - window object - // - document object - // - eval function - // - Function constructor - // - import/require - } -} -``` - -#### Input Validation -```javascript -// Validate script before execution -const validateScript = (script) => { - const dangerousPatterns = [ - /eval\s*\(/, - /Function\s*\(/, - /window\./, - /document\./, - /import\s+/, - /require\s*\(/, - /fetch\s*\(/, - /XMLHttpRequest/ - ] - - return !dangerousPatterns.some(pattern => pattern.test(script)) -} -``` - -## Testing Strategy - -### JavaScript Engine Tests - -```javascript -// FormScriptEngine unit tests -describe('FormScriptEngine', () => { - let wrapper - let mockFormData - - beforeEach(() => { - mockFormData = ref({ - quantity: 0, - price: 0, - total: 0 - }) - - wrapper = mount(FormScriptEngine, { - props: { - formData: mockFormData.value, - script: ` - onLoad: function() { - setField('quantity', 1); - setField('price', 10); - } - - onFieldChange: function(fieldName, value) { - if (fieldName === 'quantity' || fieldName === 'price') { - const total = getField('quantity') * getField('price'); - setField('total', total); - } - } - ` - } - }) - }) - - test('should initialize script and execute onLoad', async () => { - await nextTick() - - expect(mockFormData.value.quantity).toBe(1) - expect(mockFormData.value.price).toBe(10) - }) - - test('should execute onFieldChange when data changes', async () => { - await nextTick() - - // Simulate field change - mockFormData.value.quantity = 5 - await nextTick() - - expect(mockFormData.value.total).toBe(50) - }) - - test('should handle script errors gracefully', async () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation() - - const wrapper = mount(FormScriptEngine, { - props: { - formData: mockFormData.value, - script: ` - onLoad: function() { - // Intentional error - undefinedFunction(); - } - ` - } - }) - - await nextTick() - - expect(consoleErrorSpy).toHaveBeenCalled() - consoleErrorSpy.mockRestore() - }) - - test('should validate and sanitize script input', () => { - const dangerousScript = ` - onLoad: function() { - eval('alert("XSS")'); - window.location = 'http://malicious.com'; - } - ` - - const isValid = wrapper.vm.validateScript(dangerousScript) - expect(isValid).toBe(false) - }) -}) - -// Real-time calculation tests -describe('Real-time Calculations', () => { - test('should update total when quantity changes', async () => { - const formData = ref({ quantity: 2, price: 15.99, total: 0 }) - - const wrapper = mount(TestFormWithScript, { - props: { initialData: formData.value } - }) - - // Change quantity - await wrapper.find('input[name="quantity"]').setValue('3') - await nextTick() - - expect(formData.value.total).toBe(47.97) - }) - - test('should handle edge cases in calculations', async () => { - const formData = ref({ quantity: '', price: 'invalid', total: 0 }) - - const wrapper = mount(TestFormWithScript, { - props: { initialData: formData.value } - }) - - // Should handle invalid inputs gracefully - await nextTick() - expect(formData.value.total).toBe(0) - }) -}) - -// Performance tests -describe('FormScriptEngine Performance', () => { - test('should not trigger excessive re-renders', async () => { - const executeHandlerSpy = jest.spyOn(FormScriptEngine.methods, 'executeOnFieldChange') - - const wrapper = mount(FormScriptEngine, { - props: { - formData: { field1: '', field2: '', field3: '' }, - script: ` - onFieldChange: function(fieldName, value) { - console.log('Changed:', fieldName); - } - ` - } - }) - - // Make multiple rapid changes - for (let i = 0; i < 10; i++) { - wrapper.setProps({ formData: { field1: `value${i}`, field2: '', field3: '' } }) - await nextTick() - } - - // Should be debounced/optimized - expect(executeHandlerSpy.mock.calls.length).toBeLessThan(10) - }) -}) -``` - -### Integration Tests - -```javascript -// Full form workflow tests with JavaScript -describe('Form Builder with JavaScript Integration', () => { - test('should execute calculations in preview mode', async () => { - const wrapper = mount(FormBuilder) - - // Add form components - await wrapper.find('[data-component="number"]').trigger('click') - await wrapper.find('[data-component="number"]').trigger('click') - await wrapper.find('[data-component="number"]').trigger('click') - - // Configure fields - const components = wrapper.vm.formStore.formComponents - components[0].props.name = 'quantity' - components[1].props.name = 'price' - components[2].props.name = 'total' - - // Add JavaScript - wrapper.vm.formStore.currentForm.script = ` - onFieldChange: function(fieldName, value) { - if (fieldName === 'quantity' || fieldName === 'price') { - const quantity = parseFloat(getField('quantity')) || 0; - const price = parseFloat(getField('price')) || 0; - setField('total', quantity * price); - } - } - ` - - // Switch to preview mode - await wrapper.find('[data-testid="preview-toggle"]').trigger('click') - - // Input values - await wrapper.find('input[name="quantity"]').setValue('5') - await wrapper.find('input[name="price"]').setValue('19.99') - - await nextTick() - - // Check calculated total - const totalField = wrapper.find('input[name="total"]') - expect(totalField.element.value).toBe('99.95') - }) - - test('should handle form builder to preview data flow', async () => { - const wrapper = mount(FormBuilder) - - // Build form with JavaScript - await addFormComponents(wrapper) - await addJavaScriptLogic(wrapper) - - // Test preview functionality - await wrapper.find('[data-testid="preview-button"]').trigger('click') - - // Verify script execution - expect(wrapper.find('.script-engine').exists()).toBe(true) - expect(wrapper.vm.isPreviewMode).toBe(true) - - // Test field interactions - await simulateUserInput(wrapper) - await verifyCalculations(wrapper) - }) -}) -``` - -### Unit Tests - -```javascript -// Grid system tests -describe('Grid System', () => { - test('should calculate optimal placement', () => { - const store = useFormBuilderStore() - store.formComponents = [ - { id: '1', props: { gridColumn: 'span 6' } }, - { id: '2', props: { gridColumn: 'span 4' } } - ] - - const placement = store.findOptimalGridPlacement() - expect(placement.gridColumn).toBe('span 2') - expect(placement.width).toBe('17%') // Approximately 2/12 - }) - - test('should optimize layout correctly', () => { - const store = useFormBuilderStore() - store.formComponents = [ - { id: '1', props: { gridColumn: 'span 6' } }, - { id: '2', props: { gridColumn: 'span 3' } } - ] - - store.optimizeGridLayout() - - expect(store.formComponents[1].props.gridColumn).toBe('span 6') - }) -}) - -// Panel system tests -describe('Collapsible Panel', () => { - test('should auto-open when component selected', async () => { - const wrapper = mount(FormBuilder) - const store = useFormBuilderStore() - - store.selectComponent('test-id') - await nextTick() - - expect(wrapper.find('.panel-expanded').exists()).toBe(true) - }) -}) -``` - -### Integration Tests - -```javascript -// Full workflow tests -describe('Form Builder Workflow', () => { - test('should complete full component lifecycle', async () => { - const wrapper = mount(FormBuilder) - - // Add component - await wrapper.find('[data-component="text"]').trigger('click') - - // Should auto-select and open panel - expect(wrapper.find('.panel-expanded').exists()).toBe(true) - - // Update quick settings - await wrapper.find('.width-btn[data-width="50"]').trigger('click') - - // Verify component updated - const component = wrapper.vm.formStore.selectedComponent - expect(component.props.width).toBe('50%') - expect(component.props.gridColumn).toBe('span 6') - }) -}) -``` - -## Browser Compatibility - -### Supported Features -- **CSS Grid**: Supported in all modern browsers (IE11+ with autoprefixer) -- **Vue 3**: Modern browser requirements -- **Drag & Drop**: HTML5 drag/drop with fallbacks - -### Polyfills Required -```javascript -// For older browsers -import 'core-js/stable' -import 'regenerator-runtime/runtime' - -// CSS Grid polyfill for IE11 -import 'css-grid-polyfill' -``` - -## Security Considerations - -### Input Validation -```javascript -// Sanitize component props -const sanitizeProps = (props) => { - const sanitized = {} - - Object.keys(props).forEach(key => { - if (allowedProps.includes(key)) { - sanitized[key] = sanitizeHtml(props[key]) - } - }) - - return sanitized -} - -// Validate grid values -const validateGridColumn = (gridColumn) => { - const spanMatch = gridColumn?.match(/^span\s+([1-9]|1[0-2])$/) - return spanMatch ? gridColumn : 'span 12' -} -``` - -### XSS Prevention -```javascript -// Escape user input in labels and help text -const escapeHtml = (text) => { - const div = document.createElement('div') - div.textContent = text - return div.innerHTML -} -``` - -## Future Enhancements - -### Planned Features -1. **Advanced Grid Controls**: Custom grid templates, responsive breakpoints -2. **Component Templates**: Reusable component groups -3. **Theme System**: Multiple visual themes for forms -4. **Advanced Validation**: Cross-field validation, async validation -5. **Collaboration**: Real-time multi-user editing -6. **Version Control**: Form versioning and rollback -7. **Analytics**: Form usage and completion analytics - -### Technical Debt Items -1. **TypeScript Migration**: Complete TypeScript coverage -2. **Test Coverage**: Increase to 90%+ coverage -3. **Performance**: Virtual scrolling for large forms -4. **Accessibility**: Full WCAG 2.1 AA compliance - ---- - -*This documentation reflects the current implementation including all enhanced features: smart collapsible panel system, visual grid selection, intelligent width recommendations, and comprehensive UX improvements.* - -Last updated: December 2024 \ No newline at end of file diff --git a/docs/form-builder/USER_GUIDE.md b/docs/form-builder/USER_GUIDE.md deleted file mode 100644 index 79925e3..0000000 --- a/docs/form-builder/USER_GUIDE.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/form-builder/grid-system.md b/docs/form-builder/grid-system.md deleted file mode 100644 index 4a85bab..0000000 --- a/docs/form-builder/grid-system.md +++ /dev/null @@ -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 -
-
-
-``` - -#### 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 -
- -
-``` - -### 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 -
-
-
-``` - -### 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 \ No newline at end of file diff --git a/docs/json/form/customScript.js b/docs/json/form/customScript.js index 28d3152..66a3c6e 100644 --- a/docs/json/form/customScript.js +++ b/docs/json/form/customScript.js @@ -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"); - } -})(); diff --git a/docs/json/form/formComponents.json b/docs/json/form/formComponents.json index 8ee39bd..a70ab43 100644 --- a/docs/json/form/formComponents.json +++ b/docs/json/form/formComponents.json @@ -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" } } ] diff --git a/docs/json/process-builder/processDefinition.json b/docs/json/process-builder/processDefinition.json index 086f63c..0530013 100644 --- a/docs/json/process-builder/processDefinition.json +++ b/docs/json/process-builder/processDefinition.json @@ -1,2247 +1,159 @@ { "edges": [ { - "id": "start-1751870920411-form-1752471000000-1752213918971", + "id": "start-1752546699787-form-1752546702226-1752546704356", "data": {}, "type": "custom", "label": "", - "source": "start-1751870920411", - "target": "form-1752471000000", + "source": "start-1752546699787", + "target": "form-1752546702226", "animated": true, - "sourceHandle": "start-1751870920411-right", - "targetHandle": "form-1752471000000-left" + "sourceHandle": "start-1752546699787-right", + "targetHandle": "form-1752546702226-left" }, { - "id": "form-1752471000000-api-1752471000010-1752213921952", + "id": "form-1752546702226-api-1752550319410-1752550321449", "data": {}, "type": "custom", "label": "", - "source": "form-1752471000000", - "target": "api-1752471000010", + "source": "form-1752546702226", + "target": "api-1752550319410", "animated": true, - "sourceHandle": "form-1752471000000-bottom", - "targetHandle": "api-1752471000010-top" + "sourceHandle": "form-1752546702226-right", + "targetHandle": "api-1752550319410-left" }, { - "id": "api-1752471000010-script-1752471000020-1752213928277", + "id": "api-1752550319410-script-1752550430989-1752550433556", "data": {}, "type": "custom", "label": "", - "source": "api-1752471000010", - "target": "script-1752471000020", + "source": "api-1752550319410", + "target": "script-1752550430989", "animated": true, - "sourceHandle": "api-1752471000010-right", - "targetHandle": "script-1752471000020-left" + "sourceHandle": "api-1752550319410-right", + "targetHandle": "script-1752550430989-left" }, { - "id": "script-1752471000020-form-1751870928350-1752213932854", + "id": "script-1752550430989-html-1752550500000", "data": {}, "type": "custom", "label": "", - "source": "script-1752471000020", - "target": "form-1751870928350", + "source": "script-1752550430989", + "target": "html-1752550500000", "animated": true, - "sourceHandle": "script-1752471000020-right", - "targetHandle": "form-1751870928350-left" + "sourceHandle": "script-1752550430989-right", + "targetHandle": "html-1752550500000-left" }, { - "id": "form-1751870928350-api-1751871528249-1752213937166", + "id": "html-1752550500000-end-1752546716111-1752550899936", "data": {}, "type": "custom", "label": "", - "source": "form-1751870928350", - "target": "api-1751871528249", + "source": "html-1752550500000", + "target": "end-1752546716111", "animated": true, - "sourceHandle": "form-1751870928350-bottom", - "targetHandle": "api-1751871528249-top" - }, - { - "id": "api-1751871528249-script-1751871635000-1752213939472", - "data": {}, - "type": "custom", - "label": "", - "source": "api-1751871528249", - "target": "script-1751871635000", - "animated": true, - "sourceHandle": "api-1751871528249-right", - "targetHandle": "script-1751871635000-left" - }, - { - "id": "gateway-family-tree-choice-form-kategori-asnaf-1752213977562", - "data": {}, - "type": "custom", - "label": "Tidak", - "source": "gateway-family-tree-choice", - "target": "form-kategori-asnaf", - "animated": true, - "sourceHandle": "gateway-family-tree-choice-right", - "targetHandle": "form-kategori-asnaf-top" - }, - { - "id": "form-pengesahan-api-pengesahan-submit-1752214069039", - "data": {}, - "type": "custom", - "label": "", - "source": "form-pengesahan", - "target": "api-pengesahan-submit", - "animated": true, - "sourceHandle": "form-pengesahan-bottom", - "targetHandle": "api-pengesahan-submit-left" - }, - { - "id": "api-pengesahan-submit-script-process-pengesahan-1752214088487", - "data": {}, - "type": "custom", - "label": "", - "source": "api-pengesahan-submit", - "target": "script-process-pengesahan", - "animated": true, - "sourceHandle": "api-pengesahan-submit-right", - "targetHandle": "script-process-pengesahan-left" - }, - { - "id": "script-process-pengesahan-gateway-status-check-1752214096335", - "data": {}, - "type": "custom", - "label": "", - "source": "script-process-pengesahan", - "target": "gateway-status-check", - "animated": true, - "sourceHandle": "script-process-pengesahan-bottom", - "targetHandle": "gateway-status-check-left" - }, - { - "id": "start-1751870920411-form-1753000000000-1752214106743", - "data": {}, - "type": "custom", - "label": "", - "source": "start-1751870920411", - "target": "form-1753000000000", - "animated": true, - "sourceHandle": "start-1751870920411-right", - "targetHandle": "form-1753000000000-left" - }, - { - "id": "form-1753000000000-api-1753000000001-1752214111161", - "data": {}, - "type": "custom", - "label": "", - "source": "form-1753000000000", - "target": "api-1753000000001", - "animated": true, - "sourceHandle": "form-1753000000000-bottom", - "targetHandle": "api-1753000000001-top" - }, - { - "id": "api-1753000000001-script-1753000000002-1752214115516", - "data": {}, - "type": "custom", - "label": "", - "source": "api-1753000000001", - "target": "script-1753000000002", - "animated": true, - "sourceHandle": "api-1753000000001-right", - "targetHandle": "script-1753000000002-left" - }, - { - "id": "script-1753000000002-gateway-1753100000000-1752214119151", - "data": {}, - "type": "custom", - "label": "", - "source": "script-1753000000002", - "target": "gateway-1753100000000", - "animated": true, - "sourceHandle": "script-1753000000002-right", - "targetHandle": "gateway-1753100000000-left" - }, - { - "id": "gateway-1753100000000-form-1753100000001-1752214129187", - "data": {}, - "type": "custom", - "label": "Ya", - "source": "gateway-1753100000000", - "target": "form-1753100000001", - "animated": true, - "sourceHandle": "gateway-1753100000000-right", - "targetHandle": "form-1753100000001-top" - }, - { - "id": "gateway-1753100000000-form-1751870928350-1752214132173", - "data": {}, - "type": "custom", - "label": "Tidak", - "source": "gateway-1753100000000", - "target": "form-1751870928350", - "animated": true, - "sourceHandle": "gateway-1753100000000-right", - "targetHandle": "form-1751870928350-left" - }, - { - "id": "form-1753100000001-gateway-1753100000002-1752214140594", - "data": {}, - "type": "custom", - "label": "", - "source": "form-1753100000001", - "target": "gateway-1753100000002", - "animated": true, - "sourceHandle": "form-1753100000001-right", - "targetHandle": "gateway-1753100000002-left" - }, - { - "id": "gateway-1753100000002-subprocess-1753100000003-1752214144896", - "data": {}, - "type": "custom", - "label": "Ya", - "source": "gateway-1753100000002", - "target": "subprocess-1753100000003", - "animated": true, - "sourceHandle": "gateway-1753100000002-right", - "targetHandle": "subprocess-1753100000003-left" - }, - { - "id": "gateway-1753100000002-subprocess-1752202751327-1752214146974", - "data": {}, - "type": "custom", - "label": "Tidak", - "source": "gateway-1753100000002", - "target": "subprocess-1752202751327", - "animated": true, - "sourceHandle": "gateway-1753100000002-right", - "targetHandle": "subprocess-1752202751327-left" - }, - { - "id": "gateway-status-check-subprocess-1752202751327-1752214182325", - "data": {}, - "type": "custom", - "label": "Berjaya FM", - "source": "gateway-status-check", - "target": "subprocess-1752202751327", - "animated": true, - "sourceHandle": "gateway-status-check-right", - "targetHandle": "subprocess-1752202751327-left" - }, - { - "id": "gateway-status-check-subprocess-1752208906615-1752214184716", - "data": {}, - "type": "custom", - "label": "Berjaya Non-FM", - "source": "gateway-status-check", - "target": "subprocess-1752208906615", - "animated": true, - "sourceHandle": "gateway-status-check-right", - "targetHandle": "subprocess-1752208906615-left" - }, - { - "id": "gateway-status-check-notification-1753200000001-1752214194993", - "data": {}, - "type": "custom", - "label": "Berjaya Fakir Miskin & Non-FM", - "source": "gateway-status-check", - "target": "notification-1753200000001", - "animated": true, - "sourceHandle": "gateway-status-check-right", - "targetHandle": "notification-1753200000001-left" - }, - { - "id": "gateway-status-check-notification-1753200000001-1752214200770", - "data": {}, - "type": "custom", - "label": "Gagal", - "source": "gateway-status-check", - "target": "notification-1753200000001", - "animated": true, - "sourceHandle": "gateway-status-check-right", - "targetHandle": "notification-1753200000001-left" - }, - { - "id": "script-1751871635000-api-1752114771983-1752224956615", - "data": {}, - "type": "custom", - "label": "", - "source": "script-1751871635000", - "target": "api-1752114771983", - "animated": true, - "sourceHandle": "script-1751871635000-right", - "targetHandle": "api-1752114771983-left" - }, - { - "id": "api-1752114771983-gateway-family-tree-choice-1752224958790", - "data": {}, - "type": "custom", - "label": "", - "source": "api-1752114771983", - "target": "gateway-family-tree-choice", - "animated": true, - "sourceHandle": "api-1752114771983-bottom", - "targetHandle": "gateway-family-tree-choice-left" - }, - { - "id": "gateway-family-tree-choice-html-1752109761532-1752224992397", - "data": {}, - "type": "custom", - "label": "Ya", - "source": "gateway-family-tree-choice", - "target": "html-1752109761532", - "animated": true, - "sourceHandle": "gateway-family-tree-choice-right", - "targetHandle": "html-1752109761532-left" - }, - { - "id": "form-kategori-asnaf-business-rule-1751871900000-1752225961535", - "data": {}, - "type": "custom", - "label": "", - "source": "form-kategori-asnaf", - "target": "business-rule-1751871900000", - "animated": true, - "sourceHandle": "form-kategori-asnaf-right", - "targetHandle": "business-rule-1751871900000-left" - }, - { - "id": "business-rule-1751871900000-business-rule-1751871901000-1752225963292", - "data": {}, - "type": "custom", - "label": "", - "source": "business-rule-1751871900000", - "target": "business-rule-1751871901000", - "animated": true, - "sourceHandle": "business-rule-1751871900000-right", - "targetHandle": "business-rule-1751871901000-left" - }, - { - "id": "business-rule-1751871901000-form-pengesahan-1752226611009", - "data": {}, - "type": "custom", - "label": "", - "source": "business-rule-1751871901000", - "target": "form-pengesahan", - "animated": true, - "sourceHandle": "business-rule-1751871901000-right", - "targetHandle": "form-pengesahan-top" - }, - { - "id": "business-rule-1751871901000-notification-1753200000002-1752226620873", - "data": {}, - "type": "custom", - "label": "", - "source": "business-rule-1751871901000", - "target": "notification-1753200000002", - "animated": true, - "sourceHandle": "business-rule-1751871901000-right", - "targetHandle": "notification-1753200000002-top" + "sourceHandle": "html-1752550500000-right", + "targetHandle": "end-1752546716111-left" } ], "nodes": [ { - "id": "gateway-family-tree-choice", - "data": { - "label": "Lihat Tanggungan", - "shape": "diamond", - "textColor": "#374151", - "conditions": [ - { - "id": "condition-group-ya-path", - "output": "Ya", - "conditions": [ - { - "id": "condition-1", - "value": "ya", - "operator": "eq", - "variable": "lihatFamilyTree", - "valueType": "string", - "logicalOperator": "and" - } - ] - }, - { - "id": "condition-group-tidak-path", - "output": "Tidak", - "conditions": [ - { - "id": "condition-2", - "value": "tidak", - "operator": "eq", - "variable": "lihatFamilyTree", - "valueType": "string", - "logicalOperator": "and" - } - ] - } - ], - "borderColor": "#6b7280", - "defaultPath": "gateway-fakir-miskin-choice", - "description": "Check if have dependency", - "gatewayType": "exclusive", - "backgroundColor": "#f9fafb" - }, - "type": "gateway", - "label": "Lihat Tanggungan", - "position": { "x": 2070, "y": -345 } - }, - { - "id": "form-kategori-asnaf", - "data": { - "label": "Pilihan Kategori Asnaf", - "shape": "rectangle", - "formId": 7, - "formName": "Pilihan Kategori Asnaf", - "formUuid": "7e07fc8f-a160-478a-85fd-fa3364401548", - "textColor": "#6b21a8", - "borderColor": "#9333ea", - "description": "Form untuk papar syor bantuan", - "assignedRoles": [], - "assignedUsers": [], - "inputMappings": [ - { "formField": "nama_pemohon", "processVariable": "applicantName" } - ], - "assignmentType": "public", - "outputMappings": [ - { "formField": "kategori_asnaf", "processVariable": "kategoriAsnaf" } - ], - "backgroundColor": "#faf5ff", - "fieldConditions": [] - }, - "type": "form", - "label": "Papar Syor Bantuan", - "position": { "x": 1995, "y": -30 } - }, - { - "id": "start-1751870920411", + "id": "start-1752546699787", "data": { "label": "Start", "description": "Process start point" }, "type": "start", "label": "Start", - "position": { "x": 75, "y": -510 } + "position": { "x": 270, "y": 345 } }, { - "id": "form-1752471000000", + "id": "form-1752546702226", "data": { - "label": "Penilaian Awal", - "formId": 3, - "formName": "Penilaian Awal", - "formUuid": "8e07fc8f-a160-478a-85fd-fa3364401544", - "description": "Form: Penilaian Awal untuk permohonan bantuan", - "assignedRoles": [], + "label": "Pilihan Kategori Asnaf", + "formId": 7, + "formName": "Pilihan Kategori Asnaf", + "formUuid": "d3612e05-b31a-46dc-b5e5-67e6c5bd3e78", + "description": "Form: Pilihan Kategori Asnaf", + "assignedRoles": [ + { "label": "Pemohon", "value": "2", "description": "" } + ], "assignedUsers": [], "inputMappings": [], - "assignmentType": "public", + "assignmentType": "roles", "outputMappings": [ - { - "formField": "komitmen_pembiayaan", - "processVariable": "komitmenKosTinggi" - }, - { - "formField": "keperluan_mendesak", - "processVariable": "keperluanMendesak" - }, - { - "formField": "keperluan_lain_nyatakan", - "processVariable": "keperluanLainNyatakan" - }, - { - "formField": "dokumen_berkaitan", - "processVariable": "dokumenBerkaitan" - }, - { - "formField": "catatan_tambahan", - "processVariable": "catatanTambahan" - } + { "formField": "kategori_asnaf", "processVariable": "kategoriAsnaf" } ], "fieldConditions": [], "assignmentVariable": "", "assignmentVariableType": "user_id" }, "type": "form", - "label": "Penilaian Awal", - "position": { "x": 435, "y": -540 } + "label": "Pilihan Kategori Asnaf", + "position": { "x": 540, "y": 330 } }, { - "id": "api-1752471000010", - "data": { - "label": "Submit Penilaian Awal API", - "shape": "rectangle", - "apiUrl": "https://jsonplaceholder.typicode.com/posts", - "headers": "{ \"Content-Type\": \"application/json\" }", - "apiMethod": "POST", - "textColor": "#c2410c", - "borderColor": "#f97316", - "description": "Submit penilaian awal data to external system", - "requestBody": "{\n \"komitmenKosTinggi\": \"{komitmenKosTinggi}\",\n \"keperluanMendesak\": \"{keperluanMendesak}\",\n \"keperluanLainNyatakan\": \"{keperluanLainNyatakan}\",\n \"dokumenBerkaitan\": \"{dokumenBerkaitan}\",\n \"catatanTambahan\": \"{catatanTambahan}\"\n}", - "errorVariable": "penilaianAwalApiError", - "outputVariable": "penilaianAwalApiResponse", - "backgroundColor": "#fff7ed", - "continueOnError": false - }, - "type": "api", - "label": "Submit Penilaian Awal API", - "position": { "x": 435, "y": -345 } + "id": "end-1752546716111", + "data": { "label": "End", "description": "Process end point" }, + "type": "end", + "label": "End", + "position": { "x": 1890, "y": 135 } }, { - "id": "script-1752471000020", + "id": "api-1752550319410", "data": { - "label": "Process Penilaian Awal Response", - "scriptCode": "// Extract important data from Penilaian Awal API response\nconst apiData = processVariables.penilaianAwalApiResponse;\n\nif (apiData && apiData.data) {\n // Generate a reference number for the assessment\n processVariables.penilaianAwalId = apiData.data.id || 'PA-' + Date.now();\n \n // Process the high cost commitment answer\n processVariables.hasHighCostCommitment = processVariables.komitmenKosTinggi === 'ya';\n \n // Process urgent needs\n if (Array.isArray(processVariables.keperluanMendesak)) {\n // Set flags for specific urgent needs\n processVariables.hasUrgentMedicalNeed = processVariables.keperluanMendesak.includes('perubatan_kritikal');\n processVariables.hasDisasterNeed = processVariables.keperluanMendesak.includes('bencana');\n processVariables.hasDeathRelatedNeed = processVariables.keperluanMendesak.includes('kematian');\n processVariables.hasFamilyConflict = processVariables.keperluanMendesak.includes('konflik_keluarga');\n processVariables.hasHomelessness = processVariables.keperluanMendesak.includes('tiada_tempat_tinggal');\n processVariables.hasUtilityArrears = processVariables.keperluanMendesak.includes('tunggakan_utiliti');\n processVariables.hasOtherNeeds = processVariables.keperluanMendesak.includes('lain_lain');\n processVariables.hasNoUrgentNeeds = processVariables.keperluanMendesak.includes('tidak_mendesak');\n \n // Calculate urgency score based on selected needs\n let urgencyScore = 0;\n if (processVariables.hasUrgentMedicalNeed) urgencyScore += 5;\n if (processVariables.hasDisasterNeed) urgencyScore += 5;\n if (processVariables.hasDeathRelatedNeed) urgencyScore += 4;\n if (processVariables.hasFamilyConflict) urgencyScore += 3;\n if (processVariables.hasHomelessness) urgencyScore += 5;\n if (processVariables.hasUtilityArrears) urgencyScore += 2;\n if (processVariables.hasOtherNeeds) urgencyScore += 1;\n if (processVariables.hasNoUrgentNeeds) urgencyScore = 0;\n \n processVariables.urgencyScore = urgencyScore;\n processVariables.urgencyLevel = urgencyScore >= 5 ? 'high' : (urgencyScore >= 3 ? 'medium' : 'low');\n }\n \n // Check if documents were uploaded\n processVariables.hasUploadedDocuments = processVariables.dokumenBerkaitan && \n Array.isArray(processVariables.dokumenBerkaitan) && \n processVariables.dokumenBerkaitan.length > 0;\n \n // Set status for next step\n processVariables.penilaianAwalStatus = 'completed';\n processVariables.readyForPersonalInfo = true;\n \n console.log('Penilaian Awal processed successfully:', {\n penilaianAwalId: processVariables.penilaianAwalId,\n urgencyLevel: processVariables.urgencyLevel,\n urgencyScore: processVariables.urgencyScore,\n hasHighCostCommitment: processVariables.hasHighCostCommitment,\n hasUploadedDocuments: processVariables.hasUploadedDocuments\n });\n} else {\n // Handle API error case\n processVariables.penilaianAwalStatus = 'failed';\n processVariables.readyForPersonalInfo = false;\n processVariables.penilaianAwalError = 'Failed to submit penilaian awal';\n}", - "description": "Process the penilaian awal form data and API response", - "errorVariable": "penilaianAwalScriptError", - "inputVariables": [ - "penilaianAwalApiResponse", - "komitmenKosTinggi", - "keperluanMendesak", - "keperluanLainNyatakan", - "dokumenBerkaitan", - "catatanTambahan" - ], - "scriptLanguage": "javascript", - "continueOnError": false, - "outputVariables": [ - { - "name": "penilaianAwalId", - "type": "string", - "description": "Generated ID for the initial assessment" - }, - { - "name": "hasHighCostCommitment", - "type": "boolean", - "description": "Whether applicant has high cost commitments" - }, - { - "name": "urgencyScore", - "type": "number", - "description": "Calculated urgency score based on needs" - }, - { - "name": "urgencyLevel", - "type": "string", - "description": "Urgency level (high/medium/low)" - }, - { - "name": "hasUrgentMedicalNeed", - "type": "boolean", - "description": "Whether applicant has urgent medical needs" - }, - { - "name": "hasDisasterNeed", - "type": "boolean", - "description": "Whether applicant has disaster-related needs" - }, - { - "name": "hasDeathRelatedNeed", - "type": "boolean", - "description": "Whether applicant has death-related needs" - }, - { - "name": "hasFamilyConflict", - "type": "boolean", - "description": "Whether applicant has family conflict" - }, - { - "name": "hasHomelessness", - "type": "boolean", - "description": "Whether applicant is homeless" - }, - { - "name": "hasUtilityArrears", - "type": "boolean", - "description": "Whether applicant has utility arrears" - }, - { - "name": "hasOtherNeeds", - "type": "boolean", - "description": "Whether applicant has other needs" - }, - { - "name": "hasNoUrgentNeeds", - "type": "boolean", - "description": "Whether applicant has no urgent needs" - }, - { - "name": "hasUploadedDocuments", - "type": "boolean", - "description": "Whether documents were uploaded" - }, - { - "name": "penilaianAwalStatus", - "type": "string", - "description": "Status of initial assessment submission" - }, - { - "name": "readyForPersonalInfo", - "type": "boolean", - "description": "Whether ready for personal info form" - } - ] - }, - "type": "script", - "label": "Process Penilaian Awal Response", - "position": { "x": 780, "y": -345 } - }, - { - "id": "form-1751870928350", - "data": { - "label": "Borang Maklumat Peribadi", - "formId": 1, - "formName": "Borang Maklumat Peribadi", - "formUuid": "9f08fc8f-b170-478a-85fd-fa3364401533", - "description": "Form: Borang Maklumat Peribadi", - "assignedRoles": [], - "assignedUsers": [], - "inputMappings": [], - "assignmentType": "public", - "outputMappings": [ - { "formField": "text_3", "processVariable": "text3" }, - { "formField": "select_1", "processVariable": "select1" }, - { "formField": "form_jeniskp_1", "processVariable": "formJeniskp1" }, - { "formField": "form_jeniskp_2", "processVariable": "formJeniskp2" }, - { "formField": "form_jeniskp_3", "processVariable": "formJeniskp3" }, - { - "formField": "text_warganegara", - "processVariable": "textWarganegara" - }, - { "formField": "radio_jantina", "processVariable": "radioJantina" }, - { "formField": "radio_bangsa", "processVariable": "radioBangsa" }, - { "formField": "nyatakan_lain2", "processVariable": "nyatakanLain2" }, - { "formField": "radio_9_copy", "processVariable": "radio9Copy" }, - { - "formField": "radio_pendidikan", - "processVariable": "radioPendidikan" - }, - { "formField": "text_14", "processVariable": "text14" }, - { - "formField": "date_masukislam", - "processVariable": "dateMasukislam" - }, - { - "formField": "date_masukislam_copy", - "processVariable": "dateMasukislamCopy" - }, - { - "formField": "select_statusperkahwinan", - "processVariable": "selectStatusperkahwinan" - }, - { - "formField": "hubungan_keluarga", - "processVariable": "hubunganKeluarga" - }, - { - "formField": "hubungan_lain_nyatakan", - "processVariable": "hubunganLainNyatakan" - }, - { - "formField": "nama_tanggungan", - "processVariable": "namaTanggungan" - }, - { - "formField": "jenis_kad_tanggungan", - "processVariable": "jenisKadTanggungan" - }, - { - "formField": "no_pengenalan_tanggungan", - "processVariable": "noPengenalanTanggungan" - }, - { - "formField": "jantina_tanggungan", - "processVariable": "jantinaTanggungan" - }, - { - "formField": "tarikh_lahir_tanggungan", - "processVariable": "tarikhLahirTanggungan" - }, - { - "formField": "tempat_lahir_tanggungan", - "processVariable": "tempatLahirTanggungan" - }, - { - "formField": "bangsa_tanggungan", - "processVariable": "bangsaTanggungan" - }, - { - "formField": "bangsa_lain_tanggungan", - "processVariable": "bangsaLainTanggungan" - }, - { - "formField": "status_kahwin_tanggungan", - "processVariable": "statusKahwinTanggungan" - }, - { - "formField": "tarikh_masuk_islam_tanggungan", - "processVariable": "tarikhMasukIslamTanggungan" - }, - { - "formField": "tarikh_mula_kfam_tanggungan", - "processVariable": "tarikhMulaKfamTanggungan" - }, - { - "formField": "warganegara_tanggungan", - "processVariable": "warganegaraTanggungan" - }, - { - "formField": "tempat_menetap_tanggungan", - "processVariable": "tempatMenetapTanggungan" - }, - { - "formField": "no_telefon_tanggungan", - "processVariable": "noTelefonTanggungan" - }, - { - "formField": "nama_pemegang_akaun", - "processVariable": "namaPemegangAkaun" - }, - { "formField": "nama_bank", "processVariable": "namaBank" }, - { "formField": "no_akaun_bank", "processVariable": "noAkaunBank" }, - { - "formField": "cara_pembayaran", - "processVariable": "caraPembayaran" - }, - { "formField": "sebab_tunai", "processVariable": "sebabTunai" }, - { - "formField": "bersekolah_tanggungan", - "processVariable": "bersekolahTanggungan" - }, - { - "formField": "pendidikan_tertinggi_tanggungan", - "processVariable": "pendidikanTertinggiTanggungan" - }, - { - "formField": "pendidikan_lain_tanggungan", - "processVariable": "pendidikanLainTanggungan" - }, - { "formField": "nama_sekolah", "processVariable": "namaSekolah" }, - { "formField": "alamat_sekolah", "processVariable": "alamatSekolah" }, - { "formField": "daerah_sekolah", "processVariable": "daerahSekolah" }, - { "formField": "negeri_sekolah", "processVariable": "negeriSekolah" }, - { "formField": "poskod_sekolah", "processVariable": "poskodSekolah" }, - { - "formField": "tinggal_bersama_keluarga", - "processVariable": "tinggalBersamaKeluarga" - } - ], - "fieldConditions": [], - "assignmentVariable": "", - "assignmentVariableType": "user_id" - }, - "type": "form", - "label": "Borang Maklumat Peribadi", - "position": { "x": 1260, "y": -540 } - }, - { - "id": "api-1751871528249", - "data": { - "label": "Submit Profile API", - "shape": "rectangle", - "apiUrl": "https://jsonplaceholder.typicode.com/posts", - "headers": "{ \"Content-Type\": \"application/json\" }", - "apiMethod": "POST", - "textColor": "#c2410c", - "borderColor": "#f97316", - "description": "Submit user profile to external system", - "requestBody": "{\n \"applicantName\": \"{text3}\",\n \"idType\": \"{select1}\",\n \"mykadNumber\": \"{formJeniskp1}\",\n \"passportNumber\": \"{formJeniskp2}\",\n \"birthCertNumber\": \"{formJeniskp3}\",\n \"nationality\": \"{textWarganegara}\",\n \"gender\": \"{radioJantina}\",\n \"race\": \"{radioBangsa}\",\n \"otherRace\": \"{nyatakanLain2}\",\n \"isStudying\": \"{radio9Copy}\",\n \"education\": \"{radioPendidikan}\",\n \"otherEducation\": \"{text14}\",\n \"islamConversionDate\": \"{dateMasukislam}\",\n \"kfamStartDate\": \"{dateMasukislamCopy}\",\n \"maritalStatus\": \"{selectStatusperkahwinan}\",\n \"dependentInfo\": {\n \"relationship\": \"{hubunganKeluarga}\",\n \"otherRelationship\": \"{hubunganLainNyatakan}\",\n \"name\": \"{namaTanggungan}\",\n \"idType\": \"{jenisKadTanggungan}\",\n \"idNumber\": \"{noPengenalanTanggungan}\",\n \"gender\": \"{jantinaTanggungan}\",\n \"birthDate\": \"{tarikhLahirTanggungan}\",\n \"birthPlace\": \"{tempatLahirTanggungan}\",\n \"race\": \"{bangsaTanggungan}\",\n \"otherRace\": \"{bangsaLainTanggungan}\",\n \"maritalStatus\": \"{statusKahwinTanggungan}\",\n \"islamConversionDate\": \"{tarikhMasukIslamTanggungan}\",\n \"kfamStartDate\": \"{tarikhMulaKfamTanggungan}\",\n \"nationality\": \"{warganegaraTanggungan}\",\n \"residenceDuration\": \"{tempatMenetapTanggungan}\",\n \"phoneNumber\": \"{noTelefonTanggungan}\",\n \"isStudying\": \"{bersekolahTanggungan}\",\n \"education\": \"{pendidikanTertinggiTanggungan}\",\n \"otherEducation\": \"{pendidikanLainTanggungan}\",\n \"schoolName\": \"{namaSekolah}\",\n \"schoolAddress\": \"{alamatSekolah}\",\n \"schoolDistrict\": \"{daerahSekolah}\",\n \"schoolState\": \"{negeriSekolah}\",\n \"schoolPostcode\": \"{poskodSekolah}\",\n \"liveWithFamily\": \"{tinggalBersamaKeluarga}\"\n },\n \"bankingInfo\": {\n \"accountHolderName\": \"{namaPemegangAkaun}\",\n \"bankName\": \"{namaBank}\",\n \"accountNumber\": \"{noAkaunBank}\",\n \"paymentMethod\": \"{caraPembayaran}\",\n \"cashReason\": \"{sebabTunai}\"\n }\n}", - "errorVariable": "apiError", - "outputVariable": "apiResponse", - "backgroundColor": "#fff7ed", - "continueOnError": false - }, - "type": "api", - "label": "Submit Profile API", - "position": { "x": 1260, "y": -345 } - }, - { - "id": "script-1751871635000", - "data": { - "label": "Process API Response", - "scriptCode": "// Extract important data from API response\nconst apiData = processVariables.apiResponse;\n\nif (apiData && apiData.data) {\n // Extract application ID\n processVariables.applicationId = apiData.data.id || 'APP-' + Date.now();\n \n // Determine if documents are required based on profile\n processVariables.documentsRequired = true;\n \n // Set verification level based on education and other factors\n if (processVariables.radioPendidikan === 'ijazah' || processVariables.radioPendidikan === 'diploma') {\n processVariables.verificationLevel = 'enhanced';\n processVariables.documentsRequired = true;\n } else {\n processVariables.verificationLevel = 'standard';\n }\n \n // Extract applicant name for verification form\n processVariables.applicantName = processVariables.text3;\n \n // Process dependent information if available\n if (processVariables.namaTanggungan) {\n processVariables.hasDependents = true;\n processVariables.dependentCount = 1;\n processVariables.dependentName = processVariables.namaTanggungan;\n } else {\n processVariables.hasDependents = false;\n processVariables.dependentCount = 0;\n }\n \n // Process banking information\n if (processVariables.namaBank && processVariables.noAkaunBank) {\n processVariables.hasBankingInfo = true;\n processVariables.paymentReady = true;\n } else {\n processVariables.hasBankingInfo = false;\n processVariables.paymentReady = false;\n }\n \n // Set status for next step\n processVariables.profileSubmissionStatus = 'success';\n processVariables.nextStepReady = true;\n \n console.log('Profile processed successfully:', {\n applicationId: processVariables.applicationId,\n verificationLevel: processVariables.verificationLevel,\n documentsRequired: processVariables.documentsRequired,\n hasDependents: processVariables.hasDependents,\n hasBankingInfo: processVariables.hasBankingInfo\n });\n} else {\n // Handle API error case\n processVariables.profileSubmissionStatus = 'failed';\n processVariables.nextStepReady = false;\n processVariables.errorMessage = 'Failed to submit profile';\n}", - "description": "Transform API response for document verification step", - "errorVariable": "scriptError", - "inputVariables": [ - "apiResponse", - "text3", - "radioPendidikan", - "namaTanggungan", - "namaBank", - "noAkaunBank" - ], - "scriptLanguage": "javascript", - "continueOnError": false, - "outputVariables": [ - { - "name": "applicationId", - "type": "string", - "description": "Generated application ID" - }, - { - "name": "documentsRequired", - "type": "boolean", - "description": "Whether documents verification is required" - }, - { - "name": "verificationLevel", - "type": "string", - "description": "Level of verification required" - }, - { - "name": "applicantName", - "type": "string", - "description": "Applicant name for verification" - }, - { - "name": "hasDependents", - "type": "boolean", - "description": "Whether applicant has dependents" - }, - { - "name": "dependentCount", - "type": "number", - "description": "Number of dependents" - }, - { - "name": "dependentName", - "type": "string", - "description": "Name of dependent" - }, - { - "name": "hasBankingInfo", - "type": "boolean", - "description": "Whether banking information is provided" - }, - { - "name": "paymentReady", - "type": "boolean", - "description": "Whether payment processing is ready" - }, - { - "name": "profileSubmissionStatus", - "type": "string", - "description": "Status of profile submission" - }, - { - "name": "nextStepReady", - "type": "boolean", - "description": "Whether ready for next step" - } - ] - }, - "type": "script", - "label": "Process API Response", - "position": { "x": 1590, "y": -345 } - }, - { - "id": "business-rule-1751871900000", - "data": { - "label": "BF-NAS-PRF-AS-QS-03", - "shape": "rectangle", - "priority": "medium", - "textColor": "#1e40af", - "ruleGroups": [ - { - "id": "group-1", - "name": "Ketua Keluarga Base Amount", - "actions": [ - { - "id": "action-1-1", - "type": "set_variable", - "value": 1215, - "variable": "baseKifayahKetuaKeluarga" - }, - { - "id": "action-1-2", - "type": "set_variable", - "value": "ketua_keluarga", - "variable": "categoryKetuaKeluarga" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-1-1", - "value": "", - "operator": "neq", - "variable": "noKadPengenalan", - "valueType": "string" - } - ] - }, - { - "id": "group-2", - "name": "Dewasa Bekerja (18 Tahun Ke Atas)", - "actions": [ - { - "id": "action-2-1", - "type": "set_variable", - "value": 412, - "variable": "hadKifayahDewasaBekerja" - }, - { - "id": "action-2-2", - "type": "increment", - "formula": "hadKifayahDewasaBekerja * bilanganDewasaBekerja", - "variable": "totalHadKifayahDewasaBekerja" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-2-1", - "value": 0, - "operator": "gt", - "variable": "bilanganDewasaBekerja" - } - ] - }, - { - "id": "group-3", - "name": "Dewasa Tidak Bekerja (18 Tahun Ke Atas)", - "actions": [ - { - "id": "action-3-1", - "type": "set_variable", - "value": 167, - "variable": "hadKifayahDewasaTidakBekerja" - }, - { - "id": "action-3-2", - "type": "increment", - "formula": "hadKifayahDewasaTidakBekerja * bilanganDewasaTidakBekerja", - "variable": "totalHadKifayahDewasaTidakBekerja" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-3-1", - "value": 0, - "operator": "gt", - "variable": "bilanganDewasaTidakBekerja" - } - ] - }, - { - "id": "group-4", - "name": "Tanggungan Belajar IPT", - "actions": [ - { - "id": "action-4-1", - "type": "set_variable", - "value": 613, - "variable": "hadKifayahTanggunganIPT" - }, - { - "id": "action-4-2", - "type": "increment", - "formula": "hadKifayahTanggunganIPT * bilanganTanggunganIPT", - "variable": "totalHadKifayahTanggunganIPT" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-4-1", - "value": 0, - "operator": "gt", - "variable": "bilanganTanggunganIPT" - } - ] - }, - { - "id": "group-5", - "name": "Tanggungan Berumur 7-17 tahun", - "actions": [ - { - "id": "action-5-1", - "type": "set_variable", - "value": 408, - "variable": "hadKifayahTanggungan7to17" - }, - { - "id": "action-5-2", - "type": "increment", - "formula": "hadKifayahTanggungan7to17 * bilanganTanggungan7to17", - "variable": "totalHadKifayahTanggungan7to17" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-5-1", - "value": 0, - "operator": "gt", - "variable": "bilanganTanggungan7to17" - } - ] - }, - { - "id": "group-6", - "name": "Tanggungan 6 Tahun Ke Bawah", - "actions": [ - { - "id": "action-6-1", - "type": "set_variable", - "value": 175, - "variable": "hadKifayahTanggungan6KeBawah" - }, - { - "id": "action-6-2", - "type": "increment", - "formula": "hadKifayahTanggungan6KeBawah * bilanganTanggungan6KeBawah", - "variable": "totalHadKifayahTanggungan6KeBawah" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-6-1", - "value": 0, - "operator": "gt", - "variable": "bilanganTanggungan6KeBawah" - } - ] - }, - { - "id": "group-7", - "name": "Calculate Total Had Kifayah", - "actions": [ - { - "id": "action-7-1", - "type": "calculation", - "formula": "baseKifayahKetuaKeluarga + (totalHadKifayahDewasaBekerja || 0) + (totalHadKifayahDewasaTidakBekerja || 0) + (totalHadKifayahTanggunganIPT || 0) + (totalHadKifayahTanggungan7to17 || 0) + (totalHadKifayahTanggungan6KeBawah || 0)", - "variable": "totalJumlahHadKifayah" - }, - { - "id": "action-7-2", - "type": "calculation", - "formula": "((pendapatanBersih || 0) / totalJumlahHadKifayah) * 100", - "variable": "peratusHadKifayah" - }, - { - "id": "action-7-3", - "type": "set_variable", - "value": "completed", - "variable": "kifayahCalculationStatus" - } - ], - "operator": "AND", - "conditions": [ - { - "id": "condition-7-1", - "value": "", - "operator": "neq", - "variable": "noKadPengenalan" - } - ] - } - ], - "borderColor": "#3b82f6", - "description": " Analisa Data (Had Kifayah)", - "errorVariable": "kifayahCalculationError", - "outputVariable": "kifayahCalculationResult", - "backgroundColor": "#eff6ff" - }, - "type": "business-rule", - "label": "BF-NAS-PRF-AS-QS-03", - "position": { "x": 2250, "y": -30 } - }, - { - "id": "business-rule-1751871901000", - "data": { - "label": "BF-NAS-PRF-AS-QS-04", - "shape": "rectangle", - "priority": "medium", - "textColor": "#1e40af", - "ruleGroups": [ - { - "name": "Rule 1", - "actions": [ - { - "type": "set_variable", - "value": "Fakir", - "variable": "kategoriKeluargaAsnaf" - }, - { - "type": "set_variable", - "value": "Fakir", - "variable": "kategoriAsnafSyor" - }, - { - "type": "set_variable", - "value": "Fakir", - "variable": "statusKeluarga" - } - ], - "conditions": [ - { - "value": "0", - "operator": "gte", - "variable": "peratusHadKifayah" - }, - { - "value": "49.9", - "operator": "lte", - "variable": "peratusHadKifayah" - } - ], - "conditionType": "all" - }, - { - "name": "Rule 2", - "actions": [ - { - "type": "set_variable", - "value": "Miskin", - "variable": "kategoriKeluargaAsnaf" - }, - { - "type": "set_variable", - "value": "Miskin", - "variable": "kategoriAsnafSyor" - }, - { - "type": "set_variable", - "value": "Miskin", - "variable": "statusKeluarga" - } - ], - "conditions": [ - { - "value": "50", - "operator": "gte", - "variable": "peratusHadKifayah" - }, - { - "value": "100", - "operator": "lte", - "variable": "peratusHadKifayah" - } - ], - "conditionType": "all" - }, - { - "name": "Rule 3", - "actions": [ - { - "type": "set_variable", - "value": "Non-FM", - "variable": "kategoriKeluargaAsnaf" - }, - { - "type": "set_variable", - "value": "Non-FM", - "variable": "kategoriAsnafSyor" - }, - { - "type": "set_variable", - "value": "Non-FM", - "variable": "statusKeluarga" - } - ], - "conditions": [ - { - "value": "100", - "operator": "gt", - "variable": "peratusHadKifayah" - } - ], - "conditionType": "all" - }, - { - "name": "Rule 4", - "actions": [ - { - "type": "set_variable", - "value": "Muallaf", - "variable": "kategoriAsnafSyor" - }, - { - "type": "set_variable", - "value": "Muallaf", - "variable": "statusIndividu" - } - ], - "conditions": [ - { "value": "5", "operator": "lt", "variable": "tempohMasukIslam" } - ], - "conditionType": "all" - }, - { - "name": "Rule 5", - "actions": [ - { - "type": "set_variable", - "value": "Generated", - "variable": "syorPengesahanStatus" - }, - { - "type": "set_variable", - "value": "${new Date().toISOString()}", - "variable": "tarikhPengesyoran" - }, - { - "type": "set_variable", - "value": "Berdasarkan analisis had kifayah, pemohon dikategorikan sebagai ${kategoriAsnafSyor} dengan peratus had kifayah ${peratusHadKifayah.toFixed(2)}%. Jumlah had kifayah keluarga: RM ${totalJumlahHadKifayah.toFixed(2)}.", - "variable": "syorPengesahan" - } - ], - "conditions": [ - { - "value": "completed", - "operator": "eq", - "variable": "kifayahCalculationStatus" - } - ], - "conditionType": "all" - } - ], - "borderColor": "#3b82f6", - "description": " Syor Status Keluarga Asnaf", - "errorVariable": "statusRecommendationError", - "outputVariable": "statusRecommendationResult", - "backgroundColor": "#eff6ff" - }, - "type": "business-rule", - "label": "BF-NAS-PRF-AS-QS-04", - "position": { "x": 2550, "y": -30 } - }, - { - "id": "html-1752109761532", - "data": { - "label": "Family Tree", - "shape": "rectangle", - "jsCode": "", - "cssCode": "", - "htmlCode": "\n
\n

Custom HTML Content

\n

This is a custom HTML node that can display rich content.

\n
", - "textColor": "#333333", - "autoRefresh": false, - "borderColor": "#dddddd", - "description": "Family Tree for Borang", - "inputVariables": [], - "backgroundColor": "#ffffff", - "outputVariables": [], - "allowVariableAccess": true - }, - "type": "html", - "label": "Family Tree", - "position": { "x": 2535, "y": -345 } - }, - { - "id": "rectangle-shape-1752110224921", - "data": { - "label": "", - "shape": "rectangle", - "width": 650, - "height": 400, - "zIndex": 1, - "isShape": true, - "shapeType": "rectangle", - "textColor": "#374151", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" - }, - "type": "rectangle-shape", - "label": "", - "position": { "x": 375, "y": -570 } - }, - { - "id": "text-annotation-1752110279700", - "data": { - "label": "NF-NAS-PRF-AS-PA", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Pernilaian Awal", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-PA", - "position": { "x": 810, "y": -555 } - }, - { - "id": "rectangle-shape-1752110492897", - "data": { - "label": "", - "shape": "rectangle", - "width": 650, - "height": 400, - "zIndex": 1, - "isShape": true, - "shapeType": "rectangle", - "textColor": "#374151", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" - }, - "type": "rectangle-shape", - "label": "", - "position": { "x": 1185, "y": -570 } - }, - { - "id": "text-annotation-1752110562983", - "data": { - "label": "BF-NAS-PRF-AS-QS-02", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Isi Borang Permohonan Online", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-QS-02", - "position": { "x": 1620, "y": -555 } - }, - { - "id": "rectangle-shape-1752114739551", - "data": { - "label": "", - "shape": "rectangle", - "width": 750, - "height": 400, - "zIndex": 1, - "isShape": true, - "shapeType": "rectangle", - "textColor": "#374151", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" - }, - "type": "rectangle-shape", - "label": "", - "position": { "x": 1995, "y": -570 } - }, - { - "id": "api-1752114771983", - "data": { - "label": "Called Family Tree", - "shape": "rectangle", - "apiUrl": "", + "label": "API Call", + "apiUrl": "https://jsonplaceholder.typicode.com/todos/1", "headers": "{ \"Content-Type\": \"application/json\" }", "apiMethod": "GET", - "textColor": "#c2410c", - "borderColor": "#f97316", "description": "External API call", "requestBody": "", "errorVariable": "apiError", "outputVariable": "apiResponse", - "backgroundColor": "#fff7ed", "continueOnError": false }, "type": "api", - "label": "Called Family Tree", - "position": { "x": 2040, "y": -540 } + "label": "API Call", + "position": { "x": 855, "y": 345 } }, { - "id": "text-annotation-1752114833800", + "id": "script-1752550430989", "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Family Tree", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-FM", - "position": { "x": 2520, "y": -555 } - }, - { - "id": "form-1753000000000", - "data": { - "label": "Carian Profil", - "formId": 4, - "formName": "Carian Profil", - "formUuid": "4e07fc8f-a160-478a-85fd-fa3364401545", - "description": "Skrin carian asnaf atau login", - "assignedRoles": [], - "assignedUsers": [], - "inputMappings": [], - "assignmentType": "public", - "outputMappings": [ - { "formField": "search_type", "processVariable": "carianSearchType" }, - { "formField": "search_id", "processVariable": "carianSearchId" }, - { "formField": "login_id", "processVariable": "carianLoginId" }, - { - "formField": "login_password", - "processVariable": "carianLoginPassword" - } - ], - "fieldConditions": [], - "assignmentVariable": "", - "assignmentVariableType": "user_id" - }, - "type": "form", - "label": "Carian Profil", - "position": { "x": 450, "y": -15 } - }, - { - "id": "api-1753000000001", - "data": { - "label": "Submit Carian Profil API", - "shape": "rectangle", - "apiUrl": "https://api.example.com/profiles/search", - "headers": "{ \"Content-Type\": \"application/json\" }", - "apiMethod": "POST", - "textColor": "#c2410c", - "borderColor": "#f97316", - "description": "Submit profile search or login credentials", - "requestBody": "{\n \"searchType\": \"{carianSearchType}\",\n \"searchId\": \"{carianSearchId}\",\n \"loginId\": \"{carianLoginId}\",\n \"password\": \"{carianLoginPassword}\"\n}", - "errorVariable": "carianProfilApiError", - "outputVariable": "carianProfilApiResponse", - "backgroundColor": "#fff7ed", - "continueOnError": false - }, - "type": "api", - "label": "Submit Carian Profil API", - "position": { "x": 450, "y": 180 } - }, - { - "id": "script-1753000000002", - "data": { - "label": "Process Carian Profil Response", - "scriptCode": "// Process API response from profile search/login\nconst response = processVariables.carianProfilApiResponse;\n\nif (response && response.data) {\n if (response.data.loginSuccess) {\n processVariables.loginSuccess = true;\n processVariables.profileData = response.data.profile;\n processVariables.carianProfilStatus = 'login_successful';\n } else if (response.data.profileFound) {\n processVariables.profileFound = true;\n processVariables.profileData = response.data.profile;\n processVariables.carianProfilStatus = 'profile_found';\n } else {\n processVariables.profileFound = false;\n processVariables.loginSuccess = false;\n processVariables.carianProfilStatus = 'not_found';\n }\n} else {\n processVariables.carianProfilStatus = 'error';\n processVariables.carianProfilScriptError = 'Invalid or empty API response';\n}", - "description": "Process the response from the Carian Profil API", - "errorVariable": "carianProfilScriptError", - "inputVariables": ["carianProfilApiResponse"], + "label": "Script Task", + "scriptCode": "// Assign API response title to process variable\nprocessVariables.todoTitle = processVariables.apiResponse?.title || '';\n// You can add more logic here\n", + "description": "Execute JavaScript code", + "inputVariables": ["apiResponse"], "scriptLanguage": "javascript", - "continueOnError": false, "outputVariables": [ { - "name": "profileFound", - "type": "boolean", - "description": "Indicates if a profile was found via search" - }, - { - "name": "loginSuccess", - "type": "boolean", - "description": "Indicates if the asnaf login was successful" - }, - { - "name": "profileData", - "type": "object", - "description": "The retrieved profile data" - }, - { - "name": "carianProfilStatus", + "name": "todoTitle", "type": "string", - "description": "The status of the profile search/login action" + "description": "Title from API response" } ] }, "type": "script", - "label": "Process Carian Profil Response", - "position": { "x": 780, "y": 180 } + "label": "Script Task", + "position": { "x": 1200, "y": 330 } }, { - "id": "rectangle-shape-1752115136908", + "id": "html-1752550500000", "data": { - "label": "", - "shape": "rectangle", - "width": 650, - "height": 400, - "isShape": true, - "shapeType": "rectangle", - "textColor": "#374151", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" + "label": "Show Result", + "jsCode": "", + "cssCode": ".result-card { background: #f9fafb; border: 1px solid #ddd; border-radius: 8px; padding: 16px; max-width: 400px; margin: 24px auto; }", + "htmlCode": "
\n

API Result

\n

Todo Title: {{ processVariables.todoTitle }}

\n
", + "autoRefresh": true, + "description": "Display the todo title from API", + "inputVariables": ["todoTitle"], + "outputVariables": [], + "allowVariableAccess": true }, - "type": "rectangle-shape", - "label": "", - "position": { "x": 375, "y": -45 } - }, - { - "id": "text-annotation-1752115184991", - "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Carian Profil", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-QS-01", - "position": { "x": 810, "y": -30 } - }, - { - "id": "gateway-1753100000000", - "data": { - "label": "Profil Wujud?", - "shape": "diamond", - "textColor": "#374151", - "conditions": [ - { - "id": "condition-group-ya-path", - "output": "Ya", - "conditions": [ - { - "id": "condition-1", - "value": true, - "operator": "eq", - "variable": "profileFound", - "valueType": "boolean", - "logicalOperator": "and" - } - ] - }, - { - "id": "condition-group-tidak-path", - "output": "Tidak", - "conditions": [ - { - "id": "condition-2", - "value": false, - "operator": "eq", - "variable": "profileFound", - "valueType": "boolean", - "logicalOperator": "and" - } - ] - } - ], - "borderColor": "#6b7280", - "defaultPath": "form-1752471000000", - "description": "Check if profile exists in system", - "gatewayType": "exclusive", - "backgroundColor": "#f9fafb" - }, - "type": "gateway", - "label": "Profil Wujud?", - "position": { "x": 1230, "y": 0 } - }, - { - "id": "form-1753100000001", - "data": { - "label": "Pengesahan Kemaskini Profil", - "formId": 5, - "formName": "Pengesahan Kemaskini Profil", - "formUuid": "5e07fc8f-a160-478a-85fd-fa3364401546", - "description": "Borang pengesahan untuk kemaskini profil sedia ada", - "assignedRoles": [], - "assignedUsers": [], - "inputMappings": [ - { - "formField": "nama_pemohon", - "processVariable": "profileData.nama" - }, - { - "formField": "no_kad_pengenalan", - "processVariable": "profileData.noKadPengenalan" - }, - { - "formField": "tarikh_lahir", - "processVariable": "profileData.tarikhLahir" - }, - { "formField": "alamat", "processVariable": "profileData.alamat" } - ], - "assignmentType": "public", - "outputMappings": [ - { - "formField": "kemaskini_profil", - "processVariable": "kemaskiniProfil" - }, - { - "formField": "sebab_kemaskini", - "processVariable": "sebabKemaskini" - } - ], - "fieldConditions": [], - "assignmentVariable": "", - "assignmentVariableType": "user_id" - }, - "type": "form", - "label": "Pengesahan Kemaskini Profil", - "position": { "x": 1200, "y": 195 } - }, - { - "id": "gateway-1753100000002", - "data": { - "label": "Perubahan Profil?", - "shape": "diamond", - "textColor": "#374151", - "conditions": [ - { - "id": "condition-group-ya-path", - "output": "Ya", - "conditions": [ - { - "id": "condition-1", - "value": "ya", - "operator": "eq", - "variable": "kemaskiniProfil", - "valueType": "string", - "logicalOperator": "and" - } - ] - }, - { - "id": "condition-group-tidak-path", - "output": "Tidak", - "conditions": [ - { - "id": "condition-2", - "value": "tidak", - "operator": "eq", - "variable": "kemaskiniProfil", - "valueType": "string", - "logicalOperator": "and" - } - ] - } - ], - "borderColor": "#6b7280", - "defaultPath": "form-1752471000000", - "description": "Check if user wants to update profile", - "gatewayType": "exclusive", - "backgroundColor": "#f9fafb" - }, - "type": "gateway", - "label": "Perubahan Profil?", - "position": { "x": 1425, "y": 60 } - }, - { - "id": "subprocess-1753100000003", - "data": { - "label": "BF-NAS-PRF-AS-UP-02", - "shape": "rectangle", - "processId": "kemaskini-profil-process", - "textColor": "#134e4a", - "borderColor": "#14b8a6", - "description": "Sub Process: Kemaskini Profil", - "subprocessId": 3, - "inputMappings": [ - { - "processVariable": "profileData", - "subprocessVariable": "profileData" - }, - { - "processVariable": "sebabKemaskini", - "subprocessVariable": "sebabKemaskini" - } - ], - "outputMappings": [ - { - "processVariable": "updatedProfileData", - "subprocessVariable": "profileData" - }, - { - "processVariable": "profileUpdateStatus", - "subprocessVariable": "updateStatus" - } - ], - "subprocessName": "BF-NAS-PRF-AS-UP-02", - "backgroundColor": "#f0fdfa" - }, - "type": "subprocess", - "label": "BF-NAS-PRF-AS-UP-02", - "position": { "x": 1635, "y": -30 } - }, - { - "id": "subprocess-1752202751327", - "data": { - "label": "BF-NAS-BTN-PB", - "shape": "rectangle", - "textColor": "#134e4a", - "borderColor": "#14b8a6", - "description": "Sub Process: Bantuan", - "subprocessId": 4, - "subprocessName": "BF-NAS-BTN-PB", - "backgroundColor": "#f0fdfa" - }, - "type": "subprocess", - "label": "BF-NAS-BTN-PB", - "position": { "x": 1635, "y": 180 } - }, - { - "id": "notification-1753200000001", - "data": { - "label": "Terima Notifikasi", - "message": "Assalamualaikum {applicantName},\n\nPermohonan bantuan anda telah diterima dan sedang dalam proses semakan.\n\nNo Rujukan: {applicationId}\nTarikh Permohonan: {submissionDate}\nStatus: Dalam Proses\n\nAnda akan menerima notifikasi lanjut apabila terdapat kemaskini status permohonan.\n\nTerima kasih.", - "subject": "Permohonan Bantuan Diterima - {applicationId}", - "priority": "normal", - "template": "application_received", - "variables": [ - "applicantName", - "applicationId", - "submissionDate", - "applicantEmail" - ], - "recipients": [ - { "type": "variable", "value": "applicantEmail" }, - { "type": "role", "value": "admin" } - ], - "attachments": [], - "description": "Send notification when application is received", - "sendImmediately": true, - "notificationType": "email" - }, - "type": "notification", - "label": "Terima Notifikasi", - "position": { "x": 3180, "y": -435 } - }, - { - "id": "gateway-status-check", - "data": { - "label": "Status", - "shape": "diamond", - "textColor": "#374151", - "conditions": [ - { - "id": "condition-group-berjaya-fm-path", - "output": "Berjaya FM", - "conditions": [ - { - "id": "condition-1", - "value": "approved", - "operator": "eq", - "variable": "applicationStatus", - "valueType": "string", - "logicalOperator": "and" - }, - { - "id": "condition-2", - "value": "Fakir", - "operator": "eq", - "variable": "kategoriAsnafSyor", - "valueType": "string", - "logicalOperator": "and" - } - ] - }, - { - "id": "condition-group-berjaya-miskin-path", - "output": "Berjaya Non-FM", - "conditions": [ - { - "id": "condition-3", - "value": "Non-Fakir", - "operator": "eq", - "variable": "applicationStatus", - "valueType": "string", - "logicalOperator": "and" - }, - { - "id": "condition-4", - "value": "Miskin", - "operator": "eq", - "variable": "kategoriAsnafSyor", - "valueType": "string", - "logicalOperator": "and" - } - ] - }, - { - "id": "condition-group-berjaya-non-fm-path", - "output": "Berjaya Fakir Miskin & Non-FM", - "conditions": [ - { - "id": "condition-5", - "value": "approved", - "operator": "eq", - "variable": "applicationStatus", - "valueType": "string", - "logicalOperator": "and" - }, - { - "id": "condition-6", - "value": "Fakir", - "operator": "eq", - "variable": "kategoriAsnafSyor", - "valueType": "string", - "logicalOperator": "and" - }, - { - "id": "condition-1752209204119", - "value": "Non-Fakir", - "maxValue": "", - "minValue": "", - "operator": "eq", - "variable": "kategoriAsnaf", - "valueType": "string", - "logicalOperator": "or" - } - ] - }, - { - "id": "condition-group-gagal-path", - "output": "Gagal", - "conditions": [ - { - "id": "condition-7", - "value": "rejected", - "operator": "eq", - "variable": "applicationStatus", - "valueType": "string", - "logicalOperator": "and" - } - ] - } - ], - "borderColor": "#6b7280", - "defaultPath": "notification-1753200000002", - "description": "Check application status and asnaf category for routing", - "gatewayType": "exclusive", - "backgroundColor": "#f9fafb" - }, - "type": "gateway", - "label": "Status", - "position": { "x": 1635, "y": 765 } - }, - { - "id": "form-pengesahan", - "data": { - "label": "Pengesahan", - "shape": "rectangle", - "formId": 8, - "formName": "Borang Pengesahan", - "formUuid": "8e07fc8f-a160-478a-85fd-fa3364401549", - "textColor": "#6b21a8", - "borderColor": "#9333ea", - "description": "Form untuk pengesahan dokumen dan maklumat pemohon", - "assignedRoles": ["verifier", "admin"], - "assignedUsers": [], - "inputMappings": [ - { "formField": "nama_pemohon", "processVariable": "applicantName" }, - { "formField": "no_rujukan", "processVariable": "applicationId" }, - { "formField": "kategori_asnaf", "processVariable": "kategoriAsnaf" }, - { - "formField": "dokumen_berkaitan", - "processVariable": "dokumenBerkaitan" - }, - { "formField": "urgency_level", "processVariable": "urgencyLevel" } - ], - "assignmentType": "role", - "outputMappings": [ - { - "formField": "status_pengesahan", - "processVariable": "pengesahanStatus" - }, - { - "formField": "catatan_pengesahan", - "processVariable": "catatanPengesahan" - }, - { - "formField": "dokumen_tambahan_diperlukan", - "processVariable": "dokumenTambahanDiperlukan" - }, - { - "formField": "jumlah_bantuan_dicadangkan", - "processVariable": "jumlahBantuanDicadangkan" - }, - { "formField": "jenis_bantuan", "processVariable": "jenisBantuan" }, - { "formField": "verified_by", "processVariable": "verifiedBy" }, - { - "formField": "verification_date", - "processVariable": "verificationDate" - } - ], - "backgroundColor": "#faf5ff", - "fieldConditions": [], - "assignmentVariable": "", - "assignmentVariableType": "user_id" - }, - "type": "form", - "label": "Borang Pengesahan", - "position": { "x": 2505, "y": 705 } - }, - { - "id": "api-pengesahan-submit", - "data": { - "label": "Submit Pengesahan API", - "shape": "rectangle", - "apiUrl": "https://api.bantuan.gov.my/v1/verification/submit", - "headers": "{ \"Content-Type\": \"application/json\", \"Authorization\": \"Bearer {apiToken}\" }", - "apiMethod": "POST", - "textColor": "#c2410c", - "borderColor": "#f97316", - "description": "Submit verification results to central system", - "requestBody": "{\n \"applicationId\": \"{applicationId}\",\n \"verificationStatus\": \"{pengesahanStatus}\",\n \"verificationNotes\": \"{catatanPengesahan}\",\n \"additionalDocumentsRequired\": \"{dokumenTambahanDiperlukan}\",\n \"recommendedAmount\": \"{jumlahBantuanDicadangkan}\",\n \"assistanceType\": \"{jenisBantuan}\",\n \"verifiedBy\": \"{verifiedBy}\",\n \"verificationDate\": \"{verificationDate}\",\n \"applicantCategory\": \"{kategoriAsnaf}\",\n \"urgencyLevel\": \"{urgencyLevel}\"\n}", - "errorVariable": "pengesahanApiError", - "outputVariable": "pengesahanApiResponse", - "backgroundColor": "#fff7ed", - "continueOnError": false - }, - "type": "api", - "label": "Submit Pengesahan API", - "position": { "x": 2175, "y": 720 } - }, - { - "id": "script-process-pengesahan", - "data": { - "label": "Process Pengesahan Response", - "scriptCode": "// Process the pengesahan (verification) response\nconst apiData = processVariables.pengesahanApiResponse;\n\nif (apiData && apiData.status === 'success') {\n // Update application status based on verification\n switch (processVariables.pengesahanStatus) {\n case 'lulus':\n processVariables.applicationStatus = 'approved';\n processVariables.statusMessage = 'Permohonan telah diluluskan';\n processVariables.nextAction = 'payment_processing';\n break;\n case 'ditolak':\n processVariables.applicationStatus = 'rejected';\n processVariables.statusMessage = 'Permohonan ditolak: ' + processVariables.catatanPengesahan;\n processVariables.nextAction = 'send_rejection_notice';\n break;\n case 'dokumen_tambahan':\n processVariables.applicationStatus = 'pending_documents';\n processVariables.statusMessage = 'Dokumen tambahan diperlukan';\n processVariables.nextAction = 'request_additional_documents';\n break;\n case 'semakan_lanjut':\n processVariables.applicationStatus = 'under_review';\n processVariables.statusMessage = 'Permohonan dalam semakan lanjut';\n processVariables.nextAction = 'further_review';\n break;\n default:\n processVariables.applicationStatus = 'pending';\n processVariables.statusMessage = 'Status tidak diketahui';\n processVariables.nextAction = 'manual_review';\n }\n \n // Set verification completion flag\n processVariables.verificationCompleted = true;\n processVariables.verificationTimestamp = new Date().toISOString();\n \n // Calculate processing time\n if (processVariables.submissionDate) {\n const submitTime = new Date(processVariables.submissionDate);\n const verifyTime = new Date();\n const processingHours = Math.round((verifyTime - submitTime) / (1000 * 60 * 60));\n processVariables.processingTimeHours = processingHours;\n }\n \n // Set notification flags based on status\n processVariables.sendApprovalNotification = processVariables.applicationStatus === 'approved';\n processVariables.sendRejectionNotification = processVariables.applicationStatus === 'rejected';\n processVariables.sendPendingNotification = processVariables.applicationStatus.includes('pending');\n \n // Set payment processing flag for approved applications\n if (processVariables.applicationStatus === 'approved') {\n processVariables.paymentReady = true;\n processVariables.paymentAmount = processVariables.jumlahBantuanDicadangkan || 0;\n processVariables.paymentMethod = processVariables.sebabTunai === 'ya' ? 'cash' : 'bank_transfer';\n }\n \n console.log('Pengesahan processed successfully:', {\n applicationStatus: processVariables.applicationStatus,\n statusMessage: processVariables.statusMessage,\n nextAction: processVariables.nextAction,\n verificationCompleted: processVariables.verificationCompleted,\n paymentReady: processVariables.paymentReady\n });\n} else {\n // Handle API error\n processVariables.applicationStatus = 'verification_failed';\n processVariables.statusMessage = 'Ralat semasa pengesahan';\n processVariables.verificationCompleted = false;\n processVariables.pengesahanError = apiData ? apiData.message : 'Unknown API error';\n \n console.error('Pengesahan API failed:', processVariables.pengesahanError);\n}", - "description": "Process verification results and determine next steps", - "errorVariable": "pengesahanScriptError", - "inputVariables": [ - "pengesahanApiResponse", - "pengesahanStatus", - "catatanPengesahan", - "dokumenTambahanDiperlukan", - "jumlahBantuanDicadangkan", - "jenisBantuan", - "verifiedBy", - "verificationDate", - "submissionDate", - "sebabTunai" - ], - "scriptLanguage": "javascript", - "continueOnError": false, - "outputVariables": [ - { - "name": "applicationStatus", - "type": "string", - "description": "Final application status after verification" - }, - { - "name": "statusMessage", - "type": "string", - "description": "Human-readable status message" - }, - { - "name": "nextAction", - "type": "string", - "description": "Next action to be taken" - }, - { - "name": "verificationCompleted", - "type": "boolean", - "description": "Whether verification process is completed" - }, - { - "name": "verificationTimestamp", - "type": "string", - "description": "Timestamp when verification was completed" - }, - { - "name": "processingTimeHours", - "type": "number", - "description": "Total processing time in hours" - }, - { - "name": "sendApprovalNotification", - "type": "boolean", - "description": "Whether to send approval notification" - }, - { - "name": "sendRejectionNotification", - "type": "boolean", - "description": "Whether to send rejection notification" - }, - { - "name": "sendPendingNotification", - "type": "boolean", - "description": "Whether to send pending status notification" - }, - { - "name": "paymentReady", - "type": "boolean", - "description": "Whether payment processing is ready" - }, - { - "name": "paymentAmount", - "type": "number", - "description": "Amount approved for payment" - }, - { - "name": "paymentMethod", - "type": "string", - "description": "Payment method (cash/bank_transfer)" - } - ] - }, - "type": "script", - "label": "Process Pengesahan Response", - "position": { "x": 2175, "y": 510 } - }, - { - "id": "notification-1753200000002", - "data": { - "label": "BF-NAS-PRF-AS-QS-06 Terima Notifikasi", - "shape": "rectangle", - "message": "Assalamualaikum {applicantName},\n\nStatus permohonan bantuan anda telah dikemaskini.\n\nNo Rujukan: {applicationId}\nStatus Terkini: {statusMessage}\nTarikh Kemaskini: {verificationTimestamp}\nDiluluskan oleh: {verifiedBy}\n\n{conditionalMessage}\n\nUntuk maklumat lanjut, sila hubungi pejabat kami.\n\nTerima kasih.", - "subject": "Kemaskini Status Permohonan - {applicationId}", - "priority": "high", - "template": "status_update", - "textColor": "#0284c7", - "variables": [ - "applicantName", - "applicationId", - "statusMessage", - "verificationTimestamp", - "verifiedBy", - "conditionalMessage" - ], - "recipients": [{ "type": "variable", "value": "applicantEmail" }], - "attachments": [], - "borderColor": "#0ea5e9", - "description": "Send status update notification to applicant", - "backgroundColor": "#f0f9ff", - "sendImmediately": true, - "notificationType": "email", - "conditionalSending": { - "conditions": [ - { - "value": true, - "operator": "eq", - "variable": "verificationCompleted", - "valueType": "boolean" - } - ] - } - }, - "type": "notification", - "label": "Terima Notifikasi", - "position": { "x": 3165, "y": 630 } - }, - { - "id": "swimlane-horizontal-1752207659712", - "data": { - "label": "", - "shape": "rectangle", - "width": 650, - "height": 400, - "zIndex": 1, - "isShape": true, - "shapeType": "swimlane-horizontal", - "textColor": "#374151", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" - }, - "type": "swimlane-horizontal", - "label": "", - "position": { "x": 2940, "y": -570 } - }, - { - "id": "text-annotation-1752207727571", - "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Terima Notifikasi", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-QS-06", - "position": { "x": 3360, "y": -540 } - }, - { - "id": "swimlane-horizontal-1752207945135", - "data": { - "label": "", - "shape": "rectangle", - "width": 650, - "height": 400, - "zIndex": 0, - "isShape": true, - "shapeType": "swimlane-horizontal", - "textColor": "#374151", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" - }, - "type": "swimlane-horizontal", - "label": "", - "position": { "x": 2100, "y": 495 } - }, - { - "id": "swimlane-horizontal-1752208028711", - "data": { - "label": "", - "shape": "rectangle", - "width": 650, - "height": 400, - "isShape": true, - "shapeType": "swimlane-horizontal", - "textColor": "#475569", - "borderColor": "#16a34a", - "description": "", - "backgroundColor": "#e8f5e9" - }, - "type": "swimlane-horizontal", - "label": "", - "position": { "x": 2925, "y": 495 } - }, - { - "id": "subprocess-1752208906615", - "data": { - "label": "Bantuan Selain Fakir, Miskin", - "shape": "rectangle", - "textColor": "#134e4a", - "borderColor": "#14b8a6", - "description": "Sub-process: Bantuan Selain Fakir, Miskin", - "subprocessId": 5, - "subprocessName": "Bantuan Selain Fakir, Miskin", - "backgroundColor": "#f0fdfa" - }, - "type": "subprocess", - "label": "Bantuan Selain Fakir, Miskin", - "position": { "x": 2010, "y": 225 } - }, - { - "id": "text-annotation-1752214226967", - "data": { - "label": "BF-NAS-PRF-AS-QS-06", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Terima Notifikasi", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-QS-06", - "position": { "x": 3360, "y": 510 } - }, - { - "id": "text-annotation-1752214239218", - "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "Pengesahan", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "BF-NAS-PRF-AS-QS-05", - "position": { "x": 2535, "y": 510 } - }, - { - "id": "swimlane-horizontal-1752459527902", - "data": { - "label": "", - "shape": "rectangle", - "width": 3800, - "height": 600, - "zIndex": 0, - "isShape": true, - "shapeType": "swimlane-horizontal", - "textColor": "#475569", - "borderColor": "#cedced", - "description": "", - "backgroundColor": "#edf0f3" - }, - "type": "swimlane-horizontal", - "label": "", - "position": { "x": -165, "y": -690 } - }, - { - "id": "swimlane-horizontal-1752459624822", - "data": { - "label": "", - "shape": "rectangle", - "width": 3800, - "height": 600, - "isShape": true, - "shapeType": "swimlane-horizontal", - "textColor": "#475569", - "borderColor": "#cedced", - "description": "", - "backgroundColor": "#edf0f3" - }, - "type": "swimlane-horizontal", - "label": "", - "position": { "x": -165, "y": -90 } - }, - { - "id": "swimlane-horizontal-1752459677022", - "data": { - "label": "", - "shape": "rectangle", - "width": 3800, - "height": 600, - "isShape": true, - "shapeType": "swimlane-horizontal", - "textColor": "#475569", - "borderColor": "#cedced", - "description": "", - "backgroundColor": "#edf0f3" - }, - "type": "swimlane-horizontal", - "label": "", - "position": { "x": -165, "y": 390 } - }, - { - "id": "text-annotation-1752459743051", - "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "Pendaftar/Pemohon", - "position": { "x": -135, "y": -660 } - }, - { - "id": "text-annotation-1752459783975", - "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "NAS", - "position": { "x": -135, "y": -60 } - }, - { - "id": "text-annotation-1752459793219", - "data": { - "label": "", - "shape": "rectangle", - "width": 200, - "height": 80, - "isShape": true, - "shapeType": "text-annotation", - "textColor": "#92400e", - "borderColor": "#fbbf24", - "description": "", - "backgroundColor": "#fffbeb" - }, - "type": "text-annotation", - "label": "EOAD", - "position": { "x": -120, "y": 435 } + "type": "html", + "label": "Show Result", + "position": { "x": 1560, "y": 105 } } ], "viewport": { - "x": 380.3737299152659, - "y": 485.7223789581114, - "zoom": 0.3414482349217966 + "x": -660.4278622401489, + "y": 202.3364877443377, + "zoom": 0.8153893887682282 } } diff --git a/docs/json/process-builder/processVariables.json b/docs/json/process-builder/processVariables.json index 0458f8f..b59e613 100644 --- a/docs/json/process-builder/processVariables.json +++ b/docs/json/process-builder/processVariables.json @@ -1,1384 +1,28 @@ { - "text3": { - "name": "text3", - "type": "string", - "scope": "global", - "value": null, - "description": "Text field 3 from Section A" - }, - "text14": { - "name": "text14", - "type": "string", - "scope": "global", - "value": null, - "description": "Text field 14" - }, - "select1": { - "name": "select1", - "type": "string", - "scope": "global", - "value": null, - "description": "Select field 1 from Section A" - }, "apiError": { "name": "apiError", "type": "object", "scope": "global", "value": null, - "description": "API error from Submit Profile API" - }, - "apiToken": { - "name": "apiToken", - "type": "string", - "scope": "global", - "value": "your-api-token-here", - "description": "API authentication token for external services" - }, - "namaBank": { - "name": "namaBank", - "type": "string", - "scope": "global", - "value": null, - "description": "Nama bank from Section B" - }, - "nextAction": { - "name": "nextAction", - "type": "string", - "scope": "global", - "value": null, - "description": "Next action to be taken in the process" - }, - "radio9Copy": { - "name": "radio9Copy", - "type": "string", - "scope": "global", - "value": null, - "description": "Radio field 9 copy" - }, - "sebabTunai": { - "name": "sebabTunai", - "type": "string", - "scope": "global", - "value": null, - "description": "Sebab tunai from Section B" - }, - "verifiedBy": { - "name": "verifiedBy", - "type": "string", - "scope": "global", - "value": null, - "description": "User ID or name of person who verified documents" + "description": "API error from API Call" }, "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 API" - }, - "namaSekolah": { - "name": "namaSekolah", - "type": "string", - "scope": "global", "value": null, - "description": "Nama sekolah from Section B" - }, - "noAkaunBank": { - "name": "noAkaunBank", - "type": "string", - "scope": "global", - "value": null, - "description": "No akaun bank from Section B" - }, - "profileData": { - "name": "profileData", - "type": "object", - "scope": "global", - "value": { - "nama": "Ahmad Bin Ali", - "alamat": "No 123, Jalan Merdeka, 50000 Kuala Lumpur", - "tarikhLahir": "1985-01-01", - "noKadPengenalan": "850101-01-1234" - }, - "description": "The retrieved profile data" - }, - "radioBangsa": { - "name": "radioBangsa", - "type": "string", - "scope": "global", - "value": null, - "description": "Radio field for bangsa" - }, - "scriptError": { - "name": "scriptError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error information from script execution" - }, - "errorMessage": { - "name": "errorMessage", - "type": "string", - "scope": "global", - "value": null, - "description": "Error message from failed operations" - }, - "formJeniskp1": { - "name": "formJeniskp1", - "type": "string", - "scope": "global", - "value": null, - "description": "Form field for jenis KP 1" - }, - "formJeniskp2": { - "name": "formJeniskp2", - "type": "string", - "scope": "global", - "value": null, - "description": "Form field for jenis KP 2" - }, - "formJeniskp3": { - "name": "formJeniskp3", - "type": "string", - "scope": "global", - "value": null, - "description": "Form field for jenis KP 3" - }, - "jenisBantuan": { - "name": "jenisBantuan", - "type": "string", - "scope": "global", - "value": null, - "description": "Type of assistance (tunai/barangan/perkhidmatan)" - }, - "loginSuccess": { - "name": "loginSuccess", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Indicates if the asnaf login was successful" - }, - "okuAllowance": { - "name": "okuAllowance", - "type": "number", - "scope": "global", - "value": null, - "description": "OKU (disabled person) allowance (RM 247.00)" - }, - "paymentReady": { - "name": "paymentReady", - "type": "boolean", - "scope": "global", - "value": true, - "description": "Whether payment processing is ready" - }, - "profileFound": { - "name": "profileFound", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Indicates if a profile was found via search" - }, - "profileScore": { - "name": "profileScore", - "type": "number", - "scope": "global", - "value": 85, - "description": "Profile score from initial API response" - }, - "radioJantina": { - "name": "radioJantina", - "type": "string", - "scope": "global", - "value": null, - "description": "Radio field for jantina" - }, - "urgencyLevel": { - "name": "urgencyLevel", - "type": "string", - "scope": "global", - "value": null, - "description": "Urgency level (high/medium/low)" - }, - "urgencyScore": { - "name": "urgencyScore", - "type": "number", - "scope": "global", - "value": 0, - "description": "Calculated urgency score based on needs" - }, - "alamatSekolah": { - "name": "alamatSekolah", - "type": "string", - "scope": "global", - "value": null, - "description": "Alamat sekolah from Section B" - }, - "applicantName": { - "name": "applicantName", - "type": "string", - "scope": "global", - "value": null, - "description": "Applicant's full name" - }, - "applicationId": { - "name": "applicationId", - "type": "string", - "scope": "global", - "value": null, - "description": "Unique application reference number" - }, - "carianLoginId": { - "name": "carianLoginId", - "type": "string", - "scope": "global", - "value": null, - "description": "Asnaf ID for login" - }, - "daerahSekolah": { - "name": "daerahSekolah", - "type": "string", - "scope": "global", - "value": null, - "description": "Daerah sekolah from Section B" - }, - "dependentName": { - "name": "dependentName", - "type": "string", - "scope": "global", - "value": "Siti Aminah Binti Ahmad", - "description": "Name of dependent" - }, - "dependentType": { - "name": "dependentType", - "type": "string", - "scope": "global", - "value": null, - "description": "Type of dependent (school_age_7_17, below_6, etc.)" - }, - "hasDependents": { - "name": "hasDependents", - "type": "boolean", - "scope": "global", - "value": true, - "description": "Whether applicant has dependents" - }, - "hasOtherNeeds": { - "name": "hasOtherNeeds", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has other needs" - }, - "householdType": { - "name": "householdType", - "type": "string", - "scope": "global", - "value": null, - "description": "Type of household (married_with_dependents, single_with_dependents, etc.)" + "description": "API response from API Call" }, "kategoriAsnaf": { "name": "kategoriAsnaf", "type": "string", "scope": "global", - "value": null, - "description": "Category of asnaf (fakir_miskin/bukan_fakir_miskin)" + "description": "" }, - "kifayahStatus": { - "name": "kifayahStatus", + "todoTitle": { + "name": "todoTitle", "type": "string", "scope": "global", - "value": null, - "description": "Status of Had Kifayah calculation (calculated, pending, error)" - }, - "negeriSekolah": { - "name": "negeriSekolah", - "type": "string", - "scope": "global", - "value": null, - "description": "Negeri sekolah from Section B" - }, - "nextStepReady": { - "name": "nextStepReady", - "type": "boolean", - "scope": "global", - "value": true, - "description": "Whether ready for next step in process" - }, - "nyatakanLain2": { - "name": "nyatakanLain2", - "type": "string", - "scope": "global", - "value": null, - "description": "Text field for nyatakan lain-lain" - }, - "paymentAmount": { - "name": "paymentAmount", - "type": "number", - "scope": "global", - "value": null, - "description": "Amount approved for payment (RM)" - }, - "paymentMethod": { - "name": "paymentMethod", - "type": "string", - "scope": "global", - "value": null, - "description": "Payment method (cash/bank_transfer)" - }, - "poskodSekolah": { - "name": "poskodSekolah", - "type": "string", - "scope": "global", - "value": null, - "description": "Poskod sekolah from Section B" - }, - "statusMessage": { - "name": "statusMessage", - "type": "string", - "scope": "global", - "value": null, - "description": "Human-readable status message" - }, - "applicantEmail": { - "name": "applicantEmail", - "type": "string", - "scope": "global", - "value": null, - "description": "Applicant's email address for notifications" - }, - "caraPembayaran": { - "name": "caraPembayaran", - "type": "string", - "scope": "global", - "value": null, - "description": "Cara pembayaran from Section B" - }, - "carianSearchId": { - "name": "carianSearchId", - "type": "string", - "scope": "global", - "value": null, - "description": "IC or Foreign ID used for profile search" - }, - "dateMasukislam": { - "name": "dateMasukislam", - "type": "string", - "scope": "global", - "value": null, - "description": "Date field for masuk Islam" - }, - "dependentCount": { - "name": "dependentCount", - "type": "number", - "scope": "global", - "value": 1, - "description": "Number of dependents" - }, - "hasBankingInfo": { - "name": "hasBankingInfo", - "type": "boolean", - "scope": "global", - "value": true, - "description": "Whether banking information is provided" - }, - "namaTanggungan": { - "name": "namaTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Nama tanggungan from Section B" - }, - "riskAssessment": { - "name": "riskAssessment", - "type": "string", - "scope": "global", - "value": null, - "description": "Risk assessment result from verification" - }, - "sebabKemaskini": { - "name": "sebabKemaskini", - "type": "string", - "scope": "global", - "value": null, - "description": "Reason for updating profile" - }, - "statusIndividu": { - "name": "statusIndividu", - "type": "string", - "scope": "global", - "value": null, - "description": "Individual status (e.g., Muallaf)" - }, - "statusKeluarga": { - "name": "statusKeluarga", - "type": "string", - "scope": "global", - "value": null, - "description": "Family status based on had kifayah calculation" - }, - "submissionDate": { - "name": "submissionDate", - "type": "string", - "scope": "global", - "value": null, - "description": "Date when application was submitted" - }, - "syorPengesahan": { - "name": "syorPengesahan", - "type": "string", - "scope": "global", - "value": null, - "description": "AI-generated recommendation summary" - }, - "catatanTambahan": { - "name": "catatanTambahan", - "type": "string", - "scope": "global", - "value": null, - "description": "Catatan tambahan berkaitan permohonan" - }, - "hasDisasterNeed": { - "name": "hasDisasterNeed", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has disaster-related needs" - }, - "hasHomelessness": { - "name": "hasHomelessness", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant is homeless" - }, - "kemaskiniProfil": { - "name": "kemaskiniProfil", - "type": "string", - "scope": "global", - "value": null, - "description": "User's decision on whether to update profile (ya/tidak)" - }, - "lihatFamilyTree": { - "name": "lihatFamilyTree", - "type": "string", - "scope": "global", - "value": null, - "description": "User's choice whether to view family tree (ya/tidak)" - }, - "noKadPengenalan": { - "name": "noKadPengenalan", - "type": "string", - "scope": "global", - "value": "850101-01-1234", - "description": "No Kad Pengenalan" - }, - "pengesahanError": { - "name": "pengesahanError", - "type": "string", - "scope": "global", - "value": null, - "description": "General error message from pengesahan process" - }, - "penilaianAwalId": { - "name": "penilaianAwalId", - "type": "string", - "scope": "global", - "value": null, - "description": "Generated ID for the initial assessment" - }, - "radioPendidikan": { - "name": "radioPendidikan", - "type": "string", - "scope": "global", - "value": null, - "description": "Radio field for pendidikan" - }, - "rejectionReason": { - "name": "rejectionReason", - "type": "string", - "scope": "global", - "value": null, - "description": "Reason for rejection if documents incomplete" - }, - "textWarganegara": { - "name": "textWarganegara", - "type": "string", - "scope": "global", - "value": null, - "description": "Text field for warganegara" - }, - "totalHadKifayah": { - "name": "totalHadKifayah", - "type": "number", - "scope": "global", - "value": null, - "description": "Total calculated Had Kifayah amount" - }, - "bangsaTanggungan": { - "name": "bangsaTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Bangsa tanggungan from Section B" - }, - "carianSearchType": { - "name": "carianSearchType", - "type": "string", - "scope": "global", - "value": null, - "description": "Type of search for Carian Profil (e.g., 'ic_search', 'login')" - }, - "dokumenBerkaitan": { - "name": "dokumenBerkaitan", - "type": "array", - "scope": "global", - "value": null, - "description": "Upload dokumen yang berkaitan" - }, - "eligibilityScore": { - "name": "eligibilityScore", - "type": "number", - "scope": "global", - "value": null, - "description": "Eligibility score from verification API" - }, - "hasNoUrgentNeeds": { - "name": "hasNoUrgentNeeds", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has no urgent needs" - }, - "hubunganKeluarga": { - "name": "hubunganKeluarga", - "type": "string", - "scope": "global", - "value": null, - "description": "Hubungan keluarga from Section B" - }, - "pendapatanBersih": { - "name": "pendapatanBersih", - "type": "number", - "scope": "global", - "value": 1000, - "description": "Net family income" - }, - "pengesahanStatus": { - "name": "pengesahanStatus", - "type": "string", - "scope": "global", - "value": null, - "description": "Verification status (lulus/ditolak/dokumen_tambahan/semakan_lanjut)" - }, - "readyForDecision": { - "name": "readyForDecision", - "type": "boolean", - "scope": "global", - "value": null, - "description": "Whether ready for decision gateway" - }, - "tempohMasukIslam": { - "name": "tempohMasukIslam", - "type": "number", - "scope": "global", - "value": null, - "description": "Duration since converting to Islam (in years)" - }, - "verificationDate": { - "name": "verificationDate", - "type": "string", - "scope": "global", - "value": null, - "description": "Date when verification was completed" - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "string", - "scope": "global", - "value": "pending", - "description": "Current application status (pending/approved/rejected/under_review)" - }, - "baseKifayahAmount": { - "name": "baseKifayahAmount", - "type": "decimal", - "scope": "global", - "value": null, - "description": "Base Had Kifayah amount for head of household (RM 1,215.00)" - }, - "catatanPengesahan": { - "name": "catatanPengesahan", - "type": "string", - "scope": "global", - "value": null, - "description": "Verification notes from officer" - }, - "documentsComplete": { - "name": "documentsComplete", - "type": "boolean", - "scope": "global", - "value": null, - "description": "Whether all documents are complete" - }, - "documentsRequired": { - "name": "documentsRequired", - "type": "boolean", - "scope": "global", - "value": true, - "description": "Whether documents verification is required" - }, - "hasFamilyConflict": { - "name": "hasFamilyConflict", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has family conflict" - }, - "hasUtilityArrears": { - "name": "hasUtilityArrears", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has utility arrears" - }, - "jantinaTanggungan": { - "name": "jantinaTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Jantina tanggungan from Section B" - }, - "kategoriAsnafSyor": { - "name": "kategoriAsnafSyor", - "type": "string", - "scope": "global", - "value": null, - "description": "Recommended asnaf category" - }, - "keperluanMendesak": { - "name": "keperluanMendesak", - "type": "array", - "scope": "global", - "value": null, - "description": "Apakah keperluan tuan/puan mendesak sekarang ini?" - }, - "komitmenKosTinggi": { - "name": "komitmenKosTinggi", - "type": "string", - "scope": "global", - "value": null, - "description": "Adakah tuan/puan mempunyai komitmen dan pembiayaan melibatkan kos yang tinggi?" - }, - "namaPemegangAkaun": { - "name": "namaPemegangAkaun", - "type": "string", - "scope": "global", - "value": null, - "description": "Nama pemegang akaun from Section B" - }, - "notificationError": { - "name": "notificationError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from notification sending process" - }, - "peratusHadKifayah": { - "name": "peratusHadKifayah", - "type": "number", - "scope": "global", - "value": 0, - "description": "Percentage of had kifayah (income/total had kifayah * 100)" - }, - "tarikhPengesyoran": { - "name": "tarikhPengesyoran", - "type": "string", - "scope": "global", - "value": null, - "description": "Date of recommendation generation" - }, - "verificationError": { - "name": "verificationError", - "type": "string", - "scope": "global", - "value": null, - "description": "Error message from verification processing" - }, - "verificationLevel": { - "name": "verificationLevel", - "type": "string", - "scope": "global", - "value": "enhanced", - "description": "Level of verification required (standard/enhanced)" - }, - "verificationNotes": { - "name": "verificationNotes", - "type": "string", - "scope": "global", - "value": null, - "description": "Notes from document verification process" - }, - "carianProfilStatus": { - "name": "carianProfilStatus", - "type": "string", - "scope": "global", - "value": null, - "description": "The status of the profile search/login action" - }, - "childcareAllowance": { - "name": "childcareAllowance", - "type": "number", - "scope": "global", - "value": null, - "description": "Childcare allowance for 12 years and below (RM 330.00)" - }, - "conditionalMessage": { - "name": "conditionalMessage", - "type": "string", - "scope": "global", - "value": null, - "description": "Conditional message based on application status" - }, - "dateMasukislamCopy": { - "name": "dateMasukislamCopy", - "type": "string", - "scope": "global", - "value": null, - "description": "Date field for masuk Islam copy" - }, - "dependentAllowance": { - "name": "dependentAllowance", - "type": "decimal", - "scope": "global", - "value": null, - "description": "Allowance per dependent (RM 408.00 for school age 7-17)" - }, - "jenisKadTanggungan": { - "name": "jenisKadTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Jenis kad tanggungan from Section B" - }, - "pengesahanApiError": { - "name": "pengesahanApiError", - "type": "object", - "scope": "global", - "value": null, - "description": "API error from verification submission" - }, - "penilaianAwalError": { - "name": "penilaianAwalError", - "type": "string", - "scope": "global", - "value": null, - "description": "Error message from penilaian awal processing" - }, - "updatedProfileData": { - "name": "updatedProfileData", - "type": "object", - "scope": "global", - "value": null, - "description": "Updated profile data after changes" - }, - "verificationStatus": { - "name": "verificationStatus", - "type": "string", - "scope": "global", - "value": null, - "description": "Status of document verification process" - }, - "canProceedToKifayah": { - "name": "canProceedToKifayah", - "type": "boolean", - "scope": "global", - "value": null, - "description": "Whether process can proceed to kifayah analysis" - }, - "carianLoginPassword": { - "name": "carianLoginPassword", - "type": "string", - "scope": "global", - "value": null, - "description": "Password for Asnaf login" - }, - "hasDeathRelatedNeed": { - "name": "hasDeathRelatedNeed", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has death-related needs" - }, - "noTelefonTanggungan": { - "name": "noTelefonTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "No telefon tanggungan from Section B" - }, - "penilaianAwalStatus": { - "name": "penilaianAwalStatus", - "type": "string", - "scope": "global", - "value": null, - "description": "Status of initial assessment submission" - }, - "processingTimeHours": { - "name": "processingTimeHours", - "type": "number", - "scope": "global", - "value": null, - "description": "Total processing time in hours" - }, - "profileUpdateStatus": { - "name": "profileUpdateStatus", - "type": "string", - "scope": "global", - "value": null, - "description": "Status of profile update process" - }, - "spouseKifayahAmount": { - "name": "spouseKifayahAmount", - "type": "decimal", - "scope": "global", - "value": null, - "description": "Had Kifayah amount for spouse (RM 780.00)" - }, - "bangsaLainTanggungan": { - "name": "bangsaLainTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Bangsa lain tanggungan from Section B" - }, - "bersekolahTanggungan": { - "name": "bersekolahTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Bersekolah tanggungan from Section B" - }, - "carianProfilApiError": { - "name": "carianProfilApiError", - "type": "string", - "scope": "global", - "value": null, - "description": "Error from Carian Profil API" - }, - "hasUploadedDocuments": { - "name": "hasUploadedDocuments", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether documents were uploaded" - }, - "hasUrgentMedicalNeed": { - "name": "hasUrgentMedicalNeed", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether applicant has urgent medical needs" - }, - "hubunganLainNyatakan": { - "name": "hubunganLainNyatakan", - "type": "string", - "scope": "global", - "value": null, - "description": "Hubungan lain nyatakan from Section B" - }, - "kifayahAnalysisError": { - "name": "kifayahAnalysisError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from kifayah analysis API call" - }, - "readyForPersonalInfo": { - "name": "readyForPersonalInfo", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether ready for personal info form" - }, - "syorPengesahanStatus": { - "name": "syorPengesahanStatus", - "type": "string", - "scope": "global", - "value": "pending", - "description": "Status of AI recommendation generation" - }, - "verificationApiError": { - "name": "verificationApiError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from document verification API call" - }, - "bilanganDewasaBekerja": { - "name": "bilanganDewasaBekerja", - "type": "number", - "scope": "global", - "value": 1, - "description": "Number of working adults (18 years and above)" - }, - "bilanganTanggunganIPT": { - "name": "bilanganTanggunganIPT", - "type": "number", - "scope": "global", - "value": 0, - "description": "Number of dependents studying in higher education institutions" - }, - "categoryKetuaKeluarga": { - "name": "categoryKetuaKeluarga", - "type": "string", - "scope": "global", - "value": "ketua_keluarga", - "description": "Category for head of family" - }, - "hasHighCostCommitment": { - "name": "hasHighCostCommitment", - "type": "boolean", - "scope": "global", - "value": null, - "description": "Whether applicant has high cost commitments" - }, - "kategoriKeluargaAsnaf": { - "name": "kategoriKeluargaAsnaf", - "type": "string", - "scope": "global", - "value": null, - "description": "Family asnaf category (Fakir/Miskin/Non-FM)" - }, - "keperluanLainNyatakan": { - "name": "keperluanLainNyatakan", - "type": "string", - "scope": "global", - "value": null, - "description": "Keperluan lain yang dinyatakan oleh pemohon" - }, - "kifayahAnalysisResult": { - "name": "kifayahAnalysisResult", - "type": "object", - "scope": "global", - "value": null, - "description": "Result from kifayah eligibility analysis API" - }, - "pengesahanApiResponse": { - "name": "pengesahanApiResponse", - "type": "object", - "scope": "global", - "value": null, - "description": "API response from verification submission" - }, - "pengesahanScriptError": { - "name": "pengesahanScriptError", - "type": "object", - "scope": "global", - "value": null, - "description": "Script error from pengesahan processing" - }, - "penilaianAwalApiError": { - "name": "penilaianAwalApiError", - "type": "string", - "scope": "global", - "value": null, - "description": "Error from the Penilaian Awal API" - }, - "tarikhLahirTanggungan": { - "name": "tarikhLahirTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Tarikh lahir tanggungan from Section B" - }, - "tempatLahirTanggungan": { - "name": "tempatLahirTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Tempat lahir tanggungan from Section B" - }, - "totalJumlahHadKifayah": { - "name": "totalJumlahHadKifayah", - "type": "number", - "scope": "global", - "value": 0, - "description": "Total family had kifayah amount" - }, - "verificationCompleted": { - "name": "verificationCompleted", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether verification process is completed" - }, - "verificationProcessed": { - "name": "verificationProcessed", - "type": "boolean", - "scope": "global", - "value": null, - "description": "Whether verification processing is complete" - }, - "verificationTimestamp": { - "name": "verificationTimestamp", - "type": "string", - "scope": "global", - "value": null, - "description": "Timestamp when verification was completed" - }, - "warganegaraTanggungan": { - "name": "warganegaraTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Warganegara tanggungan from Section B" - }, - "kifayahCalculationDate": { - "name": "kifayahCalculationDate", - "type": "string", - "scope": "global", - "value": null, - "description": "Date and time when Had Kifayah was calculated" - }, - "noPengenalanTanggungan": { - "name": "noPengenalanTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "No pengenalan tanggungan from Section B" - }, - "statusKahwinTanggungan": { - "name": "statusKahwinTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Status kahwin tanggungan from Section B" - }, - "tinggalBersamaKeluarga": { - "name": "tinggalBersamaKeluarga", - "type": "string", - "scope": "global", - "value": null, - "description": "Tinggal bersama keluarga from Section B" - }, - "bilanganTanggungan7to17": { - "name": "bilanganTanggungan7to17", - "type": "number", - "scope": "global", - "value": 2, - "description": "Number of dependents aged 7-17 years" - }, - "carianProfilApiResponse": { - "name": "carianProfilApiResponse", - "type": "object", - "scope": "global", - "value": null, - "description": "Response from Carian Profil API" - }, - "carianProfilScriptError": { - "name": "carianProfilScriptError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from Carian Profil script execution" - }, - "chronicIllnessAllowance": { - "name": "chronicIllnessAllowance", - "type": "number", - "scope": "global", - "value": null, - "description": "Chronic illness allowance (RM 243.00)" - }, - "dependentEducationLevel": { - "name": "dependentEducationLevel", - "type": "array", - "scope": "global", - "value": ["smk"], - "description": "Dependent highest education from Section B" - }, - "hadKifayahDewasaBekerja": { - "name": "hadKifayahDewasaBekerja", - "type": "number", - "scope": "global", - "value": 412, - "description": "Had kifayah amount per working adult (RM 412.00)" - }, - "hadKifayahTanggunganIPT": { - "name": "hadKifayahTanggunganIPT", - "type": "number", - "scope": "global", - "value": 613, - "description": "Had kifayah amount per dependent in higher education (RM 613.00)" - }, - "kifayahCalculationError": { - "name": "kifayahCalculationError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from Had Kifayah calculation" - }, - "profileSubmissionStatus": { - "name": "profileSubmissionStatus", - "type": "string", - "scope": "global", - "value": "success", - "description": "Status of profile submission" - }, - "selectStatusperkahwinan": { - "name": "selectStatusperkahwinan", - "type": "string", - "scope": "global", - "value": null, - "description": "Select field for status perkahwinan" - }, - "sendPendingNotification": { - "name": "sendPendingNotification", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether to send pending status notification" - }, - "tempatMenetapTanggungan": { - "name": "tempatMenetapTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Tempat menetap tanggungan from Section B" - }, - "verificationApiResponse": { - "name": "verificationApiResponse", - "type": "object", - "scope": "global", - "value": null, - "description": "Response from document verification API" - }, - "verificationCompletedAt": { - "name": "verificationCompletedAt", - "type": "string", - "scope": "global", - "value": null, - "description": "Timestamp when verification was completed" - }, - "verificationScriptError": { - "name": "verificationScriptError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from verification response processing script" - }, - "baseKifayahKetuaKeluarga": { - "name": "baseKifayahKetuaKeluarga", - "type": "number", - "scope": "global", - "value": 1215, - "description": "Base had kifayah amount for head of family (RM 1,215.00)" - }, - "jumlahBantuanDicadangkan": { - "name": "jumlahBantuanDicadangkan", - "type": "number", - "scope": "global", - "value": null, - "description": "Recommended assistance amount (RM)" - }, - "kifayahCalculationResult": { - "name": "kifayahCalculationResult", - "type": "object", - "scope": "global", - "value": null, - "description": "Result from Had Kifayah business rule calculation" - }, - "kifayahCalculationStatus": { - "name": "kifayahCalculationStatus", - "type": "string", - "scope": "global", - "value": "pending", - "description": "Status of had kifayah calculation" - }, - "kifayahNotificationError": { - "name": "kifayahNotificationError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from sending Had Kifayah notification" - }, - "pendidikanLainTanggungan": { - "name": "pendidikanLainTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Pendidikan lain tanggungan from Section B" - }, - "penilaianAwalApiResponse": { - "name": "penilaianAwalApiResponse", - "type": "object", - "scope": "global", - "value": null, - "description": "Response from the Penilaian Awal API" - }, - "penilaianAwalScriptError": { - "name": "penilaianAwalScriptError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error from Penilaian Awal script execution" - }, - "sendApprovalNotification": { - "name": "sendApprovalNotification", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether to send approval notification" - }, - "tarikhMulaKfamTanggungan": { - "name": "tarikhMulaKfamTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Tarikh mula KFAM tanggungan from Section B" - }, - "dokumenTambahanDiperlukan": { - "name": "dokumenTambahanDiperlukan", - "type": "array", - "scope": "global", - "value": null, - "description": "List of additional documents required" - }, - "hadKifayahTanggungan7to17": { - "name": "hadKifayahTanggungan7to17", - "type": "number", - "scope": "global", - "value": 408, - "description": "Had kifayah amount per dependent aged 7-17 years (RM 408.00)" - }, - "sendRejectionNotification": { - "name": "sendRejectionNotification", - "type": "boolean", - "scope": "global", - "value": false, - "description": "Whether to send rejection notification" - }, - "statusRecommendationError": { - "name": "statusRecommendationError", - "type": "object", - "scope": "global", - "value": null, - "description": "Error information from status recommendation" - }, - "bilanganDewasaTidakBekerja": { - "name": "bilanganDewasaTidakBekerja", - "type": "number", - "scope": "global", - "value": 1, - "description": "Number of non-working adults (18 years and above)" - }, - "bilanganTanggungan6KeBawah": { - "name": "bilanganTanggungan6KeBawah", - "type": "number", - "scope": "global", - "value": 1, - "description": "Number of dependents aged 6 years and below" - }, - "dependentDocumentsComplete": { - "name": "dependentDocumentsComplete", - "type": "boolean", - "scope": "global", - "value": null, - "description": "Whether dependent documents are complete" - }, - "documentVerificationResult": { - "name": "documentVerificationResult", - "type": "string", - "scope": "global", - "value": null, - "description": "Result from document verification form (lengkap/tidak_lengkap)" - }, - "statusRecommendationResult": { - "name": "statusRecommendationResult", - "type": "object", - "scope": "global", - "value": null, - "description": "Result of status recommendation process" - }, - "tarikhMasukIslamTanggungan": { - "name": "tarikhMasukIslamTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Tarikh masuk Islam tanggungan from Section B" - }, - "dependentVerificationStatus": { - "name": "dependentVerificationStatus", - "type": "string", - "scope": "global", - "value": null, - "description": "Dependent verification status" - }, - "hadKifayahDewasaTidakBekerja": { - "name": "hadKifayahDewasaTidakBekerja", - "type": "number", - "scope": "global", - "value": 167, - "description": "Had kifayah amount per non-working adult (RM 167.00)" - }, - "hadKifayahTanggungan6KeBawah": { - "name": "hadKifayahTanggungan6KeBawah", - "type": "number", - "scope": "global", - "value": 175, - "description": "Had kifayah amount per dependent aged 6 years and below (RM 175.00)" - }, - "totalHadKifayahDewasaBekerja": { - "name": "totalHadKifayahDewasaBekerja", - "type": "number", - "scope": "global", - "value": 0, - "description": "Total had kifayah for working adults" - }, - "totalHadKifayahTanggunganIPT": { - "name": "totalHadKifayahTanggunganIPT", - "type": "number", - "scope": "global", - "value": 0, - "description": "Total had kifayah for dependents in higher education" - }, - "pendidikanTertinggiTanggungan": { - "name": "pendidikanTertinggiTanggungan", - "type": "string", - "scope": "global", - "value": null, - "description": "Pendidikan tertinggi tanggungan from Section B" - }, - "totalHadKifayahTanggungan7to17": { - "name": "totalHadKifayahTanggungan7to17", - "type": "number", - "scope": "global", - "value": 0, - "description": "Total had kifayah for dependents aged 7-17 years" - }, - "totalHadKifayahDewasaTidakBekerja": { - "name": "totalHadKifayahDewasaTidakBekerja", - "type": "number", - "scope": "global", - "value": 0, - "description": "Total had kifayah for non-working adults" - }, - "totalHadKifayahTanggungan6KeBawah": { - "name": "totalHadKifayahTanggungan6KeBawah", - "type": "number", - "scope": "global", - "value": 0, - "description": "Total had kifayah for dependents aged 6 years and below" + "description": "Title from API response" } } diff --git a/docs/overview/ARCHITECTURE.md b/docs/overview/ARCHITECTURE.md deleted file mode 100644 index 66fc9eb..0000000 --- a/docs/overview/ARCHITECTURE.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/overview/DEVELOPMENT_GUIDE.md b/docs/overview/DEVELOPMENT_GUIDE.md deleted file mode 100644 index 74a8ff6..0000000 --- a/docs/overview/DEVELOPMENT_GUIDE.md +++ /dev/null @@ -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 ` -``` - -### 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. \ No newline at end of file diff --git a/docs/overview/PROJECT_OVERVIEW.md b/docs/overview/PROJECT_OVERVIEW.md deleted file mode 100644 index 5eee56f..0000000 --- a/docs/overview/PROJECT_OVERVIEW.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/process-builder/BUSINESS_RULES_TROUBLESHOOTING.md b/docs/process-builder/BUSINESS_RULES_TROUBLESHOOTING.md deleted file mode 100644 index 769f9c6..0000000 --- a/docs/process-builder/BUSINESS_RULES_TROUBLESHOOTING.md +++ /dev/null @@ -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 - - :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 - -/> -``` - -### 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 \ No newline at end of file diff --git a/docs/process-builder/NODE_COLOR_CUSTOMIZATION.md b/docs/process-builder/NODE_COLOR_CUSTOMIZATION.md deleted file mode 100644 index c0f882d..0000000 --- a/docs/process-builder/NODE_COLOR_CUSTOMIZATION.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/process-builder/PROCESS_FLOW_CREATION_GUIDE.md b/docs/process-builder/PROCESS_FLOW_CREATION_GUIDE.md deleted file mode 100644 index f2344c9..0000000 --- a/docs/process-builder/PROCESS_FLOW_CREATION_GUIDE.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/process-builder/ROADMAP.md b/docs/process-builder/ROADMAP.md deleted file mode 100644 index 4c922a9..0000000 --- a/docs/process-builder/ROADMAP.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/process-builder/TECHNICAL_GUIDE.md b/docs/process-builder/TECHNICAL_GUIDE.md deleted file mode 100644 index 3366431..0000000 --- a/docs/process-builder/TECHNICAL_GUIDE.md +++ /dev/null @@ -1,2619 +0,0 @@ -# Process Builder Technical Appendix - -This document provides technical implementation details for developers working with the Process Builder system. - -> For user documentation and usage guidelines, please refer to [Process Builder Documentation](USER_GUIDE.md) - -## Recent Updates (December 2024) - -### Critical Bug Fixes and Enhancements -- **Process Definition Loading**: Fixed issue where processes with empty nodes array but nodes embedded in edges wouldn't display on canvas -- **URL Parameter Support**: Added direct linking to processes via `/process-builder?id=uuid` pattern -- **Save Functionality**: Enhanced with success/error messages and proper state management -- **Navigation State**: Fixed unsaved changes modal appearing after successful saves -- **Connection Dragging**: Resolved Vue Flow interference with connector dragging functionality -- **Database Integration**: Full API integration with comprehensive error handling and validation -- **Toast Notifications**: Implemented user feedback system for all operations -- **Form Builder Consistency**: Updated form builder manage page to match process builder design - -### Breaking Changes -- Process store now requires API integration for all operations -- Local state has been eliminated in favor of database-driven architecture -- URL parameters are now required for process editing workflows - -## Architecture Overview - -### Technology Stack -- **Frontend Framework**: Nuxt 3 / Vue 3 -- **State Management**: Pinia -- **Flow Visualization**: Vue Flow -- **UI Framework**: Tailwind CSS -- **Icons**: Material Design Icons -- **Validation**: Zod -- **Form Components**: FormKit - -### Key Dependencies -```json -{ - "@vue-flow/core": "^1.42.5", - "@vue-flow/background": "^1.3.2", - "@vue-flow/controls": "^1.1.2", - "@vue-flow/minimap": "^1.5.3", - "@pinia/nuxt": "^0.4.11", - "@formkit/nuxt": "^1.5.5", - "uuid": "^10.0.0", - "zod": "^3.22.2" -} -``` - -## Project Structure - -``` -pages/ -├── process-builder/ -│ ├── index.vue # Main builder interface -│ └── manage.vue # Process management -components/ -├── process-flow/ -│ ├── ProcessFlowCanvas.vue # Flow canvas -│ ├── ProcessFlowNodes.js # Custom node types -│ ├── FormSelector.vue # Form selection component -│ ├── GatewayConditionManager.vue # Gateway conditions UI -│ ├── ApiNodeConfiguration.vue # API node configuration -│ ├── FormNodeConfiguration.vue # Form node configuration -│ ├── BusinessRuleConfiguration.vue # Business rule configuration -│ └── VariableManager.vue # Process variables manager -stores/ -├── processBuilder.js # State management -└── variableStore.js # Variable state management -composables/ -└── useProcessValidation.js # Process validation -types/ -└── process-builder.d.ts # TypeScript definitions -``` - -## URL Parameter System - -The Process Builder now supports direct linking to specific processes via URL parameters, enabling seamless navigation and bookmarking. - -### Implementation - -#### Route Handling -```javascript -// pages/process-builder/index.vue -const route = useRoute(); -const router = useRouter(); - -// Watch for URL parameter changes -watch(() => route.query.id, async (newId, oldId) => { - if (newId && newId !== oldId) { - try { - await loadProcessFromUrl(newId); - } catch (error) { - console.error('Error loading process from URL:', error); - // Redirect to clean state on error - router.push('/process-builder'); - } - } -}, { immediate: true }); - -// Load process from URL parameter -const loadProcessFromUrl = async (processId) => { - if (!processId || processId === 'new') return; - - try { - setLoading(true); - await processStore.loadProcess(processId); - - if (!processStore.currentProcess) { - throw new Error('Process not found'); - } - - // Update URL without triggering navigation - await router.replace({ - path: '/process-builder', - query: { id: processId } - }); - - } catch (error) { - console.error('Failed to load process:', error); - toast.error('Failed to load process: ' + (error.message || 'Unknown error')); - - // Clear invalid URL parameter - await router.replace('/process-builder'); - } finally { - setLoading(false); - } -}; -``` - -#### Navigation Updates -```javascript -// Create new process with URL update -const createNewProcess = async () => { - try { - processStore.clearProcess(); - // Navigate to clean URL for new process - await router.push('/process-builder'); - } catch (error) { - console.error('Error creating new process:', error); - } -}; - -// Save process with URL synchronization -const saveProcess = async () => { - try { - const result = await processStore.saveProcess(); - - if (result && result.id) { - // Update URL with saved process ID - await router.replace({ - path: '/process-builder', - query: { id: result.id } - }); - - toast.success('Process saved successfully'); - } - } catch (error) { - toast.error('Failed to save process'); - } -}; -``` - -### URL Patterns - -- **New Process**: `/process-builder` (no parameters) -- **Edit Process**: `/process-builder?id={uuid}` -- **Navigation**: Automatic URL updates when saving new processes -- **Validation**: Invalid IDs redirect to clean builder state - -### Error Handling - -- **Invalid Process ID**: Graceful fallback to new process state -- **Network Errors**: User-friendly error messages with toast notifications -- **Missing Processes**: Automatic cleanup of invalid URL parameters -- **Loading States**: Visual feedback during process loading - -### Integration Points - -- **Process Management**: Direct links from manage page to builder -- **Form Builder**: Consistent URL pattern across builders -- **Navigation Guards**: Unsaved changes detection with URL awareness -- **Bookmarking**: Users can bookmark specific processes for quick access - -## Database Integration & API System - -The Process Builder now features comprehensive database integration with a RESTful API system, replacing local state management with persistent storage. - -### API Endpoints - -#### Core Process Operations -```javascript -// GET /api/process - List all processes with pagination -GET /api/process?page=1&limit=10&search=workflow&status=draft - -// GET /api/process/[id] - Get specific process -GET /api/process/550e8400-e29b-41d4-a716-446655440000 - -// POST /api/process - Create new process -POST /api/process -{ - "name": "New Workflow", - "description": "Process description", - "processDefinition": { nodes: [], edges: [] }, - "processVariables": [], - "isTemplate": false -} - -// PUT /api/process/[id] - Update existing process -PUT /api/process/550e8400-e29b-41d4-a716-446655440000 -{ - "name": "Updated Workflow", - "processDefinition": { /* updated definition */ } -} - -// DELETE /api/process/[id] - Delete process -DELETE /api/process/550e8400-e29b-41d4-a716-446655440000 -``` - -#### Advanced Operations -```javascript -// POST /api/process/[id]/duplicate - Duplicate process -POST /api/process/550e8400-e29b-41d4-a716-446655440000/duplicate -{ - "name": "Workflow Copy", - "regenerateIds": true -} - -// POST /api/process/[id]/publish - Publish process -POST /api/process/550e8400-e29b-41d4-a716-446655440000/publish -{ - "version": "1.0.0", - "notes": "Initial release" -} - -// GET /api/process/templates - Get process templates -GET /api/process/templates -``` - -### Process Store Integration - -#### Enhanced Store Methods -```javascript -// stores/processBuilder.js -export const useProcessBuilderStore = defineStore('processBuilder', () => { - - // Load process from API with error handling - const loadProcess = async (processId) => { - try { - loading.value = true; - const { data } = await $fetch(`/api/process/${processId}`); - - if (!data) { - throw new Error('Process not found'); - } - - currentProcess.value = data; - - // Handle backward compatibility for process definitions - if (data.processDefinition) { - if (data.processDefinition.nodes?.length === 0 && - data.processDefinition.edges?.length > 0) { - // Extract nodes from edges for backward compatibility - const extractedNodes = extractNodesFromEdges(data.processDefinition.edges); - nodes.value = extractedNodes; - edges.value = data.processDefinition.edges; - } else { - nodes.value = data.processDefinition.nodes || []; - edges.value = data.processDefinition.edges || []; - } - } - - return data; - } catch (error) { - console.error('Error loading process:', error); - throw error; - } finally { - loading.value = false; - } - }; - - // Save process with validation and success feedback - const saveProcess = async () => { - try { - loading.value = true; - - const processData = { - name: localProcess.value.name, - description: localProcess.value.description, - processDefinition: { - nodes: nodes.value, - edges: edges.value - }, - processVariables: processVariables.value, - processSettings: { - // Process configuration settings - processType: localProcess.value.processType, - priority: localProcess.value.priority, - category: localProcess.value.category, - timeoutDuration: localProcess.value.timeoutDuration, - allowParallel: localProcess.value.allowParallel, - enableErrorRecovery: localProcess.value.enableErrorRecovery, - sendNotifications: localProcess.value.sendNotifications - } - }; - - let result; - if (currentProcess.value?.id) { - // Update existing process - result = await $fetch(`/api/process/${currentProcess.value.id}`, { - method: 'PUT', - body: processData - }); - } else { - // Create new process - result = await $fetch('/api/process', { - method: 'POST', - body: processData - }); - } - - if (result?.data) { - currentProcess.value = result.data; - hasUnsavedChanges.value = false; - lastSavedState.value = JSON.stringify(processData); - return result.data; - } - - throw new Error('Save operation failed'); - } catch (error) { - console.error('Error saving process:', error); - throw error; - } finally { - loading.value = false; - } - }; - - // Fetch all processes with filtering - const fetchProcesses = async (options = {}) => { - try { - const params = new URLSearchParams({ - page: options.page || 1, - limit: options.limit || 20, - ...(options.search && { search: options.search }), - ...(options.status && { status: options.status }) - }); - - const response = await $fetch(`/api/process?${params}`); - processes.value = response.data || []; - return response; - } catch (error) { - console.error('Error fetching processes:', error); - throw error; - } - }; - - return { - // State - currentProcess: readonly(currentProcess), - processes: readonly(processes), - loading: readonly(loading), - hasUnsavedChanges: readonly(hasUnsavedChanges), - - // Actions - loadProcess, - saveProcess, - fetchProcesses, - clearProcess, - duplicateProcess, - deleteProcess - }; -}); -``` - -### Backward Compatibility - -#### Process Definition Loading -```javascript -// Handle legacy process definitions with embedded nodes in edges -const extractNodesFromEdges = (edges) => { - const nodeMap = new Map(); - - edges.forEach(edge => { - // Extract source node - if (edge.sourceNode && !nodeMap.has(edge.source)) { - nodeMap.set(edge.source, { - id: edge.source, - type: edge.sourceNode.type, - position: edge.sourceNode.position || { x: 0, y: 0 }, - data: edge.sourceNode.data || {} - }); - } - - // Extract target node - if (edge.targetNode && !nodeMap.has(edge.target)) { - nodeMap.set(edge.target, { - id: edge.target, - type: edge.targetNode.type, - position: edge.targetNode.position || { x: 0, y: 0 }, - data: edge.targetNode.data || {} - }); - } - }); - - return Array.from(nodeMap.values()); -}; -``` - -### Error Handling & Validation - -#### API Error Responses -```javascript -// Standardized error response format -{ - "success": false, - "error": { - "code": "VALIDATION_ERROR", - "message": "Process name is required", - "details": { - "field": "name", - "value": "", - "constraint": "required" - } - } -} - -// Network error handling -try { - await processStore.saveProcess(); -} catch (error) { - if (error.statusCode === 404) { - toast.error('Process not found'); - } else if (error.statusCode === 422) { - toast.error('Validation error: ' + error.data?.error?.message); - } else { - toast.error('An unexpected error occurred'); - } -} -``` - -#### Data Validation -```javascript -// Process validation using Zod schemas -import { z } from 'zod'; - -const ProcessSchema = z.object({ - name: z.string().min(1, 'Process name is required'), - description: z.string().optional(), - processDefinition: z.object({ - nodes: z.array(z.any()), - edges: z.array(z.any()) - }), - processVariables: z.array(z.any()).default([]), - isTemplate: z.boolean().default(false) -}); - -// Validate before save -const validateProcess = (processData) => { - try { - return ProcessSchema.parse(processData); - } catch (error) { - throw new Error(`Validation failed: ${error.message}`); - } -}; -``` - -## Vue Flow Integration & Performance Fixes - -Critical fixes were implemented to resolve interference between state management and Vue Flow's internal operations, particularly affecting connection dragging functionality. - -### Connection Dragging Bug Fix - -#### Problem -The aggressive syncing of node positions and edge updates was interfering with Vue Flow's native drag-and-drop functionality, causing connections to fail when dragging from node handles. - -#### Solution -```javascript -// stores/processBuilder.js - Optimized node sync handling -const syncNodePositions = (vueFlowNodes) => { - if (!vueFlowNodes || dragging.value) return; // Skip sync during dragging - - const positionsChanged = vueFlowNodes.some(vfNode => { - const storeNode = nodes.value.find(n => n.id === vfNode.id); - if (!storeNode) return false; - - return Math.abs(storeNode.position.x - vfNode.position.x) > 1 || - Math.abs(storeNode.position.y - vfNode.position.y) > 1; - }); - - if (positionsChanged) { - vueFlowNodes.forEach(vfNode => { - const nodeIndex = nodes.value.findIndex(n => n.id === vfNode.id); - if (nodeIndex !== -1) { - nodes.value[nodeIndex].position = { ...vfNode.position }; - } - }); - } -}; - -// Enhanced edge handling with change detection -const handleEdgeChanges = (changes, currentEdges) => { - if (!changes || changes.length === 0) return; - - let hasChanges = false; - - changes.forEach(change => { - if (change.type === 'add' && change.item) { - // Only add if it doesn't already exist - const exists = edges.value.some(e => e.id === change.item.id); - if (!exists) { - addEdge(change.item); - hasChanges = true; - } - } else if (change.type === 'remove') { - const index = edges.value.findIndex(e => e.id === change.id); - if (index !== -1) { - edges.value.splice(index, 1); - hasChanges = true; - } - } - }); - - if (hasChanges) { - markUnsavedChanges(); - } -}; -``` - -#### Canvas Component Updates -```vue - - - - -``` - -### Performance Optimizations - -#### Reduced Re-renders -```javascript -// Computed properties for reactive data binding -const flowNodes = computed({ - get: () => processStore.nodes, - set: (newNodes) => { - if (!isDragging.value) { - processStore.updateNodes(newNodes); - } - } -}); - -const flowEdges = computed({ - get: () => processStore.edges, - set: (newEdges) => { - processStore.updateEdges(newEdges); - } -}); - -// Debounced position sync for smooth dragging -import { debounce } from 'lodash-es'; - -const debouncedSync = debounce((nodes) => { - processStore.syncNodePositions(nodes); -}, 100); -``` - -#### Memory Management -```javascript -// Cleanup watchers and event listeners -onBeforeUnmount(() => { - // Clear any pending debounced calls - debouncedSync.cancel(); - - // Reset dragging state - processStore.setDragging(false); - - // Clear selections - if (vueFlowInstance.value) { - vueFlowInstance.value.setSelectedNodes([]); - vueFlowInstance.value.setSelectedEdges([]); - } -}); -``` - -### State Synchronization - -#### Bidirectional Data Flow -```javascript -// Process Store - Enhanced state management -export const useProcessBuilderStore = defineStore('processBuilder', () => { - const dragging = ref(false); - - const setDragging = (value) => { - dragging.value = value; - }; - - const updateNodes = (newNodes) => { - if (!dragging.value) { - nodes.value = newNodes.map(node => ({ - ...node, - position: { ...node.position } - })); - markUnsavedChanges(); - } - }; - - const updateEdges = (newEdges) => { - edges.value = newEdges.map(edge => ({ ...edge })); - markUnsavedChanges(); - }; - - // Smart change detection - const markUnsavedChanges = () => { - const currentState = JSON.stringify({ - nodes: nodes.value, - edges: edges.value, - variables: processVariables.value - }); - - if (currentState !== lastSavedState.value) { - hasUnsavedChanges.value = true; - } - }; - - return { - // State - dragging: readonly(dragging), - - // Actions - setDragging, - updateNodes, - updateEdges, - syncNodePositions, - handleNodeChanges, - handleEdgeChanges - }; -}); -``` - -## Component Architecture - -### Core Components - -1. **ProcessFlowCanvas.vue** -```vue - -``` - -2. **ProcessFlowNodes.js** -```javascript -import { h, markRaw } from 'vue'; -import { Handle, Position } from '@vue-flow/core'; - -// Enhanced custom node renderer with 4-point connection system -const CustomNode = markRaw({ - template: ` -
- - - - - - - - - - - - - -
-
- -
-
{{ label }}
-
- -
- -
-
- `, - props: ['id', 'type', 'label', 'data'], - components: { Handle }, - mounted() { - // Add event listeners for handle visibility - this.$el.addEventListener('mouseenter', this.showHandles); - this.$el.addEventListener('mouseleave', this.hideHandles); - }, - methods: { - showHandles() { - const handles = this.$el.querySelectorAll('.vue-flow__handle'); - handles.forEach(handle => { - handle.style.opacity = '1'; - }); - }, - hideHandles() { - const handles = this.$el.querySelectorAll('.vue-flow__handle'); - handles.forEach(handle => { - handle.style.opacity = '0'; - }); - } - } -}); - -// Enhanced CSS for handles -const handleStyles = ` -.custom-node:hover .vue-flow__handle { - opacity: 1 !important; - z-index: 100; -} - -.vue-flow__handle { - opacity: 0; - transition: all 0.2s ease; - cursor: crosshair; - border: 2px solid; - border-radius: 50%; -} - -.handle-input { - background-color: #3b82f6; - border-color: #1d4ed8; -} - -.handle-output { - background-color: #10b981; - border-color: #059669; -} - -.vue-flow__handle:hover { - transform: scale(1.2); - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3); -} - -.custom-node.connecting .vue-flow__handle { - opacity: 1; -} - -/* Special handling for gateway nodes (rotated) */ -.node-gateway .vue-flow__handle { - position: absolute; -} - -.node-gateway .vue-flow__handle[data-handlepos="top"] { - top: -6px; - left: 50%; - transform: translate(-50%, -50%) rotate(-45deg); -} - -.node-gateway .vue-flow__handle[data-handlepos="right"] { - right: -6px; - top: 50%; - transform: translate(50%, -50%) rotate(-45deg); -} - -.node-gateway .vue-flow__handle[data-handlepos="bottom"] { - bottom: -6px; - left: 50%; - transform: translate(-50%, 50%) rotate(-45deg); -} - -.node-gateway .vue-flow__handle[data-handlepos="left"] { - left: -6px; - top: 50%; - transform: translate(-50%, -50%) rotate(-45deg); -} -`; - -// Node type definitions with enhanced handle system -export const nodeTypes = markRaw({ - task: TaskNode, - start: StartNode, - end: EndNode, - gateway: GatewayNode, - form: FormNode, - api: ApiNode, - script: ScriptNode, - 'business-rule': BusinessRuleNode, - notification: NotificationNode -}); - -// Connection validation -export const isValidConnection = (connection) => { - // Prevent self-connections - if (connection.source === connection.target) { - return false; - } - - // Validate handle types - const sourceHandle = connection.sourceHandle; - const targetHandle = connection.targetHandle; - - // Ensure proper source/target handle types - if (sourceHandle && !sourceHandle.includes('right') && !sourceHandle.includes('bottom')) { - return false; - } - - if (targetHandle && !targetHandle.includes('top') && !targetHandle.includes('left')) { - return false; - } - - return true; -}; - -// Enhanced connection handler with handle-specific routing -export const onConnect = (params) => { - const { source, target, sourceHandle, targetHandle } = params; - - return { - id: `${source}-${target}-${Date.now()}`, - source, - target, - sourceHandle, - targetHandle, - type: 'smoothstep', - animated: true, - style: { - strokeWidth: 2, - stroke: '#64748b' - }, - data: { - sourcePosition: sourceHandle?.split('-')[1] || 'bottom', - targetPosition: targetHandle?.split('-')[1] || 'top' - } - }; -}; - -#### Handle System Features - -1. **4-Point Connection System**: - - **Top Handle**: Primary input connection point (blue, target) - - **Right Handle**: Secondary output connection point (green, source) - - **Bottom Handle**: Primary output connection point (green, source) - - **Left Handle**: Secondary input connection point (blue, target) - -2. **Enhanced Visibility**: - - Handles are invisible by default for clean UI - - Become visible on node hover with smooth transitions - - Unique IDs for precise connection targeting (`nodeId-position`) - -3. **Visual Feedback**: - - Color-coded handles (blue for inputs, green for outputs) - - Hover effects with scaling and shadow - - Connection state awareness - -4. **Special Node Handling**: - - **Start Nodes**: Only output handles (right + bottom) - - **End Nodes**: Only input handles (top + left) - - **Gateway Nodes**: Rotated handle positioning for diamond shape - -5. **Connection Validation**: - - Prevents self-connections - - Validates proper source/target handle types - - Ensures logical connection flow - -## Enhanced Node Configuration Components - -The following components implement the improved UI/UX for node configuration: - -### 1. VariableManager.vue - -The Variable Manager allows users to create, edit, and manage global process variables. - -```vue - -``` - -### 2. BusinessRuleConfiguration.vue - -The Business Rule Configuration component provides a stepped workflow for configuring rule nodes. - -```vue - - - - -### 3. GatewayConditionManager.vue - -The Gateway Condition Manager provides an enhanced UI for decision point configuration. - -```vue - -``` - -### 4. ApiNodeConfiguration.vue - -The API Node Configuration component provides a stepped interface for configuring API calls. - -```vue - -``` - -### 5. FormNodeConfiguration.vue - -The Form Node Configuration component provides a comprehensive 3-step interface for configuring form interactions with enhanced data mapping and conditional field behavior. - -#### Architecture Overview - -```vue - -``` - -#### Script Implementation - -```javascript - -``` - -#### Key Features - -1. **3-Step Configuration Workflow**: - - **Step 1**: Form selection with integrated FormSelector component - - **Step 2**: Bidirectional data mapping between process variables and form fields - - **Step 3**: Dynamic field conditions for runtime form behavior - -2. **Input/Output Mappings**: - - **Input Mappings**: Map process variables to form fields for pre-filling - - **Output Mappings**: Capture form submission data in process variables - - **Auto-Variable Creation**: Automatically create process variables from form fields - - **FormKit Integration**: Seamless dropdown selection with proper v-model binding - -3. **Field Conditions**: - - **Conditional Logic**: Support for 9 different operators (equals, contains, greater than, etc.) - - **Multiple Actions**: readonly, hide, show, required, optional, enable - - **Process Variable Integration**: Conditions based on current process state - - **Real-time Updates**: Dynamic form behavior during process execution - -4. **Data Persistence**: - - **Deep Copying**: Proper reactivity management to prevent data corruption - - **Explicit Save**: Manual save mechanism with `saveAllChanges()` function exposed via `defineExpose` - - **Change Tracking**: Reliable change detection and persistence - -5. **API Integration**: - - **Form Field Loading**: Dynamic loading of form fields via `/api/forms/{formId}/fields` - - **Error Handling**: Comprehensive error handling for API failures - - **Loading States**: Proper loading state management - -#### Component Integration - -The FormNodeConfiguration component integrates with: - -- **FormSelector.vue**: For form selection and browsing -- **VariableStore**: For process variable management and creation -- **FormNodeConfigurationModal.vue**: Modal wrapper with save/cancel functionality -- **ProcessBuilder**: Main process builder integration - -#### Data Flow - -``` -1. User selects form → handleFormSelection() → loadFormFields() → Update localNodeData -2. User adds mappings → addInputMapping()/addOutputMapping() → Update arrays → saveChanges() -3. User configures conditions → addFieldCondition() → Update conditions array → saveChanges() -4. User clicks Save → FormNodeConfigurationModal calls saveAllChanges() → emit('update', data) -5. Parent receives update → Process node data is persisted -``` - -This architecture ensures reliable data persistence, proper reactivity management, and seamless integration with the broader process builder system. - -## State Management - -The project uses Pinia for state management. Key stores include: - -### processBuilder.js Enhanced with Settings Management - -The Process Builder store has been enhanced to handle comprehensive process settings: - -```javascript -export const useProcessBuilderStore = defineStore('processBuilder', { - state: () => ({ - processes: [], - currentProcess: null, - selectedNodeId: null, - selectedEdgeId: null, - history: [], - historyIndex: -1, - unsavedChanges: false - }), - - actions: { - /** - * Update the current process with new data including settings - */ - updateCurrentProcess(processUpdates) { - if (!this.currentProcess) return; - - this.currentProcess = { - ...this.currentProcess, - ...processUpdates, - updatedAt: new Date().toISOString() - }; - - this.unsavedChanges = true; - this.saveToHistory('Update process settings'); - }, - - /** - * Enhanced save process with settings persistence - */ - async saveProcess() { - if (!this.currentProcess) return; - - try { - // Save process data including all settings - const processData = { - ...this.currentProcess, - variables: useVariableStore().getAllVariables.process, - // Settings are now part of the process object structure - settings: this.currentProcess.settings || {} - }; - - // TODO: Implement API call to save process with settings - const index = this.processes.findIndex(p => p.id === this.currentProcess.id); - if (index !== -1) { - this.processes[index] = processData; - } else { - this.processes.push(processData); - } - - this.unsavedChanges = false; - return true; - } catch (error) { - console.error('Error saving process:', error); - return false; - } - } - } -}); -``` - -### Process Settings Data Structure - -The enhanced process object now includes comprehensive settings: - -```typescript -interface ProcessSettings { - // Process Info - priority: 'low' | 'normal' | 'high' | 'critical'; - category: string; - owner: string; - - // Execution Settings - processType: 'standard' | 'approval' | 'data_collection' | 'automation' | 'review'; - maxExecutionTime: number; // minutes - autoTimeout: number; // hours - allowParallel: boolean; - enableErrorRecovery: boolean; - sendNotifications: boolean; - - // Data & Variables - dataPersistence: 'session' | 'temporary' | 'short_term' | 'long_term' | 'permanent'; - logVariableChanges: boolean; - encryptSensitiveData: boolean; - dataRetentionPolicy: string; - - // Permissions - executionPermission: 'public' | 'authenticated' | 'roles' | 'managers' | 'admin'; - allowedRoles: string; - modificationPermission: 'owner' | 'managers' | 'admin' | 'editors'; - requireApproval: boolean; - enableAuditTrail: boolean; -} - -interface EnhancedProcess { - id: string; - name: string; - description: string; - nodes: ProcessNode[]; - edges: ProcessEdge[]; - variables: Record; - settings: ProcessSettings; - createdAt: string; - updatedAt: string; -} -``` - -## Process Settings Implementation - -### ProcessSettingsModal.vue Component Architecture - -The Process Settings modal is implemented as a comprehensive tabbed interface: - -```vue - - - -``` - -### JSON Export Functionality - -The JSON export feature provides comprehensive process configuration export: - -```javascript -// Complete export data structure -const formattedJson = computed(() => { - const exportData = { - processInfo: { - id: localProcess.value.id, - name: localProcess.value.name, - description: localProcess.value.description, - priority: localProcess.value.priority, - category: localProcess.value.category, - owner: localProcess.value.owner - }, - settings: { - processType: localProcess.value.processType, - maxExecutionTime: localProcess.value.maxExecutionTime, - autoTimeout: localProcess.value.autoTimeout, - allowParallel: localProcess.value.allowParallel, - enableErrorRecovery: localProcess.value.enableErrorRecovery, - sendNotifications: localProcess.value.sendNotifications - }, - dataSettings: { - dataPersistence: localProcess.value.dataPersistence, - logVariableChanges: localProcess.value.logVariableChanges, - encryptSensitiveData: localProcess.value.encryptSensitiveData, - dataRetentionPolicy: localProcess.value.dataRetentionPolicy - }, - permissions: { - executionPermission: localProcess.value.executionPermission, - allowedRoles: localProcess.value.allowedRoles, - modificationPermission: localProcess.value.modificationPermission, - requireApproval: localProcess.value.requireApproval, - enableAuditTrail: localProcess.value.enableAuditTrail - }, - workflow: { - nodes: processStore.currentProcess?.nodes || [], - edges: processStore.currentProcess?.edges || [] - }, - variables: variableStore.getAllVariables, - metadata: { - nodeCount: nodeCount.value, - edgeCount: edgeCount.value, - variableCount: variableCount.value, - exportedAt: new Date().toISOString() - } - } - - return JSON.stringify(exportData, null, 2) -}) - -// Export functions -const copyToClipboard = async () => { - try { - await navigator.clipboard.writeText(formattedJson.value) - console.log('JSON copied to clipboard') - } catch (err) { - console.error('Failed to copy JSON:', err) - } -} - -const downloadJson = () => { - const blob = new Blob([formattedJson.value], { type: 'application/json' }) - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = `${localProcess.value.name || 'process'}_settings.json` - document.body.appendChild(a) - a.click() - document.body.removeChild(a) - URL.revokeObjectURL(url) -} -``` - -### Integration with Process Builder - -The Process Settings modal is integrated into the main Process Builder interface: - -```vue - - - - -``` - -### API Integration Considerations - -For future API integration, the settings should be handled as follows: - -```javascript -// API endpoint structure for process settings -POST /api/processes/{processId}/settings -PUT /api/processes/{processId}/settings -GET /api/processes/{processId}/settings - -// Request/Response format -{ - "processInfo": { - "name": "Customer Onboarding", - "description": "Complete customer onboarding workflow", - "priority": "high", - "category": "Sales", - "owner": "Sales Manager" - }, - "settings": { - "processType": "approval", - "maxExecutionTime": 480, - "autoTimeout": 48, - "allowParallel": true, - "enableErrorRecovery": true, - "sendNotifications": true - }, - "dataSettings": { - "dataPersistence": "long_term", - "logVariableChanges": true, - "encryptSensitiveData": false, - "dataRetentionPolicy": "Delete after 30 days" - }, - "permissions": { - "executionPermission": "roles", - "allowedRoles": "hr_manager,department_head", - "modificationPermission": "managers", - "requireApproval": true, - "enableAuditTrail": true - } -} -``` - -### Performance Considerations - -1. **Lazy Loading**: Settings are only loaded when the modal is opened -2. **Local State Management**: Changes are made to local copies to avoid unnecessary reactivity -3. **Debounced Updates**: Consider debouncing settings updates for better performance -4. **Validation**: Client-side validation before API calls - -### Security Considerations - -1. **Permission Validation**: Server-side validation of permission changes -2. **Audit Trail**: All settings changes should be logged -3. **Role Verification**: Verify user permissions before allowing settings modifications -4. **Data Encryption**: Implement proper encryption for sensitive settings - -### variableStore.js - -```javascript -import { defineStore } from 'pinia'; - -export const useVariableStore = defineStore('variables', { - state: () => ({ - variables: { - global: [], - local: {} - } - }), - - getters: { - getAllVariables: (state) => state.variables, - getVariableByName: (state) => (name, scope = 'global') => { - if (scope === 'global') { - return state.variables.global.find(v => v.name === name); - } else { - return state.variables.local[scope]?.find(v => v.name === name); - } - } - }, - - actions: { - addVariable(variable) { - const { scope = 'global' } = variable; - - if (scope === 'global') { - this.variables.global.push(variable); - } else { - if (!this.variables.local[scope]) { - this.variables.local[scope] = []; - } - this.variables.local[scope].push(variable); - } - }, - - updateVariable(name, updatedVariable, scope = 'global') { - if (scope === 'global') { - const index = this.variables.global.findIndex(v => v.name === name); - if (index !== -1) { - this.variables.global[index] = { ...updatedVariable }; - } - } else { - if (this.variables.local[scope]) { - const index = this.variables.local[scope].findIndex(v => v.name === name); - if (index !== -1) { - this.variables.local[scope][index] = { ...updatedVariable }; - } - } - } - }, - - deleteVariable(name, scope = 'global') { - if (scope === 'global') { - this.variables.global = this.variables.global.filter(v => v.name !== name); - } else if (this.variables.local[scope]) { - this.variables.local[scope] = this.variables.local[scope].filter(v => v.name !== name); - } - } - } -}); -``` - -## UI Component Styling - -The project uses Tailwind CSS for styling with consistent patterns: - -### Color Theming by Component Type - -Each node type has a consistent color theme: - -- **Business Rules**: Purple -- **API Tasks**: Indigo -- **Form Tasks**: Emerald -- **Decision Points**: Orange -- **Variables**: Blue - -### Common Visual Elements - -1. **Modal Headers**: -```html -
-
-
- -
-
-

{Title}

-

{Description}

-
-
-
-``` - -2. **Step Indicators**: -```html -
-
- -
-
- -
-``` - -3. **Empty States**: -```html -
- -

- {Empty State Title} -

-

- {Empty State Description} -

- - - {Action Text} - -
-``` - -## Best Practices for Development - -When developing new components or enhancing existing ones: - -1. **Consistent Design Pattern**: - - Follow the established design patterns for node configurations - - Use the same structure for headers, step indicators, and action buttons - - Maintain the color coding system for different node types - -2. **Responsive Components**: - - Ensure all components work on various screen sizes - - Use responsive utilities from Tailwind - - Test on mobile and desktop views - -3. **State Management**: - - Store node configuration in the appropriate Pinia store - - Use reactive Vue 3 patterns with `ref`, `computed`, etc. - - Implement proper validation before saving - -4. **Accessibility**: - - Ensure all UI elements are keyboard accessible - - Use semantic HTML elements - - Maintain proper focus management in modals - -5. **Data Flow Visualization**: - - Use visual indicators to show data flow direction - - Provide clear feedback on how variables are used - - Highlight connections between nodes - ---- - -For user documentation and usage guidelines, please refer to [Process Builder Documentation](USER_GUIDE.md). - -Last updated: December 2024 \ No newline at end of file diff --git a/docs/process-builder/USER_GUIDE.md b/docs/process-builder/USER_GUIDE.md deleted file mode 100644 index 2424d01..0000000 --- a/docs/process-builder/USER_GUIDE.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/process-execution/TECHNICAL_GUIDE.md b/docs/process-execution/TECHNICAL_GUIDE.md deleted file mode 100644 index 94aa3e9..0000000 --- a/docs/process-execution/TECHNICAL_GUIDE.md +++ /dev/null @@ -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; - 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; - } - ``` - -## 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 \ No newline at end of file diff --git a/docs/process-execution/USER_GUIDE.md b/docs/process-execution/USER_GUIDE.md deleted file mode 100644 index f7abc6e..0000000 --- a/docs/process-execution/USER_GUIDE.md +++ /dev/null @@ -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. - -![Execution Dashboard](../assets/images/execution-dashboard.png) - -### 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. - -![New Case](../assets/images/new-case.png) - -### 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](../assets/images/task-inbox.png) - -### 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](../assets/images/task-detail.png) - -### 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](../assets/images/case-history.png) - -### 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 \ No newline at end of file diff --git a/pages/process-builder/manage.vue b/pages/process-builder/manage.vue index c6f6472..c950cee 100644 --- a/pages/process-builder/manage.vue +++ b/pages/process-builder/manage.vue @@ -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(() => {
+ + +