Files
low-code-react-app-b/docs/architecture/CONFIG_ARCHITECTURE.md
2026-01-17 00:33:02 +00:00

16 KiB

Declarative Page Configuration System

Overview

CodeForge uses a declarative, JSON-driven configuration system to manage pages, components, props, and layouts. This architecture enables rapid development without modifying core application logic.

Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                        App.tsx                               │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  Uses page-loader to:                                 │   │
│  │  • Get enabled pages                                  │   │
│  │  • Resolve component props dynamically                │   │
│  │  • Render pages with correct configuration            │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ▲
                            │
                            │ imports & uses
                            │
┌─────────────────────────────────────────────────────────────┐
│              page-loader.ts (Resolution Logic)               │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  • getPageConfig()                                    │   │
│  │  • getEnabledPages(featureToggles)                    │   │
│  │  • resolveProps(propConfig, state, actions)           │   │
│  │  • getPageShortcuts()                                 │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ▲
                            │
                            │ reads
                            │
┌─────────────────────────────────────────────────────────────┐
│            pages.json (Declarative Config)                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  {                                                    │   │
│  │    "pages": [                                         │   │
│  │      {                                                │   │
│  │        "id": "models",                                │   │
│  │        "component": "ModelDesigner",                  │   │
│  │        "props": {                                     │   │
│  │          "state": ["models"],                         │   │
│  │          "actions": ["onModelsChange:setModels"]     │   │
│  │        }                                              │   │
│  │      }                                                │   │
│  │    ]                                                  │   │
│  │  }                                                    │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ▲
                            │
                            │ validates
                            │
┌─────────────────────────────────────────────────────────────┐
│         validate-config.ts (Validation Logic)                │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  • validatePageConfig()                               │   │
│  │  • Checks for duplicate IDs/shortcuts                 │   │
│  │  • Validates state/action keys exist                  │   │
│  │  • Validates resizable configurations                 │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ▲
                            │
                            │ uses schemas
                            │
┌─────────────────────────────────────────────────────────────┐
│          page-schema.ts (Type Definitions)                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  • PropConfigSchema                                   │   │
│  │  • ResizableConfigSchema                              │   │
│  │  • SimplePageConfigSchema                             │   │
│  │  • Zod schemas for runtime validation                 │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

Core Files

1. pages.json - Source of Truth

The declarative configuration file that defines all pages, their components, and props.

Location: /src/config/pages.json

Purpose:

  • Define all application pages
  • Configure component props mapping
  • Set keyboard shortcuts
  • Configure resizable layouts
  • Enable/disable features

2. page-loader.ts - Resolution Engine

The TypeScript module that reads and processes the JSON configuration.

Location: /src/config/page-loader.ts

Key Functions:

  • getPageConfig() - Returns full page configuration
  • getEnabledPages(featureToggles) - Filters pages based on toggles
  • resolveProps(propConfig, stateContext, actionContext) - Maps JSON config to actual props
  • getPageShortcuts(featureToggles) - Extracts keyboard shortcuts

3. validate-config.ts - Configuration Validator

Validates the pages.json configuration for errors and warnings.

Location: /src/config/validate-config.ts

Validates:

  • Required fields (id, title, component)
  • Duplicate IDs, shortcuts, or order numbers
  • Valid state and action keys
  • Resizable configuration correctness
  • Feature toggle key validity

4. page-schema.ts - Type Definitions

Zod schemas and TypeScript types for type-safe configuration.

Location: /src/config/page-schema.ts

Exports:

  • PropConfigSchema - Props configuration structure
  • ResizableConfigSchema - Resizable layout structure
  • SimplePageConfigSchema - Individual page structure
  • TypeScript types inferred from schemas

Data Flow

Adding a New Page

graph LR
    A[Edit pages.json] --> B[Add page config with props]
    B --> C[page-loader reads config]
    C --> D[App.tsx calls resolveProps]
    D --> E[Props mapped from state/actions]
    E --> F[Component rendered with props]

Prop Resolution Flow

1. Component needs props
   ↓
2. App.tsx calls getPropsForComponent(pageId)
   ↓
3. getPropsForComponent finds page config
   ↓
4. Calls resolveProps(page.props, stateContext, actionContext)
   ↓
5. resolveProps iterates over state/action arrays
   ↓
6. Maps JSON keys to actual values from contexts
   ↓
7. Returns resolved props object
   ↓
8. Props passed to component

Configuration Examples

Simple Page (Dashboard)

{
  "id": "dashboard",
  "title": "Dashboard",
  "icon": "ChartBar",
  "component": "ProjectDashboard",
  "enabled": true,
  "shortcut": "ctrl+1",
  "order": 1,
  "props": {
    "state": ["files", "models", "components"]
  }
}

Page with Actions (Model Designer)

{
  "id": "models",
  "title": "Models",
  "component": "ModelDesigner",
  "props": {
    "state": ["models"],
    "actions": ["onModelsChange:setModels"]
  }
}

Page with Renamed Props (Flask Designer)

{
  "id": "flask",
  "component": "FlaskDesigner",
  "props": {
    "state": ["config:flaskConfig"],
    "actions": ["onConfigChange:setFlaskConfig"]
  }
}

Resizable Split-Panel Page (Code Editor)

{
  "id": "code",
  "component": "CodeEditor",
  "requiresResizable": true,
  "props": {
    "state": ["files", "activeFileId"],
    "actions": ["onFileChange:handleFileChange"]
  },
  "resizableConfig": {
    "leftComponent": "FileExplorer",
    "leftProps": {
      "state": ["files"],
      "actions": ["onFileSelect:setActiveFileId"]
    },
    "leftPanel": {
      "defaultSize": 20,
      "minSize": 15,
      "maxSize": 30
    },
    "rightPanel": {
      "defaultSize": 80
    }
  }
}

State Context Keys

Available state variables that can be referenced in props.state:

  • files - Project files
  • models - Data models
  • components - Component definitions
  • componentTrees - Component tree structures
  • workflows - Workflow definitions
  • lambdas - Lambda functions
  • theme - Theme configuration
  • playwrightTests - Playwright tests
  • storybookStories - Storybook stories
  • unitTests - Unit tests
  • flaskConfig - Flask configuration
  • nextjsConfig - Next.js configuration
  • npmSettings - NPM settings
  • featureToggles - Feature toggles
  • activeFileId - Active file ID

Action Context Keys

Available action functions that can be referenced in props.actions:

  • handleFileChange - Update file content
  • setActiveFileId - Set active file
  • handleFileClose - Close file
  • handleFileAdd - Add new file
  • setModels - Update models
  • setComponents - Update components
  • setComponentTrees - Update component trees
  • setWorkflows - Update workflows
  • setLambdas - Update lambdas
  • setTheme - Update theme
  • setPlaywrightTests - Update Playwright tests
  • setStorybookStories - Update Storybook stories
  • setUnitTests - Update unit tests
  • setFlaskConfig - Update Flask config
  • setNextjsConfig - Update Next.js config
  • setNpmSettings - Update NPM settings
  • setFeatureToggles - Update feature toggles

Benefits of This Architecture

1. Declarative Configuration

  • All pages defined in one place
  • Easy to understand at a glance
  • No need to read through TypeScript code

2. Separation of Concerns

  • Configuration separate from logic
  • Props resolution handled systematically
  • Business logic stays in components

3. Type Safety

  • Zod schemas validate runtime data
  • TypeScript types ensure compile-time safety
  • Catch errors early in development

4. Maintainability

  • Add pages without touching App.tsx logic
  • Modify props without code changes
  • Clear validation error messages

5. Scalability

  • Easy to add new pages
  • Simple to extend prop mappings
  • Can add more configuration options

6. Testability

  • Validate configuration independently
  • Test prop resolution in isolation
  • Mock configurations for testing

Validation

Run configuration validation:

# Via TypeScript (if configured)
npm run validate-config

# Or manually import and run
import { validatePageConfig, printValidationErrors } from '@/config/validate-config'
const errors = validatePageConfig()
printValidationErrors(errors)

Validation checks:

  • Required fields present
  • No duplicate IDs or shortcuts
  • Valid state/action keys
  • Correct resizable config format
  • Panel sizes sum to 100
  • ⚠️ Warnings for missing icons or duplicate orders

Extending the System

Adding New State Keys

  1. Add state to useProjectState hook
  2. Add to stateContext in App.tsx
  3. Add to validation in validate-config.ts
  4. Document in this README

Adding New Action Keys

  1. Create action function in App.tsx or hook
  2. Add to actionContext in App.tsx
  3. Add to validation in validate-config.ts
  4. Document in this README

Adding New Configuration Options

  1. Update pages.json with new field
  2. Update PageConfig interface in page-loader.ts
  3. Update Zod schema in page-schema.ts
  4. Add validation in validate-config.ts
  5. Implement logic in App.tsx

Migration Guide

From Hardcoded Props to JSON Config

Before (Hardcoded in App.tsx):

const getPropsForComponent = (pageId: string) => {
  const propsMap: Record<string, any> = {
    'ModelDesigner': {
      models,
      onModelsChange: setModels,
    },
  }
  return propsMap[pageId] || {}
}

After (Declarative in pages.json):

{
  "id": "models",
  "component": "ModelDesigner",
  "props": {
    "state": ["models"],
    "actions": ["onModelsChange:setModels"]
  }
}

Best Practices

  1. Keep props minimal - Only pass what the component needs
  2. Use descriptive prop names - Clear intent over brevity
  3. Validate configurations - Run validation before deployment
  4. Document new features - Update this README when extending
  5. Consistent naming - Follow existing patterns for state/actions
  6. Group related props - Keep state and actions organized

Troubleshooting

Props not reaching component?

  • Check state/action key spelling in pages.json
  • Verify key exists in stateContext/actionContext
  • Run validation to catch errors

Component not rendering?

  • Ensure component added to componentMap in App.tsx
  • Check component name matches exactly (case-sensitive)
  • Verify component has default export

Resizable panel issues?

  • Ensure defaultSize values sum to 100
  • Check leftComponent exists in componentMap
  • Verify resizableConfig structure is complete

Future Enhancements

Potential improvements to the configuration system:

  • Hot-reload configuration changes in development
  • Visual configuration editor UI
  • Import/export page configurations
  • Configuration presets/templates
  • Computed/derived props from state
  • Conditional prop mappings
  • Hook injection via configuration
  • Layout templates beyond resizable panels
  • Permission-based page visibility
  • Analytics tracking configuration