Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Version: 1.1
Created: 2026-01-28
Last Updated: 2026-04-14
Related Documents:
  • specs/_templates/WIZARD_SPEC_TEMPLATE.md - Specification template
  • specs/pf/specs/PF-41-configurable-module-wizards.md - PF-41 infrastructure spec
  • src/platform/wizards/README.md - Platform wizard implementation
  • docs/development/WIZARD_WORKFLOW_RECOMMENDATIONS.md - Workflow recommendations

Quick Start

1. Create Wizard Specification

# Use the wizard template
create-spec --core hr --name leave-request-wizard --type wizard

# Or let auto-detection prompt you
create-spec --core hr --name leave-request-wizard
# → "Name contains 'wizard'. Use WIZARD_SPEC_TEMPLATE.md? (Y/n)"
Output: specs/hr/ux/HR-UX-##-leave-request-wizard.md

2. Validate Specification

validate-spec --file specs/hr/ux/HR-UX-04-leave-request-wizard.md --type wizard

3. Generate Implementation Tasks

generate-tasks HR-UX-04

4. Implement and Test

Follow the implementation plan in the spec, using PF-41 infrastructure.

Wizard Development Lifecycle

┌─────────────────────────────────────────────────────────────────┐
│                  WIZARD DEVELOPMENT LIFECYCLE                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. SPECIFICATION         2. PLANNING          3. IMPLEMENTATION │
│  ┌──────────────┐        ┌──────────────┐     ┌──────────────┐  │
│  │ Create spec  │───────▶│ Choose PF-41 │────▶│ Build steps  │  │
│  │ using wizard │        │ approach     │     │ & components │  │
│  │ template     │        │              │     │              │  │
│  └──────────────┘        └──────────────┘     └──────────────┘  │
│         │                       │                    │           │
│         ▼                       ▼                    ▼           │
│  ┌──────────────┐        ┌──────────────┐     ┌──────────────┐  │
│  │ Define steps │        │ Design data  │     │ Register     │  │
│  │ & validation │        │ model        │     │ with PF-41   │  │
│  └──────────────┘        └──────────────┘     └──────────────┘  │
│                                                      │           │
│                                                      ▼           │
│  4. TESTING               5. DEPLOYMENT        6. ANALYTICS     │
│  ┌──────────────┐        ┌──────────────┐     ┌──────────────┐  │
│  │ Unit tests   │───────▶│ Feature flag │────▶│ Track usage  │  │
│  │ E2E tests    │        │ System templ │     │ & completion │  │
│  │ RLS tests    │        │              │     │              │  │
│  └──────────────┘        └──────────────┘     └──────────────┘  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Phase 1: Specification

Using WIZARD_SPEC_TEMPLATE.md

The wizard specification template includes these key sections:
SectionPurposeRequired
Executive SummaryQuick overview of wizard
Business ContextWhy this wizard is needed
Objectives & Success CriteriaMeasurable goals
ScopeWhat’s in/out of scope
User StoriesUser-facing requirements
PF-41 Wizard ArchitectureIntegration approach
Wizard Step DefinitionsDetailed step specs
Navigation & FlowFlow diagram and rules
Data ModelWizard data types
Integration PointsAPI/hook integrations
UI/UX RequirementsDesign requirements
Testing StrategyTest scenarios
Analytics EventsTracking eventsRecommended
Implementation PlanPhased tasks

Step Definition Requirements

Each wizard step must document:
### Step N: [Step Name]

**Purpose:** [What this step accomplishes]

**Step Type:** `form` / `custom` / `review`

**Component ID (if custom):** `{core}.{feature}.{step}`

**Fields/Components:**
| Field | Type | Required | Validation |
|-------|------|----------|------------|
| field1 | text | Yes | min 2 chars |
| field2 | select | No | - |

**Help Content:**
- **Title:** [Help title]
- **Content:** [Help text]

**Tooltip Definitions:**
| Term | Tooltip Key | Content |
|------|-------------|---------|
| term1 | `wizard_term_definition` | [Text] |

**Navigation:**
- **Can Skip:** Yes/No - [Reason]
- **Can Go Back:** Yes/No
- **Next Step:** [Next step name]

**Mobile Considerations:**
- [ ] Touch targets ≥44x44px
- [ ] Form fields stack vertically on mobile
- [ ] Native pickers for date/time on mobile
- [ ] Validation errors visible without scrolling

Step Icons

Every wizard step SHOULD have an icon to improve visual scanning and user orientation. Icons appear inside the step circle indicator in all three layout variants (horizontal, timeline, sidebar).

Two Layers of Icon Support

LayerTypeWhere storedRendering
Presentational (WizardShellStep.icon)React component (ComponentType)In-memory step arrayRendered directly by WizardShell
Template JSON (WizardStep.icon)string key (e.g. 'user', 'calendar')pf_wizard_templates.steps JSONBResolved via resolveStepIcon() at runtime

Using Icons with WizardShell (Presentational)

Pass Lucide icon components directly in the steps array:
import { WizardShell } from '@/platform/wizards';
import { User, Calendar, ClipboardCheck } from 'lucide-react';

const steps = [
  { id: 'info', title: 'Personal Info', icon: User },
  { id: 'dates', title: 'Schedule', icon: Calendar },
  { id: 'review', title: 'Review', icon: ClipboardCheck },
];

<WizardShell steps={steps} layout="timeline" /* ... */ />

Using Icons in PF-41 Templates (JSON Config)

For configurable wizard templates stored in the database, use string icon keys from the WIZARD_STEP_ICONS catalog:
// Template step definition (JSON)
{
  "id": "step-1",
  "title": "Employee Info",
  "icon": "user",
  "component_type": "form",
  // ...
}
Resolve string keys to components using resolveStepIcon():
import { resolveStepIcon } from '@/platform/wizards';

const steps = template.steps.map((step) => ({
  id: step.id,
  title: step.title,
  icon: resolveStepIcon(step.icon),
}));

Available Icon Keys

The WIZARD_STEP_ICONS catalog in @/platform/wizards provides ~50 curated icons organized by category:
CategoryKeys
Peopleuser, user-plus, users, badge-check
Contactmail, phone, map-pin, home
Datescalendar, clock
Documentsfile-text, file-check, clipboard, upload, image-plus
Financewallet, banknote, credit-card, circle-dollar-sign
Workbriefcase, building, laptop, package
Healthstethoscope, heart
Securityshield, shield-check, lock
Settingssettings, target, sparkles
Wizard lifecyclereview, complete, confirm, summary, info
Adding new icons: Add to src/platform/wizards/utils/stepIcons.ts, update this table, and follow the Icon Guide. Healthcare examples: examples/wizard-templates/healthcare-icon-keys.json — copy-paste PF-41 snippets for stethoscope, pill, syringe. Unknown icon keys (fallback behavior): If a template references an icon key that isn’t in WIZARD_STEP_ICONS and doesn’t match a module code, resolveStepIcon() returns the FALLBACK_STEP_ICON (CircleHelp from Lucide) so the step indicator stays visible instead of silently collapsing to the step number. In development, a one-time console.warn per unknown key surfaces the typo to the author; the warning is stripped from production builds. Authors should still pick a real key from the catalog — the fallback is a safety net, not a sanctioned wildcard. An empty/missing icon field is treated as the intentional “show the step number” mode and does not trigger the fallback.

Icon Selection Rules

  1. Semantic relevance: Choose icons that represent the step’s purpose, not its position.
  2. Consistency: Use the same icon for the same concept across wizards (e.g. user for personal info, calendar for scheduling).
  3. Review/completion steps: Use review or complete — these are recognizable patterns.
  4. No icon is acceptable: Steps without an icon fall back to step number display.
  5. Accessibility: Step icons are decorative (step circle has aria-label with step title); no additional aria-label needed on icons.

Icon Display Behavior

StateWhat renders
Completed stepCheckmark (Check icon) — always overrides step icon
Current step with iconStep icon in primary border circle
Current step without iconStep number in primary border circle
Future step with iconStep icon in muted border circle
Future step without iconStep number in muted border circle

Phase 2: PF-41 Integration Decision

Decision Tree

Is this wizard...

├─ A standardized process that organizations should customize?
│   └─ YES → Use System Template approach

├─ Highly integrated with module-specific logic?
│   └─ YES → Use Module-Specific approach

└─ Both complex logic AND needs customization?
    └─ YES → Use Hybrid approach

Approach Comparison

AspectSystem TemplateModule-SpecificHybrid
Customizable✅ Yes❌ No✅ Partial
Marketplace✅ Yes❌ No⚠️ Limited
ImplementationPF-41 templatesCustom componentsMixed
MaintenanceLowerHigherMedium
Best ForOnboarding, setupComplex workflowsEnterprise

Implementation Patterns

// src/cores/{core}/pages/{Wizard}Page.tsx
import { ModuleWizardRenderer } from '@/platform/wizards';
import { useActiveWizard } from '@/platform/wizards';

export function LeaveRequestWizardPage() {
  const { data: template } = useActiveWizard({
    module: 'hr',
    wizardType: 'leave_request',
  });

  return (
    <ModuleWizardRenderer
      template={template}
      onComplete={handleComplete}
      onCancel={handleCancel}
    />
  );
}

Module-Specific

// src/cores/{core}/wizards/{wizard}/WizardPage.tsx
import { WizardRenderer } from '@/platform/forms/components/wizard';
import { Step1, Step2, ReviewStep } from './steps';

export function LeaveRequestWizard() {
  const [step, setStep] = useState(1);
  const [data, setData] = useState({});

  const steps = [
    { component: Step1, title: 'Leave Type' },
    { component: Step2, title: 'Dates' },
    { component: ReviewStep, title: 'Review' },
  ];

  return (
    <WizardRenderer
      steps={steps}
      currentStep={step}
      data={data}
      onStepChange={setStep}
      onDataChange={setData}
    />
  );
}

Hybrid

// Combine PF-41 form steps with custom components
import { ModuleWizardRenderer, registerWizardStep } from '@/platform/wizards';
import { CustomValidationStep } from './steps/CustomValidationStep';

// Register custom step
registerWizardStep({
  id: 'hr.leave.validation',
  module: 'hr',
  name: 'Custom Validation',
  component: CustomValidationStep,
});

// Template uses both form steps and custom step

Phase 3: Implementation

File Structure

src/cores/{core}/
├── wizards/
│   ├── register{Core}WizardSteps.ts    # Step registration (PF-41)
│   └── {wizard-name}/
│       ├── {WizardName}Page.tsx        # Wizard page component
│       ├── steps/
│       │   ├── Step1Component.tsx      # Custom step components
│       │   ├── Step2Component.tsx
│       │   └── ReviewStep.tsx
│       ├── hooks/
│       │   ├── useWizardData.ts        # Data fetching
│       │   └── useWizardValidation.ts  # Validation logic
│       └── types.ts                    # TypeScript types

Custom Step Component Pattern

// src/cores/hr/wizards/leave-request/steps/LeaveTypeStep.tsx
import { WizardStepProps } from '@/platform/wizards';
import { FormField, FormItem, FormLabel } from '@/shared/ui/form';
import { useLeaveTypes } from '@/cores/hr/hooks/useLeaveTypes';

interface LeaveTypeStepProps extends WizardStepProps {
  // Step-specific props
}

export function LeaveTypeStep({ 
  data, 
  onDataChange, 
  onValidate 
}: LeaveTypeStepProps) {
  const { data: leaveTypes, isLoading } = useLeaveTypes();

  const handleTypeSelect = (typeId: string) => {
    onDataChange({ ...data, leaveTypeId: typeId });
  };

  return (
    <div className="space-y-4">
      <FormField
        name="leaveType"
        render={({ field }) => (
          <FormItem>
            <FormLabel>Select Leave Type</FormLabel>
            {/* ... form control */}
          </FormItem>
        )}
      />
    </div>
  );
}

// Validation function
LeaveTypeStep.validate = (data: WizardFormData) => {
  const errors: ValidationErrors = {};
  if (!data.leaveTypeId) {
    errors.leaveTypeId = 'Please select a leave type';
  }
  return errors;
};

Step Registration (PF-41)

// src/cores/hr/wizards/registerHRWizardSteps.ts
import { registerWizardStep } from '@/platform/wizards';
import { lazy } from 'react';

export function registerHRWizardSteps() {
  // Leave Request Wizard Steps
  registerWizardStep({
    id: 'hr.leave.type-selection',
    module: 'hr',
    name: 'Leave Type Selection',
    description: 'Select leave type and view balance',
    component: lazy(() => import('./leave-request/steps/LeaveTypeStep')),
  });

  registerWizardStep({
    id: 'hr.leave.date-selection',
    module: 'hr',
    name: 'Date Selection',
    description: 'Select start and end dates',
    component: lazy(() => import('./leave-request/steps/DateSelectionStep')),
  });

  // ... additional steps
}

Draft Persistence

import { useWizardDraft } from '@/platform/wizards';

function MyWizard() {
  const { 
    draft, 
    saveDraft, 
    loadDraft, 
    clearDraft 
  } = useWizardDraft({
    wizardType: 'leave_request',
    userId: user.id,
  });

  // Auto-save on step change
  const handleStepComplete = (stepData: StepData) => {
    saveDraft({
      currentStep: step,
      data: { ...data, ...stepData },
    });
  };

  return (/* ... */);
}

Phase 4: Testing

Required Tests

Test TypeLocationCoverage Target
Unittests/unit/{core}/{wizard}/80%
Integrationtests/integration/{core}/{wizard}/Key flows
E2Etests/e2e/{core}/{wizard}/Happy path + errors
RLStests/rls/{core}-{wizard}/If new tables

Unit Test Example

// tests/unit/hr/leave-request-wizard/LeaveTypeStep.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { LeaveTypeStep } from '@/cores/hr/wizards/leave-request/steps/LeaveTypeStep';

describe('LeaveTypeStep', () => {
  it('validates leave type selection', () => {
    const errors = LeaveTypeStep.validate({});
    expect(errors.leaveTypeId).toBe('Please select a leave type');
  });

  it('passes validation with leave type selected', () => {
    const errors = LeaveTypeStep.validate({ leaveTypeId: 'vacation' });
    expect(errors).toEqual({});
  });

  it('shows available leave balances', async () => {
    render(<LeaveTypeStep data={{}} onDataChange={jest.fn()} />);
    expect(await screen.findByText('Vacation')).toBeInTheDocument();
    expect(await screen.findByText('16 hours available')).toBeInTheDocument();
  });
});

E2E Test Example

// tests/e2e/hr/leave-request-wizard.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Leave Request Wizard', () => {
  test('completes full wizard flow', async ({ page }) => {
    await page.goto('/hr/leave/request');

    // Step 1: Select leave type
    await page.click('[data-testid="leave-type-vacation"]');
    await page.click('[data-testid="next-button"]');

    // Step 2: Select dates
    await page.fill('[data-testid="start-date"]', '2026-02-15');
    await page.fill('[data-testid="end-date"]', '2026-02-20');
    await page.click('[data-testid="next-button"]');

    // Step 3: Add details
    await page.fill('[data-testid="notes"]', 'Family vacation');
    await page.click('[data-testid="next-button"]');

    // Step 4: Review and submit
    await expect(page.locator('[data-testid="summary-leave-type"]'))
      .toHaveText('Vacation');
    await page.click('[data-testid="submit-button"]');

    // Verify success
    await expect(page.locator('[data-testid="success-message"]'))
      .toBeVisible();
  });

  test('saves and resumes draft', async ({ page }) => {
    await page.goto('/hr/leave/request');

    // Complete step 1
    await page.click('[data-testid="leave-type-vacation"]');
    await page.click('[data-testid="next-button"]');

    // Navigate away
    await page.goto('/hr/dashboard');

    // Return to wizard
    await page.goto('/hr/leave/request');

    // Should resume at step 2
    await expect(page.locator('[data-testid="step-indicator"]'))
      .toHaveText('Step 2 of 4');
  });
});

Phase 5: Analytics Integration

Standard Events

All wizards should track these events:
import { trackWizardEvent } from '@/platform/wizards';

// Wizard started
trackWizardEvent('leave_request_wizard_started', {
  wizard_id: templateId,
  user_id: userId,
  device_type: isMobile ? 'mobile' : 'desktop',
  entry_point: 'dashboard_quick_action',
});

// Step completed
trackWizardEvent('leave_request_wizard_step_completed', {
  step_id: 'date-selection',
  step_name: 'Date Selection',
  time_spent_ms: 45000,
  fields_completed: 3,
});

// Wizard completed
trackWizardEvent('leave_request_wizard_completed', {
  total_time_ms: 120000,
  steps_completed: 4,
  steps_skipped: 0,
  entity_id: leaveRequestId,
});

PF-41 Built-in Analytics

When using PF-41 ModuleWizardRenderer, analytics are tracked automatically:
<ModuleWizardRenderer
  template={template}
  trackAnalytics={true}  // Default: true
  onComplete={handleComplete}
/>

Phase 6: Deployment

Feature Flags

Use feature flags for gradual rollout:
import { useWizardFeatureFlags } from '@/platform/wizards';

function LeaveRequestPage() {
  const { useConfigurableWizard } = useWizardFeatureFlags('hr');

  if (useConfigurableWizard) {
    return <ConfigurableLeaveWizard />;
  }
  return <LegacyLeaveForm />;
}

System Template Deployment

For PF-41 system templates, create a migration:
-- supabase/migrations/YYYYMMDDHHMMSS_add_leave_request_wizard_template.sql
INSERT INTO pf_wizard_templates (
  module,
  wizard_type,
  name,
  description,
  wizard_config,
  steps,
  is_template,
  is_active
) VALUES (
  'hr',
  'leave_request',
  'Leave Request Wizard',
  'Guided workflow for requesting time off',
  '{"progress_style": "stepper", "validation_mode": "on_advance"}',
  '[{"id": "step-1", "title": "Leave Type", "component_type": "custom", "component_id": "hr.leave.type-selection"}, ...]'::jsonb,
  true,  -- System template
  true   -- Active
);

Common Pitfalls

1. Missing PF-41 Integration Documentation

Problem: Spec doesn’t document PF-41 approach. Solution: Always include the “PF-41 Wizard Architecture” section, even for module-specific wizards:
## PF-41 Wizard Architecture

**Approach:** Module-Specific

**Rationale:** This wizard requires complex validation logic that integrates 
deeply with HR leave policy rules. A future enhancement will migrate to 
PF-41 hybrid approach for organization customization.

2. Incomplete Step Definitions

Problem: Steps missing validation rules, help content, or mobile considerations. Solution: Use the complete step template and run validate-spec --file <path> --type wizard to catch gaps.

3. No Draft Persistence

Problem: Users lose progress when navigating away. Solution: Always implement draft persistence using useWizardDraft or PF-41’s built-in draft management.

4. Missing Analytics

Problem: No tracking of wizard usage and drop-off points. Solution: Include the Analytics Events section in spec and implement tracking.

5. Poor Mobile Experience

Problem: Wizard unusable on mobile devices. Solution:
  • Use the mobile checklist per step
  • Test on actual mobile devices
  • Include mobile E2E tests

Commands Reference

CommandPurpose
create-wizard-specInteractive wizard UX spec creation (step prompts, icon selection, PF-41 decisions)
create-spec --type wizardQuick wizard spec creation from template
validate-spec --type wizardValidate wizard spec (icons, naming, validation pattern, PF-41, sections)
audit-wizard-specsAudit all wizard specs across cores with scoring (icons, naming, compliance)
recommend-wizardsAnalyze module specs for wizard candidates with scoring
generate-wizard-lovable-promptsGenerate Lovable browser walkthrough prompts from wizard specs
generate-tasksGenerate implementation tasks from spec
verify-taskVerify task completion
Full workflow:
recommend-wizards --core {core}

create-wizard-spec --core {core} --name {name} --based-on {SPEC-ID}

validate-spec --file specs/{core}/ux/{CORE}-UX-##-{name}.md --type wizard

generate-tasks {CORE}-UX-##

implement (using WizardShell / DialogWizardShell / ModuleWizardRenderer)

audit-wizard-specs --core {core}

Resources

  • Specification Template: specs/_templates/WIZARD_SPEC_TEMPLATE.md
  • PF-41 Spec: specs/pf/specs/PF-41-configurable-module-wizards.md
  • Platform Implementation: src/platform/wizards/README.md
  • Consolidation Plan: specs/cross-cutting/WIZARD-CONSOLIDATION-PLAN.md
  • Cross-Cutting Patterns: specs/cross-cutting/WIZARD-PATTERN.md
  • Cross-Cutting Rules: specs/cross-cutting/WIZARD-RULES.md
  • Selection Guide: docs/guides/use-cases/wizard-selection.md
  • UX Standard: docs/development/WIZARD_UX_STANDARD.md
  • Icon Guide: docs/development/ICON_GUIDE.md
  • Workflow Recommendations: docs/development/WIZARD_WORKFLOW_RECOMMENDATIONS.md
  • Analytics Guide: docs/pf/wizard-analytics-guide.md
  • Branching Guide: docs/pf/wizard-branching-guide.md
  • Marketplace Guide: docs/pf/wizard-marketplace-guide.md

Architecture Recommendations

Wizard Infrastructure Audit Findings (2026-04-14)

The platform currently has three distinct wizard rendering paths. This section documents known gaps, recommended consolidations, and standards to enforce going forward.

1. Consolidate Step Registration at Bootstrap

Wizard step registration should happen centrally in src/bootstrap/registerModuleWizardSteps.ts so module entry points do not race dynamic step availability. Current baseline (recommended pattern): register all core step modules in registerAllModuleWizardSteps():
// src/bootstrap/registerModuleWizardSteps.ts
const WIZARD_MODULES = [
  { id: 'fa.wizards', load: () => import('@/cores/fa/wizards/registerFAWizardSteps') },
  { id: 'ce.screening', load: () => import('@/cores/ce/wizards/screening/registerScreeningWizardSteps') },
  { id: 'ce.partner-contract', load: () => import('@/cores/ce/wizards/partner-contract/registerPartnerContractWizardSteps') },
  { id: 'ce.sequences', load: () => import('@/cores/ce/wizards/sequences/registerCeSequenceWizardSteps') },
  { id: 'gr.wizards', load: () => import('@/cores/gr/wizards/registerGRWizardSteps') },
  { id: 'gr.training-course', load: () => import('@/cores/gr/wizards/training-course/registerTrainingCourseWizardSteps') },
  { id: 'hr.wizards', load: () => import('@/cores/hr/components/wizards/registerHRWizardSteps') },
  { id: 'hr.payroll-wizards', load: () => import('@/cores/hr/wizards/registerHRPayrollWizardSteps') },
  { id: 'rh.wizards', load: () => import('@/cores/rh/components/wizards/registerRHWizardSteps') },
] as const;

export function registerAllModuleWizardSteps(): void {
  for (const moduleSpec of WIZARD_MODULES) {
    // Load each module independently so one failure cannot block app boot.
    void moduleSpec.load();
  }
}

2. Deprecate Custom Stepper UIs

Custom stepper usage should be triaged as either (a) migration target or (b) documented exception:
Custom stepperLocationRecommendation
PayrollWizardSteppersrc/cores/hr/components/payroll/Deprecated / removed (migrated to PF-41 renderer flows)
KioskProgressSteppersrc/cores/pm/components/kiosk/Migrated to WizardShell horizontal header
MigrationSteppersrc/platform/settings/pages/data-migration/Exception: tab/segmented navigation, not a sequential wizard
StepIndicator (inline)src/cores/pm/components/PreadmissionFormRenderer.tsxException: schema-driven section renderer, not PF-41 wizard navigation
ApprovalChainSteppersrc/cores/fw/components/approvals/ApprovalChainStepper.tsxException: approval-state tracker (read-only process visualization), not step navigation
PDSACycleDetail phase rowsrc/cores/gr/components/PDSACycleDetail.tsxException: business process phase viewer (Plan/Do/Study/Act), not a wizard
StepIndicator (inline)src/platform/organizations/OnboardingDialog.tsxMigrated to DialogWizardShell
New wizard builds MUST NOT create custom steppers. Allowed exceptions are limited to non-wizard patterns (parallel tabs, schema-section progress) and should be explicitly documented in spec/guide artifacts.

3. Icon String Resolution for Template Wizards

The WizardStep.icon field (JSON config) accepts a string, while WizardShellStep.icon (presentational) accepts a React component. The resolveStepIcon() utility bridges this gap. ModuleWizardRenderer and any code converting template steps to shell steps SHOULD use resolveStepIcon() to map string keys to components.

4. Standardize Wizard Validation Patterns

Three validation patterns exist across wizard implementations:
PatternUsed byRecommendation
PF-41 validation.rules on WizardStepTemplate-driven ModuleWizardRenderer flowsPreferred for form steps
StepComponentProps-driven validationCustom registerWizardStep componentsPreferred for new custom steps (static .validate() is legacy-compatible only)
Inline react-hook-form validationAd-hoc WizardShell / DialogWizardShell flowsAcceptable for shell-only implementations
Avoid mixing patterns within the same wizard. Template-driven wizards should use PF-41 validation exclusively.

5. Wizard Selection Decision Tree

Need a multi-step flow?

├─ Parallel tab navigation (non-sequential)?
│   └─ Not a wizard — use tabs / segmented controls

├─ Form data capture only (no business logic)?
│   └─ Use FW-31 MultiPageFormRenderer

├─ Guided business process with custom logic?
│   │
│   ├─ Organizations should customize steps?
│   │   └─ PF-41 System Template + ModuleWizardRenderer
│   │
│   ├─ Tightly coupled to module internals?
│   │   └─ WizardShell + custom step components
│   │
│   └─ Both customizable AND complex?
│       └─ PF-41 Hybrid (form + custom steps via registerWizardStep)

└─ Short dialog-based flow (≤4 steps)?
    └─ DialogWizardShell

6. Standard Step Naming Conventions

Steps across all wizards SHOULD follow these naming patterns for consistency:
Step purposeRecommended titleIcon key
Personal/demographic data”Personal Information”user
Contact details”Contact Information”mail
Employment/role details”Employment Details”briefcase
Address/location”Address”map-pin
Schedule/dates”Schedule”calendar
Financial/payment”Payment Information”wallet
Document upload”Documents”file-text
Credentials/compliance”Credentials”shield-check
Consent/agreements”Consent & Agreements”clipboard-check
Settings/config”Configuration”settings
Summary/review”Review & Submit”review
Success/completion”Complete”complete

Last Updated: 2026-05-13