Frontend Development¶
Overview¶
The FormFiller frontend is a modern, React and TypeScript based single-page application (SPA) that dynamically renders forms, data grids, and tree structures based on JSON configuration.
Main Advantages¶
| Advantage | Description |
|---|---|
| Configuration-driven | No hardcoded UI - everything comes from the schema |
| Multiple render engines | DevExtreme, Material-UI, Print - same config |
| Reactive | Fields automatically react to each other |
| Type-safe | Full TypeScript coverage |
| Modular | Easy to extend with new components |
| Offline-capable | Optimistic UI, background synchronization |
Schematic Structure¶
flowchart TB
subgraph App["FRONTEND APPLICATION"]
subgraph Pages["PAGES"]
P1[Home]
P2[Form]
P3[Results]
P4[Profile]
P5[Admin]
P6[Tasks]
end
subgraph Components["COMPONENTS"]
C1[FormComponent]
C2[GridView]
C3[TreeView]
subgraph Renderers["RENDERER FACTORIES"]
R1[DevExtreme]
R2[Material-UI]
R3[Print]
end
end
subgraph Managers["MANAGERS & SERVICES"]
M1[FormManager]
M2[EventHandler Registry]
M3[DataService]
end
subgraph Contexts["CONTEXTS"]
CTX1[AuthContext]
CTX2[NavigationContext]
CTX3[ThemeContext]
end
end
API[BACKEND API]
Pages --> Components
C1 --> Renderers
C2 --> Renderers
C3 --> Renderers
Components --> Managers
Managers --> Contexts
Contexts --> API
Call Chains¶
Form Display¶
flowchart TB
URL["URL: /form/:configId/:recordId"]
FP["FormPage<br/>← React Router"]
CS["configService"]
BE["Backend<br/>GET /api/config/:configId"]
FC["FormComponent<br/>← Config processing"]
FM["FormManager<br/>← Field registration"]
RF["Renderer Factory<br/>← DevExtreme/MUI/Print"]
DX["DevExtreme Components<br/>← UI rendering"]
URL --> FP
FP --> CS
CS --> BE
CS --> FC
FC --> FM
FM --> RF
RF --> DX
Field Value Change¶
flowchart TB
U["User enters value"]
DX["DevExtreme Editor<br/>onValueChanged"]
FM["FormManager<br/>handleFieldChange"]
EHR["EventHandlerRegistry<br/>(onValueChanged)"]
FUS["FieldUpdateService<br/>(visibleIf, etc.)"]
H["Handlers run:<br/>calculate, setValue, validate"]
F["Other fields update<br/>automatically"]
U --> DX
DX --> FM
FM --> EHR
FM --> FUS
EHR --> H
FUS --> F
Save Process¶
flowchart TB
U["User clicks 'Save'"]
FM["FormManager<br/>collectData()"]
V["Validator<br/>validate(data)"]
VAL{Validation}
ERR["Error message<br/>display"]
DS["dataService<br/>save(configId, data)"]
API["Backend API<br/>POST /api/data/..."]
OK["Successful save<br/>Toast notification"]
U --> FM
FM --> V
V --> VAL
VAL -->|INVALID| ERR
VAL -->|VALID| DS
DS --> API
API --> OK
Detailed Documentation¶
Detailed descriptions of specific features in separate documents:
- User Management - Registration, login, profile
- Access Control (RBAC) - Roles, permissions, UI filtering
- Multisite Management - Multi-tenant operation on frontend
- Theme and Localization - Themes, multi-language
Architecture¶
The frontend is React and TypeScript based, with Vite build system and DevExtreme UI components. Form rendering logic has been extracted to the formfiller-embed package for reusability.
Project Structure¶
src/
├── components/ # React components
│ ├── form/ # Form wrapper components
│ ├── header/ # Header
│ ├── footer/ # Footer
│ ├── config-editor/ # Configuration editor
│ └── views/ # View modules (Grid, Tree)
├── pages/ # Page components
│ ├── home/ # Home page
│ ├── form/ # Form page
│ ├── results/ # Results page
│ ├── profile/ # Profile page
│ └── admin/ # Admin pages
├── services/ # Business logic and API
│ ├── configService.ts # Config API calls
│ ├── dataService.ts # Data API calls
│ └── authService.ts # Auth API calls
├── contexts/ # React contexts (Auth, Navigation, Theme)
├── interfaces/ # TypeScript interfaces (re-exports from embed)
├── types/ # Type definitions
├── utils/ # Utility functions
└── themes/ # DevExtreme themes
Embed Package Integration¶
The frontend uses the formfiller-embed package for form rendering. This separation provides:
- Reusability: Same form rendering for main app and external integrations
- Maintainability: Single source of truth for form logic
- Bundle optimization: Embed can be loaded independently
import { EmbedForm, useEmbedForm } from 'formfiller-embed';
// Using the component
<EmbedForm
configId="my-form"
apiBaseUrl={API_URL}
onSubmitSuccess={handleSuccess}
/>
// Using the hook for custom rendering
const { formData, setFieldValue, submit } = useEmbedForm({
configId: 'my-form',
apiBaseUrl: API_URL
});
Renderers¶
The system supports three rendering engines:
DevExtreme Renderer¶
The main rendering engine with full functionality:
import { DevExtremeFormRenderer } from './factories/DevExtremeFormRenderer';
const renderer = new DevExtremeFormRenderer();
const form = renderer.render(config, data, mode);
Material-UI Renderer¶
Alternative Material Design appearance:
Print Renderer¶
Print-optimized view.
Form Manager¶
The FormManager is the central state manager for forms, located in the formfiller-embed package. It has been refactored into specialized sub-managers for better maintainability:
Architecture¶
import { FormManager } from 'formfiller-embed';
const formManager = new FormManager(config);
// Sub-managers handle specific concerns:
// - FormStateManager: Form data and value management
// - FormValidationManager: Validation logic and error tracking
// - FormVisibilityManager: Field visibility and disabled states
Usage¶
// Register field
formManager.registerField('firstName', {
value: '',
onChange: (value) => console.log('Changed:', value)
});
// Set value (delegated to FormStateManager)
formManager.setValue('firstName', 'John');
// Validate (delegated to FormValidationManager)
const isValid = await formManager.validateForm();
// Check visibility (delegated to FormVisibilityManager)
const isVisible = formManager.isFieldVisible('conditionalField');
// Collect data
const formData = formManager.collectFormData();
Sub-Manager Responsibilities¶
| Manager | Responsibility |
|---|---|
| FormStateManager | Form data storage, value get/set, data normalization |
| FormValidationManager | Validation execution, error tracking, error callbacks |
| FormVisibilityManager | visibleIf/disabledIf evaluation, field state updates |
Event Handling¶
Declarative event handling system:
// Configuration
const fieldConfig = {
name: 'quantity',
type: 'number',
onValueChanged: [
{ handler: 'log', params: { message: 'Quantity changed' } },
{ handler: 'calculate', params: { target: 'total', formula: 'quantity * price' } }
]
};
// Register custom handler
EventHandlerRegistry.register('myHandler', (context, params) => {
// Handler implementation
});
API Calls¶
The dataService handles API communication:
import { configService, dataService } from './services/dataService';
// Get configuration
const config = await configService.getById(configId);
// Get data
const data = await dataService.query(configId, { filter, sort, skip, take });
// Save
await dataService.save(configId, formData);
Contexts¶
Auth Context¶
import { useAuth } from './contexts/AuthContext';
const { user, login, logout, isAuthenticated } = useAuth();
Navigation Context¶
import { useNavigation } from './contexts/NavigationContext';
const { navigate, currentRoute } = useNavigation();
Component Development¶
Creating New Component¶
// components/MyComponent/MyComponent.tsx
import React from 'react';
import './MyComponent.scss';
interface MyComponentProps {
title: string;
onAction: () => void;
}
export const MyComponent: React.FC<MyComponentProps> = ({ title, onAction }) => {
return (
<div className="my-component">
<h2>{title}</h2>
<button onClick={onAction}>Action</button>
</div>
);
};
Themes¶
DevExtreme themes in the themes/ directory: