Appearance
Form System: Complete Guide
Overview
The Cavai Form System is a specialized block architecture that allows building dynamic forms with various input types. This guide covers the complete implementation across Application-Frontend (AF) and Creative-Engine (CE).
Architecture Summary
Core Concept
- Specialized Block Pattern: Forms are specialized blocks that manage their own subblocks (form inputs)
- Single Input Type: All form inputs use
BLOCKS.FORM_INPUT(formInputProperties) with atypeproperty for differentiation - Centralized Type System: All input type metadata is managed in a single source of truth
- Persistent Numbering: Each input gets a stable, per-type identifier that doesn't change on reorder
Key Benefits
- ✅ DRY: Shared base properties and validation logic
- ✅ Flexible: Easy to add new input types without creating new visual elements
- ✅ Consistent: Same data structure for all input types
- ✅ Maintainable: Single configuration component and centralized type definitions
Recent Improvements (August 2025)
FormConfiguration.vue Cleanup
The main form configuration component has been significantly simplified and improved:
Code Simplification
- Reduced complexity: Simplified
renumberInputDisplayNames()method from complex TypeScript types to clean, readable code - Consistent patterns: All methods now use
updateValue()pattern for consistency - Removed debug code: Cleaned up console.log statements and unnecessary comments
- Method optimization:
addFormInput(): Reduced from 41 to 16 linesremoveFormInput(): Simplified parameter handling and logicupdateFormInput(): Streamlined to 3 lines
Bug Fixes
- Fixed circular reference: Removed
formInputDefaultsfromformDefaults.inputsto prevent crashes - Fixed parameter order: Corrected
updateValue('inputs', inputs)parameter sequence - Fixed border defaults: Set
border: falsein form defaults to prevent rendering issues - Fixed typeIndex renumbering: Now correctly renumbers form inputs after deletion
Real-time Updates Investigation
Discovered architectural limitation preventing real-time preview updates:
- Root cause: Creative-Engine runs in iframe, isolated from Application-Frontend Vuex store
- Current behavior: Configuration changes save correctly but don't reflect immediately in preview
- Workaround: Manual iframe reload or creative republish required for preview updates
- Documentation: Added detailed explanation in
creative-engine-frontend-integration.md
CreativeFormBlock.vue Modernization
Completed comprehensive cleanup and modernization of the Creative-Engine form rendering component:
Code Cleanup
- Removed unused code: Cleaned up submit button styling section, removed complex fallback logic
- Optimized methods: Simplified
getInputComponent()from loop to direct object access - Consistent patterns: Aligned styling code with direct property access pattern
Component Architecture Improvements
- Confirmed modern patterns: Dynamic component rendering with
<component :is="getInputComponent(input.type)">already in place - Excellent slot usage: FormInputBase.vue provides sophisticated slot system for flexibility
- Proper component separation: All 10 input types have dedicated components with proper mapping
New Error Handling Component
- Created FormErrorDisplay.vue: Reusable error component with flexible slot system
- Slot-based customization: Supports custom error containers and message formatting
- Integrated seamlessly: Replaced hardcoded error display in CreativeFormBlock
- Improved modularity: Provides consistent error handling across form system
Submit Button Fix
- Default text: Set
submitButtonText: 'Submit'in Application-Frontend defaults.ts - Improved UX: Submit button now appears by default on new forms instead of being hidden
File Structure and Responsibilities
Core Files
1. Constants and Types
src/constants/blocks.ts
├── BLOCKS.FORM = 'formProperties'
└── BLOCKS.FORM_INPUT = 'formInputProperties'
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/types.ts
├── FormProperties (form block type)
├── FormInputProperties (form input subblock type)
└── InputTypeDefinition (base input type interface)2. Input Type Definitions (Source of Truth)
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/form/inputTypes.ts
├── INPUT_TYPES (centralized metadata for all input types)
├── ExtendedInputTypeDefinition (extended type definition)
├── getInputTypeDefinition() (helper function)
└── getAllInputTypes() (helper function)This is the single source of truth for all input type metadata including:
- Type identifiers
- Display labels
- Icons (Material Design names)
- Short codes (FT, FE, FP, etc.)
- Validation field definitions
- Categories (text, number, selection, date)
- Descriptions
3. Configuration Components
src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/
├── FormConfiguration.vue (main form block config)
├── FormInputConfiguration.vue (form input subblock config)
└── components/form/
├── FormInputBaseSection.vue (shared base config)
└── InputLabel.vue (form-specific label component)4. Store Logic
src/store/modules/blocks.ts
├── addBlock mutation (handles form input creation with persistent numbering)
├── generateDefaultFormInputPayload1() (default input 1)
├── generateDefaultFormInputPayload2() (default input 2)
└── Form block creation logic5. Defaults and Data
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/defaults.ts
├── formInputDefaults (default form input properties)
└── formDefaults (default form properties)6. Creative Engine Rendering
Creative-Engine/src/components/creative/CreativeFormBlock/CreativeFormBlock.vue
├── Form block rendering component
├── Input type-specific rendering logic
└── Style application and CSS class generationInput Type System
Centralized Input Types
All input types are defined in inputTypes.ts with the following structure:
typescript
export const INPUT_TYPES: Record<string, ExtendedInputTypeDefinition> = {
text: {
type: 'text',
label: 'Text Input',
icon: 'text_fields',
shortCode: 'FT',
category: 'text',
validationFields: ['minLength', 'maxLength', 'pattern'],
description: 'Single-line text input for general text entry',
},
// ... other types
};Current Input Types
| Type | Label | Short Code | Category | Validation Fields |
|---|---|---|---|---|
| text | Text Input | FT | text | minLength, maxLength, pattern |
| textArea | Text Area | FA | text | minLength, maxLength |
| Email Input | FE | text | minLength, maxLength, pattern | |
| tel | Phone Input | FP | text | minLength, maxLength, pattern |
| number | Number Input | FN | number | min, max, step |
| date | Date Input | FD | date | min, max |
| select | Dropdown | FS | selection | options |
| checkbox | Checkbox | FC | selection | - |
| radio | Radio Buttons | FR | selection | options |
| range | Range Slider | FRS | number | min, max, step |
| clickoutLink | Clickout Link | FL | text | clickoutUrl |
Adding New Input Types
To add a new input type, only update inputTypes.ts:
typescript
export const INPUT_TYPES = {
// ... existing types
newType: {
type: 'newType',
label: 'New Input Type',
icon: 'new_icon_name',
shortCode: 'FNT',
category: 'text',
validationFields: ['customField'],
description: 'Description of new input type',
},
};The new type will automatically be available in:
- Form configuration UI
- Input type selectors
- Short code generation
- Display name generation
Form Block Architecture
Data Structure
Form Block
javascript
formBlock = {
blockName: 'formProperties-1',
blockType: 'formProperties',
formTitle: 'Contact Form',
submitButtonText: 'Submit',
successMessage: 'Thank you!',
errorMessage: 'Please check your entries',
inputs: [
{
/* full subblock object 'formInputProperties-1' */
},
{
/* full subblock object 'formInputProperties-2' */
},
],
// ... styling and layout properties
};Form Input Subblock
javascript
formInputBlock = {
blockName: 'formInputProperties-1',
blockType: 'formInputProperties',
parent: 'formProperties-1',
type: 'text',
typeIndex: 1, // Persistent per-type numbering
label: 'Text Input #1',
displayName: 'Text Input #1',
shortCode: 'FT',
placeholder: 'Enter text',
required: true,
order: 0,
validation: {
minLength: 2,
maxLength: 50,
},
// ... styling and layout properties
};Persistent Per-Type Numbering
Form inputs use a persistent numbering system:
- typeIndex: Stable identifier per input type (1, 2, 3...)
- displayName: Includes type label and index (
Text Input #1) - shortCode: From centralized input types (
FT,FE, etc.) - Numbers don't change on reorder: Maintains consistency for styling and analytics
Implementation in Store
javascript
// In blocks.ts addBlock mutation for FORM_INPUT
const sameTypeCount = parentBlock.inputs.filter((input) => input.type === type).length;
const typeIndex = sameTypeCount + 1;
const inputTypeDef = getInputTypeDefinition(type);
const displayName = `${inputTypeDef.label} #${typeIndex}`;
const subblock = {
...defaults,
...additionalProperties,
// Authoritative fields (cannot be overridden)
parent: targetBlockPath,
blockType: BLOCKS.FORM_INPUT,
type,
typeIndex,
displayName,
label: displayName,
shortCode: inputTypeDef.shortCode,
blockName: uniqueBlockName,
};Configuration Components
FormConfiguration.vue
Manages the main form block settings and subblock list:
- Form Properties: Title, button text, messages
- Styling: Background, border, layout
- Input Management: Add, remove, reorder form inputs
- Dynamic Input Types: Uses
getAllInputTypes()for type options
Key methods:
addFormInput(type): Creates new input subblock with persistent numberingremoveFormInput(index): Removes input and updates orderrenumberInputDisplayNames(): Ensures consistent numbering across types
FormInputConfiguration.vue
Manages individual form input settings:
- Base Properties: Label, placeholder, type, required
- Validation: Type-specific validation rules
- Styling: Background, border, font, layout
- Type-Specific UI: Conditional sections based on input type
Key computed properties:
inputTypeDefinition: Gets metadata from centralized typeshasValidationFields: Determines if validation UI should showshortCodeDisplay: Generates display badge with type and number
Creative Engine Rendering
CreativeFormBlock.vue
Renders the complete form with all inputs:
vue
<template>
<div class="form-block">
<h2 v-if="typedBlock.formTitle">{{ typedBlock.formTitle }}</h2>
<div
v-for="input in formInputs"
:key="input.blockName"
:class="getInputWrapperClass(input)"
:style="getInputWrapperStyles(input)"
>
<!-- Input type-specific rendering -->
<input
v-if="input.type === 'text'"
:id="getInputId(input)"
:name="getInputName(input)"
:placeholder="input.placeholder"
:style="getInputFieldStyles(input)"
type="text"
/>
<textarea
v-else-if="input.type === 'textArea'"
:id="getInputId(input)"
:name="getInputName(input)"
:placeholder="input.placeholder"
:style="getInputFieldStyles(input)"
/>
<!-- Other input types... -->
</div>
<button type="submit">{{ typedBlock.submitButtonText }}</button>
</div>
</template>Key methods:
formInputs: Computed property that orders inputs usingtypedBlock.inputsarraygetInputWrapperClass(): Generates semantic CSS classes (fi--ft1,fi--fe2)getInputId(): Creates semantic IDs based on display namegetInputFieldStyles(): Applies input-specific styling
Styling System
CSS Class Generation
Form inputs get semantic CSS classes for styling:
css
.form-input.fi--ft1 {
/* Text Input #1 */
}
.form-input.fi--fe2 {
/* Email Input #2 */
}
.form-input.fi--fd1 {
/* Date Input #1 */
}Pattern: fi--{shortCode}{typeIndex} (lowercase)
Style Properties
Form inputs support comprehensive styling:
Wrapper Styles
- Background (color, gradient, image)
- Border (width, style, color)
- Padding and margin
- Width and height
- Alignment
Input Field Styles
- Font properties (family, size, weight, color)
- Border and border radius
- Padding
- Background color
Label Styles
- Font properties
- Text alignment
- Color
i18n Integration
Form-related translations are organized in en.js:
javascript
visuals: {
blocks: {
formProperties: 'Form',
formInputProperties: 'Form Input',
},
form: {
label: 'Label',
placeholder: 'Placeholder',
type: 'Input Type',
required: 'Required field',
validation: {
title: 'Validation',
minLength: 'Minimum Length',
// ... other validation labels
},
inputTypes: {
text: 'Text',
email: 'Email',
// ... other type labels
},
},
}Development Workflow
Adding New Input Types
- Update
inputTypes.tswith new type definition - Add i18n entries if needed for type-specific labels
- Add type-specific configuration in
FormInputConfiguration.vue(optional) - Update Creative Engine rendering in
CreativeFormBlock.vue - Test in form builder and preview
Modifying Existing Types
- Update metadata in
inputTypes.ts - Update configuration UI if properties changed
- Update rendering logic if needed
- Update type definitions in both AF and CE if new properties added
Debugging Common Issues
Form Inputs Not Displaying
- Check
getSubBlocksutility is working - Verify
inputsarray contains correct objects - Ensure subblocks have correct
parentproperty
Type-Specific Configuration Not Showing
- Check
inputTypeDefinitioncomputed property - Verify
validationFieldsarray ininputTypes.ts - Ensure conditional templates use correct type/category
Styling Not Applied
- Check payload being sent to CE
- Verify style methods in
CreativeFormBlock.vue - Use browser dev tools to inspect rendered elements
Best Practices
1. Use Centralized Types
- Always use
getInputTypeDefinition()andgetAllInputTypes() - Never hardcode input type metadata
- Update only
inputTypes.tswhen adding new types
2. Maintain Persistent Numbering
- Don't override
typeIndex,displayName, orshortCodein UI - Let the store manage authoritative fields
- Use
renumberInputDisplayNames()when needed
3. Follow Specialized Block Pattern
- Keep inputs as subblocks, not visual elements
- Use
targetBlockPathwhen creating subblocks - Maintain
inputsarray with full objects
4. Consistent Styling
- Use semantic CSS classes with short codes
- Apply styles through proper style methods
- Preserve units (px, %, etc.) in style properties
5. i18n Everything
- Use
$t()for all user-facing text - Add new strings to i18n files
- Keep translations organized by feature
Migration Notes
This system has been consolidated from multiple previous implementations. Key changes:
- Centralized input types replace multiple hardcoded mappings
- Consistent short codes use
Fprefix pattern - Unified documentation replaces fragmented docs
- Single source of truth for all input type metadata
When working with forms, always refer to this guide and the centralized inputTypes.ts file for the most current implementation details.