Aptiwise
Aptiwise
Aptiwise DocumentationForm Layout Feature - Implementation CompleteForm Layout Feature Implementation Guide
Getting Started
User Guide
Workflow Types & Patterns

Form Layout Feature Implementation Guide

Implementation guide for the form layout feature with sections and grid-based layouts

Form Layout Feature Implementation Guide

Status: Backend Complete ✅ | Frontend Pending ⏳

Overview

This document provides the implementation plan for the form layout feature that allows users to organize workflow forms into sections with grid-based layouts and control field visibility per workflow step.

✅ Completed: Backend Implementation

1. Parser Updates (src/app/core/approvalml/parser.py)

New Models Added:

class ResponsiveLayout(BaseModel):
    tablet: Optional[int] = None  # Max columns on tablet
    mobile: Optional[int] = None  # Max columns on mobile

class FormSection(BaseModel):
    id: str
    title: str
    description: Optional[str] = None
    initial: bool = False
    grid: list[list[str]]  # Grid layout: rows with field names

class FormLayout(BaseModel):
    sections: list[FormSection]
    responsive: Optional[ResponsiveLayout] = None

ApprovalProcess Enhanced:

  • Added form_layout: Optional[FormLayout] field
  • Extracts layout from form.layout in YAML
  • Validates section references in workflow steps
  • Validates field references in section grids

WorkflowStep Enhanced:

  • Added view_sections: Optional[list[str]] - sections shown as readonly
  • Added edit_sections: Optional[list[str]] - sections shown as editable
  • Default behavior: if both are None, all sections shown in view mode

Field Types Added:

  • EMAIL = "email" - Email validation
  • RADIO = "radio" - Radio button group with optional display_as: "buttons"

Validation Logic:

  • Ensures section IDs are unique
  • Validates grid field references exist in form.fields
  • Validates workflow step section references exist in layout.sections
  • Validates responsive breakpoint values

2. AI Reference Updates (src/app/core/approvalml/syntax_reference.py)

Documentation Added:

  • Complete "Form Layout (Optional)" section with examples
  • Section visibility in workflow steps documentation
  • Updated field type list to include email and radio
  • Updated STEP_TYPES to include view_sections and edit_sections

3. Test Results

Example YAML successfully parses with validation:

✅ Parsing successful!
Workflow name: Employee Onboarding
Has layout: True
Number of sections: 3
  - personal_info: Employee Details (initial=True, rows=3)
  - it_setup: IT Equipment Setup (initial=False, rows=3)
  - hr_verification: HR Verification (initial=False, rows=2)
Responsive settings: tablet=2, mobile=1
Workflow steps:
  - it_provisioning: view=['personal_info'], edit=['it_setup']
  - hr_finalization: view=['personal_info', 'it_setup'], edit=['hr_verification']
✅ Validation: True

⏳ TODO: Frontend Implementation

Phase 1: Core Form Rendering Updates

File: frontend/src/components/workflows/DynamicFormHookForm.jsx

Current Behavior:

  • Renders fields in a simple list
  • No section grouping
  • No grid layout support

Required Changes:

  1. Add Layout Detection:
const hasLayout = workflow?.workflow_definition?.form_layout;
const formLayout = workflow?.workflow_definition?.form_layout;
  1. Create Section Renderer Component:
const FormSection = ({ section, fields, control, register, watch, setValue, errors, mode = 'edit' }) => {
  const sectionFields = {};

  // Build a map of field names to field definitions
  fields.forEach(field => {
    sectionFields[field.name] = field;
  });

  const isReadonly = mode === 'view';

  return (
    <div className="form-section mb-8">
      {/* Section Header */}
      <div className="section-header bg-gradient-to-r from-blue-50 to-blue-100 p-4 rounded-t-lg border-b-2 border-blue-200">
        <h3 className="text-lg font-semibold text-gray-800">{section.title}</h3>
        {section.description && (
          <p className="text-sm text-gray-600 mt-1">{section.description}</p>
        )}
      </div>

      {/* Section Content with Grid */}
      <div className="section-content bg-white p-6 rounded-b-lg border border-gray-200">
        {section.grid.map((row, rowIndex) => (
          <div
            key={rowIndex}
            className={`grid gap-4 mb-4 ${getGridClass(row.length, formLayout?.responsive)}`}
          >
            {row.map(fieldName => {
              const field = sectionFields[fieldName];
              if (!field) return null;

              return (
                <div key={fieldName} className="field-wrapper">
                  {renderField(field, {
                    control,
                    register,
                    watch,
                    setValue,
                    errors,
                    readonly: isReadonly
                  })}
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
};
  1. Grid CSS Class Generator:
const getGridClass = (columnCount, responsive) => {
  // Default desktop grid
  let gridClass = `grid-cols-${columnCount}`;

  // Apply responsive breakpoints
  if (responsive) {
    if (responsive.tablet) {
      gridClass += ` md:grid-cols-${Math.min(columnCount, responsive.tablet)}`;
    }
    if (responsive.mobile) {
      gridClass += ` sm:grid-cols-${Math.min(columnCount, responsive.mobile)}`;
    }
  }

  return gridClass;
};
  1. Main Render Logic Update:
// In DynamicFormHookForm main render
if (hasLayout) {
  // Determine which sections to show based on current step
  const sectionsToShow = getSectionsForCurrentStep();

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {sectionsToShow.map(({ section, mode }) => (
        <FormSection
          key={section.id}
          section={section}
          fields={allFields}
          control={control}
          register={register}
          watch={watch}
          setValue={setValue}
          errors={errors}
          mode={mode}
        />
      ))}
    </form>
  );
} else {
  // Fall back to current simple list rendering
  return (/* existing code */);
}
  1. Section Visibility Logic:
const getSectionsForCurrentStep = () => {
  const currentStep = getCurrentWorkflowStep(); // From context or props
  const layout = formLayout;

  if (!layout || !currentStep) {
    // Show all sections in edit mode if no step info
    return layout.sections.map(section => ({ section, mode: 'edit' }));
  }

  const result = [];

  // Add view sections
  if (currentStep.view_sections) {
    currentStep.view_sections.forEach(sectionId => {
      const section = layout.sections.find(s => s.id === sectionId);
      if (section) {
        result.push({ section, mode: 'view' });
      }
    });
  }

  // Add edit sections
  if (currentStep.edit_sections) {
    currentStep.edit_sections.forEach(sectionId => {
      const section = layout.sections.find(s => s.id === sectionId);
      if (section) {
        result.push({ section, mode: 'edit' });
      }
    });
  }

  // Default: if no sections specified, show all in view mode
  if (result.length === 0) {
    return layout.sections.map(section => ({ section, mode: 'view' }));
  }

  return result;
};

File: frontend/src/components/workflows/ApprovalPageUI.jsx

Required Changes:

  1. Update Field Display Logic:
  • Check if form has layout
  • Group fields by section
  • Render sections with banners
  • Respect readonly mode for view sections
  1. Section-Based Rendering:
const renderFormWithLayout = () => {
  const layout = workflowDefinition?.form_layout;
  const currentStep = getCurrentStep();

  if (!layout) {
    return renderFormFields(); // Existing logic
  }

  return (
    <div className="form-sections">
      {layout.sections.map(section => {
        const isViewMode = currentStep?.view_sections?.includes(section.id);
        const isEditMode = currentStep?.edit_sections?.includes(section.id);
        const shouldShow = !currentStep || isViewMode || isEditMode;

        if (!shouldShow) return null;

        return (
          <div key={section.id} className="section-display mb-6">
            <div className="section-header bg-gray-100 px-4 py-3 rounded-t-lg">
              <h4 className="font-semibold text-gray-800">{section.title}</h4>
              {section.description && (
                <p className="text-sm text-gray-600">{section.description}</p>
              )}
            </div>
            <div className="section-fields bg-white p-4 rounded-b-lg border border-gray-200">
              {renderSectionFields(section, isViewMode)}
            </div>
          </div>
        );
      })}
    </div>
  );
};

Phase 2: Initial Submission Form

File: frontend/src/components/workflows/WorkflowSubmissionForm.jsx (or similar)

Required Changes:

  1. Show Only Initial Sections:
const getInitialSections = () => {
  const layout = workflow?.workflow_definition?.form_layout;
  if (!layout) return null;

  return layout.sections.filter(section => section.initial);
};
  1. Render Initial Sections:
  • Show banner for each initial section
  • Render fields in grid layout
  • All fields editable (not readonly)

Phase 3: Test Mode Updates

File: frontend/src/components/admin/WorkflowTestMode.jsx

Required Changes:

  1. Display Section Information:
<div className="test-workflow-info">
  {workflow.form_layout && (
    <div className="layout-info mb-4">
      <h4 className="font-semibold">Form Layout</h4>
      <div className="sections-list">
        {workflow.form_layout.sections.map(section => (
          <div key={section.id} className="section-item flex items-center gap-2 py-1">
            <span className="text-sm">{section.title}</span>
            {section.initial && (
              <span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded">
                Initial
              </span>
            )}
          </div>
        ))}
      </div>
    </div>
  )}
</div>
  1. Show Section Visibility for Each Step:
<div className="step-sections-info text-sm text-gray-600">
  {step.view_sections && (
    <div>View: {step.view_sections.join(', ')}</div>
  )}
  {step.edit_sections && (
    <div>Edit: {step.edit_sections.join(', ')}</div>
  )}
</div>

Phase 4: CSS and Responsive Styling

File: frontend/src/styles/workflow-layout.css (new file)

/* Section Styling */
.form-section {
  @apply mb-6;
}

.section-header {
  @apply bg-gradient-to-r from-blue-50 to-indigo-50 px-6 py-4 rounded-t-lg border-l-4 border-blue-500;
}

.section-header h3 {
  @apply text-lg font-semibold text-gray-800 mb-1;
}

.section-header p {
  @apply text-sm text-gray-600;
}

.section-content {
  @apply bg-white p-6 rounded-b-lg border border-gray-200 shadow-sm;
}

/* Grid Layout */
.section-grid {
  @apply grid gap-4;
}

/* Responsive grid classes - generate for 1-6 columns */
@layer utilities {
  .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
  .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

  @media (max-width: 768px) {
    .sm\\:grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)) !important; }
    .sm\\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  }

  @media (max-width: 1024px) and (min-width: 769px) {
    .md\\:grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)) !important; }
    .md\\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
    .md\\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
  }
}

/* Readonly field styling */
.field-readonly {
  @apply bg-gray-50 cursor-not-allowed opacity-75;
}

.field-readonly input,
.field-readonly textarea,
.field-readonly select {
  @apply bg-gray-50 cursor-not-allowed;
}

Phase 5: Documentation Updates

File: frontend/content/docs/markup-language/formtypes.md

Updates Needed:

  1. Add comprehensive section about Form Layouts
  2. Include grid syntax examples
  3. Document initial section behavior
  4. Explain view_sections and edit_sections
  5. Add responsive breakpoint documentation
  6. Include complete working examples

Suggested Structure:

# Form Layout and Sections

## Overview
The layout feature allows you to organize form fields into sections with custom grid layouts...

## Basic Syntax
[Layout YAML example]

## Section Properties
- `id`: Unique identifier
- `title`: Display title
- `description`: Optional description
- `initial`: Show on initial submission
- `grid`: Array of field name arrays

## Grid Layout
[Grid examples with 1, 2, 3+ columns]

## Responsive Behavior
[Tablet and mobile breakpoint examples]

## Workflow Integration
[view_sections and edit_sections examples]

## Complete Examples
[Full working examples]

Testing Checklist

Unit Tests

  • Parser validates layout structure
  • Parser catches invalid section references
  • Parser catches invalid field references in grids
  • Workflow step validation works

Integration Tests

  • Layout renders correctly with 1 column
  • Layout renders correctly with 2 columns
  • Layout renders correctly with 3+ columns
  • Responsive breakpoints work on mobile
  • Responsive breakpoints work on tablet
  • Initial sections show on form creation
  • View sections are readonly in approval steps
  • Edit sections are editable in approval steps
  • Section banners display correctly
  • No layout fallback works (legacy forms)

E2E Tests

  • Create workflow with layout
  • Submit initial form (only initial sections)
  • Approve step with view/edit sections
  • Complete full workflow with multiple steps
  • Test mode displays layout correctly
  • Mobile responsive works end-to-end

Example YAML for Testing

Located in: docs/examples/advanced_layout_test.yaml

This file contains a complete working example with:

  • 3 sections (personal_info, it_setup, hr_verification)
  • Grid layouts with varying column counts
  • Responsive settings (tablet: 2, mobile: 1)
  • Workflow steps with view_sections and edit_sections
  • Initial section marked (personal_info)

Migration Guide

For Existing Workflows (Backwards Compatibility)

No layout specified:

  • Works as before
  • All fields display in simple list
  • No breaking changes

With layout specified:

  • Fields must be referenced in section grids
  • Unreferenced fields won't display (validation catches this)
  • Initial submission shows only initial: true sections

Performance Considerations

  1. Grid Rendering: CSS Grid is performant for up to 100+ fields
  2. Section Visibility: Computed once per render, not per field
  3. Responsive: Uses CSS media queries, not JavaScript
  4. Validation: Happens at parse time, not render time

Browser Support

  • Modern browsers (Chrome, Firefox, Safari, Edge)
  • CSS Grid support required (all browsers since 2017)
  • Mobile responsive works on iOS Safari and Chrome Android

Future Enhancements (Not in Scope)

  1. Conditional section visibility (show section if condition)
  2. Section-level permissions
  3. Nested sections
  4. Custom section styling themes
  5. Drag-and-drop field reordering
  6. Visual layout editor

Contact / Questions

For questions about this implementation:

  • Backend: Parser and validation logic
  • Frontend: Component rendering and styling
  • Documentation: Markup language docs

Last Updated: 2025-10-07 Status: Backend complete, frontend implementation pending Estimated Frontend Work: 8-12 hours

Form Layout Feature - Implementation Complete

Complete implementation of the form layout feature for workflow forms

Getting Started

Next Page

On this page

Form Layout Feature Implementation GuideStatus: Backend Complete ✅ | Frontend Pending ⏳Overview✅ Completed: Backend Implementation1. Parser Updates (src/app/core/approvalml/parser.py)2. AI Reference Updates (src/app/core/approvalml/syntax_reference.py)3. Test Results⏳ TODO: Frontend ImplementationPhase 1: Core Form Rendering UpdatesFile: frontend/src/components/workflows/DynamicFormHookForm.jsxFile: frontend/src/components/workflows/ApprovalPageUI.jsxPhase 2: Initial Submission FormFile: frontend/src/components/workflows/WorkflowSubmissionForm.jsx (or similar)Phase 3: Test Mode UpdatesFile: frontend/src/components/admin/WorkflowTestMode.jsxPhase 4: CSS and Responsive StylingFile: frontend/src/styles/workflow-layout.css (new file)Phase 5: Documentation UpdatesFile: frontend/content/docs/markup-language/formtypes.mdTesting ChecklistUnit TestsIntegration TestsE2E TestsExample YAML for TestingMigration GuideFor Existing Workflows (Backwards Compatibility)Performance ConsiderationsBrowser SupportFuture Enhancements (Not in Scope)Contact / Questions