Skip to content

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 a type property 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 lines
    • removeFormInput(): Simplified parameter handling and logic
    • updateFormInput(): Streamlined to 3 lines

Bug Fixes

  • Fixed circular reference: Removed formInputDefaults from formDefaults.inputs to prevent crashes
  • Fixed parameter order: Corrected updateValue('inputs', inputs) parameter sequence
  • Fixed border defaults: Set border: false in 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 logic

5. 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 generation

Input 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

TypeLabelShort CodeCategoryValidation Fields
textText InputFTtextminLength, maxLength, pattern
textAreaText AreaFAtextminLength, maxLength
emailEmail InputFEtextminLength, maxLength, pattern
telPhone InputFPtextminLength, maxLength, pattern
numberNumber InputFNnumbermin, max, step
dateDate InputFDdatemin, max
selectDropdownFSselectionoptions
checkboxCheckboxFCselection-
radioRadio ButtonsFRselectionoptions
rangeRange SliderFRSnumbermin, max, step
clickoutLinkClickout LinkFLtextclickoutUrl

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 numbering
  • removeFormInput(index): Removes input and updates order
  • renumberInputDisplayNames(): 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 types
  • hasValidationFields: Determines if validation UI should show
  • shortCodeDisplay: 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 using typedBlock.inputs array
  • getInputWrapperClass(): Generates semantic CSS classes (fi--ft1, fi--fe2)
  • getInputId(): Creates semantic IDs based on display name
  • getInputFieldStyles(): 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

  1. Update inputTypes.ts with new type definition
  2. Add i18n entries if needed for type-specific labels
  3. Add type-specific configuration in FormInputConfiguration.vue (optional)
  4. Update Creative Engine rendering in CreativeFormBlock.vue
  5. Test in form builder and preview

Modifying Existing Types

  1. Update metadata in inputTypes.ts
  2. Update configuration UI if properties changed
  3. Update rendering logic if needed
  4. Update type definitions in both AF and CE if new properties added

Debugging Common Issues

Form Inputs Not Displaying

  • Check getSubBlocks utility is working
  • Verify inputs array contains correct objects
  • Ensure subblocks have correct parent property

Type-Specific Configuration Not Showing

  • Check inputTypeDefinition computed property
  • Verify validationFields array in inputTypes.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() and getAllInputTypes()
  • Never hardcode input type metadata
  • Update only inputTypes.ts when adding new types

2. Maintain Persistent Numbering

  • Don't override typeIndex, displayName, or shortCode in UI
  • Let the store manage authoritative fields
  • Use renumberInputDisplayNames() when needed

3. Follow Specialized Block Pattern

  • Keep inputs as subblocks, not visual elements
  • Use targetBlockPath when creating subblocks
  • Maintain inputs array 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 F prefix 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.

Internal documentation