Question Types

This document describes all question types supported by the registration workflow, including their backend implementation, frontend rendering, validation rules, and example use cases.

1. Overview

The registration workflow presents a series of questions to users during the membership or event registration process. Each question is rendered based on its type, which determines the UI component used and the validation rules applied.

1.1. Development Phases

Phase Period Scope

P1

Past 2 years

Initial implementation (complete)

P2

Dec 2025 - Mar 2026

Current work - 11 new question types

P3

After Mar 2026

Future enhancements

1.2. FormController Inheritance Model

Question types are registered in FormController classes that form an inheritance hierarchy:

formcontroller-hierarchy

1.3. Layer 3 Component Architecture

Questions are displayed using a Layer 3 Question Panel component that sits within the existing page structure:

  • Layer 1: Site frame (navbar + page container)

  • Layer 2: Functional area template (membership/event details panel)

  • Layer 3: Question panel (question header + person-answer table + navigation)

The question panel follows a consistent layout:

question-panel-layout
P2 enhances the person info column to include DOB, age, gender, and category (context-dependent) in addition to the person name.

1.4. StepCode vs UI_CODE Mapping

The backend uses StepCode to define question logic, while the frontend uses UI_CODE to determine rendering. Multiple StepCodes can map to the same UI_CODE when they share the same visual presentation but differ in business logic.

1.4.1. Base FormController Questions (Generic)

StepCode UI_CODE Description Status

TXT

TXT

Free text with length/pattern validation

P1 ✓

SEL

SEL

Select one option from list

P1 ✓

RBO

RAD

Radio button options

P1 ✓

BCB

BCB

Binary checkbox (yes/no)

P1 ✓

ITC

ITC

Terms & conditions acceptance

P1 ✓

ONE

ONE

Radio button single selection (pick one person)

P1 ✓

CON

CON

Communication preference (Email/SMS/WhatsApp)

P2

PHN

PHN

Phone with country code + number

P2

EMC

EMC

Emergency contact (name + phone)

P2

CLV

SEL

Select from configured custom list

P2

1.4.2. MembershipFormController Questions (Membership-Specific)

StepCode UI_CODE Description Status

MFA

BCB

Select adults for family membership

P1 ✓

MFC

BCB

Select children for family membership

P1 ✓

MFM

ONE

Select primary family member

P1 ✓

MCS

RAD

Opt-in for cross-sell membership

P1 ✓

1.4.3. EventFormController Questions (Event-Specific)

StepCode UI_CODE Description Status

CSI

CSI

CSA/UCI ID number capture

P2

CSM

CSM

Validate CSA membership status

P2

CSL

CSL

Validate CSA racing license

P2

ECA

SEL

Select event category (age/gender filtered)

P2

ERT

SEL

Select race types to compete in (multi-select)

P2

NSR

ESR

Select/replace race number

P2

TSR

ESR

Select/replace timing tag/chip

P2

1.5. Question Type Summary

Category P1 Complete P2 New

Base FormController

6

4

MembershipFormController

4

0

EventFormController

0

7

Total

10

11

2. Core Question Types

2.1. TXT - Text Input

Purpose: Capture free text responses from each person.

Backend:

  • Class: FreeTextFormField.java

  • StepCode: TXT

  • UI_CODE: TXT

Frontend Rendering:

  • Component: Text input field per person

  • Layout: Person name on left, text input on right

Validation:

  • Required check (if configured)

  • Minimum/maximum length (valueMin, valueMax)

  • Regex pattern matching (valuePattern)

Example Use Cases:

  • Emergency contact number

  • Medical notes

  • Special dietary requirements

DTO Example:

{
  "title": "Emergency Contact Number",
  "questionType": "TXT",
  "required": true,
  "people": [
    { "id": 1, "firstName": "Sarah", "lastName": "Smith", "answer": null },
    { "id": 2, "firstName": "Billy", "lastName": "Smith", "answer": null }
  ]
}

2.2. SEL - Dropdown Selection

Purpose: Allow users to select one option from a predefined list per person.

Backend:

  • Class: DropDownOptionFormField.java

  • StepCode: SEL

  • UI_CODE: SEL

Frontend Rendering:

  • Component: Dropdown/select element per person

  • Layout: Person name on left, dropdown on right

  • Options populated from person.options array

Validation:

  • Required check (if configured)

  • Answer must match one of the available options

Example Use Cases:

  • T-shirt size selection

  • School selection

  • Experience level

  • Communication preference

DTO Example:

{
  "title": "T-Shirt Size",
  "questionType": "SEL",
  "required": true,
  "people": [
    {
      "id": 1,
      "firstName": "Sarah",
      "lastName": "Smith",
      "answer": null,
      "options": [
        { "id": "XS", "value": "XS (Extra Small)" },
        { "id": "S", "value": "S (Small)" },
        { "id": "M", "value": "M (Medium)" },
        { "id": "L", "value": "L (Large)" },
        { "id": "XL", "value": "XL (Extra Large)" }
      ]
    }
  ]
}

Mockups:


2.3. BCB - Binary Checkbox

Purpose: Capture a yes/no response per person.

Backend:

  • Class: CheckBoxBooleanFormField.java

  • StepCode: BCB

  • UI_CODE: BCB

Frontend Rendering:

  • Component: Single checkbox per person

  • Layout: Checkbox with person name as label

  • Answer values: "0" (unchecked) or "1" (checked)

Validation:

  • Boolean validation (answer must be "0" or "1")

  • Does NOT enforce required flag

Example Use Cases:

  • Medical conditions declaration

  • Photo consent

  • Newsletter subscription

DTO Example:

{
  "title": "Medical Conditions",
  "description": "Does anyone have any medical conditions we should be aware of?",
  "questionType": "BCB",
  "required": false,
  "people": [
    {
      "id": 1,
      "firstName": "Sarah",
      "lastName": "Smith",
      "answer": "0",
      "options": [{ "id": "1", "value": "Yes, I have medical conditions" }]
    }
  ]
}

2.4. ONE - Radio Single Selection

Purpose: Allow selection of exactly one person from the group.

Backend:

  • Class: RadioButtonPickOneFormField.java

  • StepCode: ONE

  • UI_CODE: ONE

Frontend Rendering:

  • Component: Radio button group (all share same name attribute)

  • Layout: Radio button with person name as label

  • Only one person can have answer "1" at a time

Validation:

  • At least one person must have answer "1" (if required)

  • All other persons automatically set to "0"

Example Use Cases:

  • Primary contact selection

  • Main account holder

  • Team captain selection

DTO Example:

{
  "title": "Primary Contact Person",
  "description": "Who will be the primary contact person for this membership?",
  "questionType": "ONE",
  "required": true,
  "people": [
    { "id": 1, "firstName": "Sarah", "lastName": "Smith", "answer": "1" },
    { "id": 2, "firstName": "Billy", "lastName": "Smith", "answer": "0" }
  ]
}

2.5. ITC - Terms & Conditions

Purpose: Require acceptance of terms and conditions from all members.

Backend:

  • Class: IndemnityTermsConditionsFormField.java

  • StepCode: ITC

  • UI_CODE: ITC

Frontend Rendering:

  • Component: Master checkbox + individual checkboxes

  • Layout: Scrollable terms content, master "Accept for all", individual acceptance per person

  • Master checkbox propagates to all individual checkboxes

Validation:

  • All persons must have answer "1" to proceed

  • Does NOT enforce required flag (validation is always applied)

Special Behavior:

  • Terms text displayed in scrollable container

  • Master checkbox toggles all individual checkboxes

  • Individual checkboxes update master state

  • Not included in summary data collection

DTO Example:

{
  "title": "Terms and Conditions",
  "description": "Please read and accept the terms and conditions.",
  "questionType": "ITC",
  "required": true,
  "infoURL": "https://example.com/terms",
  "people": [
    { "id": 1, "firstName": "Sarah", "lastName": "Smith", "answer": "1" },
    { "id": 2, "firstName": "Billy", "lastName": "Smith", "answer": "1" }
  ]
}

3. Membership-Specific Question Types

These question types are used specifically for family membership workflows. They render using standard UI components but have custom business logic.

3.1. MFA - Family Adult Selection

Purpose: Select which adults will be included in a family membership.

Backend:

  • Class: MembershipAdultSelectFormField.java

  • StepCode: MFA

  • UI_CODE: BCB (renders as checkbox)

Visibility Rules:

  • Only shown for adults (18+)

  • Only shown when family membership is possible

Validation:

  • Ensures exactly 2 adults are selected for family membership

  • Not included in summary data

Example Context:

When registering multiple people and family membership pricing applies, this question allows users to select which 2 adults will be part of the family unit.


3.2. MFC - Family Child Selection

Purpose: Select which children will be included in a family membership.

Backend:

  • Class: MembershipChildSelectFormField.java

  • StepCode: MFC

  • UI_CODE: BCB (renders as checkbox)

Visibility Rules:

  • Only shown for children (under 18)

  • Only shown when family membership is possible

Validation:

  • Children selection is optional

  • Not included in summary data

Example Context:

After selecting family adults, users can choose which children to include in the family membership at no additional cost.


3.3. MFM - Family Main Member

Purpose: Select the primary adult for the family membership.

Backend:

  • Class: MembershipMainSelectFormField.java

  • StepCode: MFM

  • UI_CODE: ONE (renders as radio button)

Visibility Rules:

  • Only shown for selected family adults

  • Only shown after MFA step completes

Validation:

  • Exactly one main member must be selected

  • Not included in summary data

Example Context:

After selecting family adults, users must designate one as the primary member who will receive the main membership number and communications.

4. Interactive UI Prototypes

The following interactive HTML prototypes demonstrate the visual design and interaction patterns for question screens.

Screen StepCode UI_CODE Prototype

Text Input

TXT

TXT

View

Binary Checkbox

BCB

BCB

View

Dropdown Selection

SEL

SEL

View

Dropdown (T-Shirt)

SEL

SEL

View

Dropdown (School)

SEL

SEL

View

Radio Single Select

ONE

ONE

View

Terms & Conditions

ITC

ITC

View

Family Adult Selection

MFA

BCB

View

Family Child Selection

MFC

BCB

View

Family Main Member

MFM

ONE

View

These prototypes are interactive HTML files that demonstrate the intended user experience. Navigate between screens using the Back/Next buttons.

5. Backend Implementation Reference

5.1. FormField Class Hierarchy

formfield-hierarchy

5.2. Source Files

File Path

FormField (Base)

admin-service/src/main/java/za/co/idealogic/event/admin/form/FormField.java

FreeTextFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/FreeTextFormField.java

DropDownOptionFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/DropDownOptionFormField.java

CheckBoxBooleanFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/CheckBoxBooleanFormField.java

RadioButtonPickOneFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/RadioButtonPickOneFormField.java

IndemnityTermsConditionsFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/IndemnityTermsConditionsFormField.java

MembershipAdultSelectFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/MembershipAdultSelectFormField.java

MembershipChildSelectFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/MembershipChildSelectFormField.java

MembershipMainSelectFormField

admin-service/src/main/java/za/co/idealogic/event/admin/form/MembershipMainSelectFormField.java

6. P2 Question Types - Generic (Base FormController)

These question types are available to both Membership and Event registration workflows.

6.1. CON - Communication Preference

Purpose: Capture preferred communication channel (Email, SMS, or WhatsApp) per person.

Backend:

  • Class: CommunicationPreferenceFormField.java (P2)

  • StepCode: CON

  • UI_CODE: CON

Frontend Rendering:

  • Component: Toggle buttons for channel type + conditional input (email or phone)

  • Layout: Person info on left, channel selection + value input on right

  • Optional "Apply to all persons" checkbox for bulk application

Validation:

  • Channel type required (EMAIL, SMS, WHATSAPP)

  • If EMAIL: valid email format

  • If SMS/WHATSAPP: valid phone format with country code

Data Model:

{
  "type": "EMAIL|SMS|WHATSAPP",
  "value": "[email protected]",
  "countryCode": "+27"  // For SMS/WhatsApp only
}

6.2. PHN - Phone Number

Purpose: Capture phone number with country code per person.

Backend:

  • Class: PhoneFormField.java (P2)

  • StepCode: PHN

  • UI_CODE: PHN

Frontend Rendering:

  • Component: Country code dropdown + phone number input

  • Layout: Person info on left, phone input on right

  • Default country code: +27 (South Africa)

Validation:

  • Country code required

  • Phone number format (digits, optional dashes/spaces)

Data Model:

{
  "countryCode": "+27",
  "number": "0821234567"
}

6.3. EMC - Emergency Contact

Purpose: Capture emergency contact name and phone number per person.

Backend:

  • Class: EmergencyContactFormField.java (P2)

  • StepCode: EMC

  • UI_CODE: EMC

Frontend Rendering:

  • Component: Name input + phone input (reuses PHN component)

  • Layout: Person info on left, contact details on right

Validation:

  • Contact name required

  • Phone number required (reuses PHN validation)

Data Model:

{
  "name": "John Doe",
  "phone": {
    "countryCode": "+27",
    "number": "0821234567"
  }
}

6.4. CLV - Custom List Value

Purpose: Allow selection from a configurable custom list (T-Shirt sizes, Clubs, Schools, etc.).

Backend:

  • Class: CustomListValueFormField.java (P2)

  • StepCode: CLV

  • UI_CODE: SEL (renders as standard dropdown)

Frontend Rendering:

  • Component: Standard dropdown (reuses SEL component)

  • Layout: Person info on left, dropdown on right

  • Options populated from CustomListValue entities

Configuration:

  • ProcessStep references which CustomList to use

  • Options dynamically loaded based on list configuration

Validation:

  • Required selection from list

Data Model:

{
  "customListId": 123,
  "valueId": 456,
  "value": "Medium"
}

7. P2 Question Types - Event-Specific (EventFormController)

These question types are specific to event registration workflows.

7.1. CSI - CSA/UCI Identification

Purpose: Capture participant’s CSA or UCI identification number.

Backend:

  • Class: CSAIdentificationFormField.java (P2)

  • StepCode: CSI

  • UI_CODE: CSI

Frontend Rendering:

  • Component: Toggle buttons (CSA Number / UCI Number / None) + conditional number input

  • Layout: Person info (name, DOB, age, gender) on left, ID capture on right

Validation:

  • If type != NONE, number is required

  • CSA number format validation

  • UCI number format validation

Data Model:

{
  "type": "CSA|UCI|NONE",
  "number": "123456"
}

7.2. CSM - CSA Membership Check

Purpose: Validate participant has valid CSA membership for event dates.

Backend:

  • Class: CSAMembershipCheckFormField.java (P2)

  • StepCode: CSM

  • UI_CODE: CSM

Frontend Rendering:

  • Component: Status display with conditional options for non-compliant participants

  • Layout: Person info on left, status/options on right

Compliance Configuration (per-category or event level):

  • strictCompliance: If true, no deferment allowed

  • dayLicenseAllowed: If true, can purchase day license

Compliance Matrix:

Strict Day License Available Options

false

true

Day License, Defer, Remove

false

false

Defer, Remove

true

true

Day License, Remove

true

false

Remove only (championship events)

Flow Logic:

  1. Check CSA membership status via CSA API

  2. If VALID → proceed to next step

  3. If INVALID → present options based on configuration

Data Model:

{
  "status": "VALID|DAY_LICENSE|DEFERRED|REMOVED",
  "membershipNumber": "123456",
  "dayLicenseOrderId": "order-xyz",
  "deferUntil": "2026-03-15T00:00:00Z"
}

7.3. CSL - CSA License Check

Purpose: Validate participant has valid racing license for discipline/event.

Backend:

  • Class: CSALicenseCheckFormField.java (P2)

  • StepCode: CSL

  • UI_CODE: CSL

Prerequisites:

  • CSM (membership check) must be VALID or DAY_LICENSE

  • CSL always follows CSM in step order

Frontend Rendering:

  • Component: Status display with conditional options (similar to CSM)

  • Layout: Person info (including category) on left, status/options on right

Flow Logic:

  1. Verify CSM status (implicit membership requirement)

  2. Check racing license for discipline on event dates via CSA API

  3. If VALID → proceed

  4. If INVALID → present options (same as CSM)

Data Model:

{
  "status": "VALID|DAY_LICENSE|DEFERRED|REMOVED",
  "licenseNumber": "RC-2025-1234",
  "discipline": "Road Cycling",
  "dayLicenseOrderId": "order-xyz",
  "deferUntil": "2026-03-15T00:00:00Z"
}

7.4. ECA - Event Category Selection

Purpose: Select event category based on participant age/gender.

Backend:

  • Class: EventCategoryFormField.java (P2)

  • StepCode: ECA

  • UI_CODE: SEL (renders as dropdown with special logic)

Frontend Rendering:

  • Component: Dropdown (extends SEL) with filtered options

  • Layout: Person info on left, category dropdown on right

  • Special handling for no eligible categories

Logic:

  1. Filter categories by participant’s age and gender

  2. If 0 matching → Show "Remove participant" option

  3. If 1 matching → Auto-select, show confirmation

  4. If 2+ matching → User must select

Data Model:

{
  "categoryId": 123,
  "categoryName": "Elite Men 35-39"
}

7.5. ERT - Event Race Type Selection

Purpose: Select which races participant will compete in (multi-select).

Backend:

  • Class: EventRaceTypeFormField.java (P2)

  • StepCode: ERT

  • UI_CODE: SEL (multi-select variant)

Frontend Rendering:

  • Component: Multi-select checkbox list

  • Layout: Person info on left, race type checkboxes on right

  • Options filtered by selected category

Validation:

  • At least one race type required

Data Model:

{
  "raceTypes": [
    { "id": 1, "name": "Scratch Race" },
    { "id": 2, "name": "Match Sprint" },
    { "id": 4, "name": "Elimination" }
  ]
}

7.6. NSR - Number Select/Replacement

Purpose: Select preferred race number or request replacement.

Backend:

  • Class: NumberSelectFormField.java (P2)

  • StepCode: NSR

  • UI_CODE: ESR (shared Equipment Select/Replace component)

Frontend Rendering:

  • Component: ESR (Equipment Select/Replace) - shared with TSR

  • Layout: Person info on left, equipment options on right

  • Header text configurable per equipment type

Logic:

  • Shows participant’s previously assigned numbers from past events

  • Allows selection of existing number or request for new/replacement

Data Model:

{
  "action": "USE_EXISTING|REPLACEMENT|NEW",
  "selectedNumberId": 123,
  "numberValue": "127",
  "replacementReason": "Lost number board"
}

7.7. TSR - Tag Select/Replacement

Purpose: Select preferred timing tag/chip or request replacement.

Backend:

  • Class: TagSelectFormField.java (P2)

  • StepCode: TSR

  • UI_CODE: ESR (shared Equipment Select/Replace component)

Frontend Rendering:

  • Component: ESR (Equipment Select/Replace) - shared with NSR

  • Layout: Person info on left, equipment options on right

  • Header text configurable per equipment type

Logic:

  • Shows participant’s previously assigned timing tags

  • Allows selection of existing tag, request replacement, or rental

Data Model:

{
  "action": "USE_EXISTING|REPLACEMENT|RENTAL|NEW",
  "selectedTagId": 456,
  "tagNumber": "A001234",
  "replacementReason": "Tag expired"
}

8. Answer Data Migration (P2)

P2 introduces structured JSON answers to support complex data types.

8.1. Storage Design

  • Column: data_value increased from VARCHAR(100) to VARCHAR(500)

  • Format: JSON strings parsed using typed DTOs

8.2. Backward Compatibility

Data Type P1 Storage P2 Storage Detection

TXT, BCB, SEL

"John", "1", "123"

Same

Not JSON

CON, PHN, EMC, etc.

N/A

{"type":…​}

Starts with {

8.3. Answer DTOs

// PHN - Phone Number
public record PhoneAnswer(
    String countryCode,
    String number
) {}

// CON - Communication Preference
public record CommunicationPreferenceAnswer(
    CommunicationType type,  // EMAIL, SMS, WHATSAPP
    String value,
    String countryCode       // Optional, for SMS/WhatsApp
) {}

// CSI - License Identification
public record LicenseAnswer(
    LicenseType type,  // CSA, UCI, NONE
    String number      // Optional, null for NONE
) {}

// EMC - Emergency Contact
public record EmergencyContactAnswer(
    String name,
    PhoneAnswer phone
) {}