Overview
The Formula Builder module is a sophisticated rule engine that enables administrators to create, edit, and manage scoring formulas for questionnaires within the Nimbly audit-admin system. This module provides a drag-and-drop interface for building complex conditional logic that automatically calculates questionnaire grades based on response patterns.
Core Functionality
The Formula Builder enables users to:
- Create rule-based formulas using a visual drag-and-drop interface
- Define conditions based on questionnaire question responses (green/red flags)
- Set up complex boolean logic with AND/OR operators
- Apply formulas to specific questionnaires for automated scoring
- Manage formula lifecycle with full CRUD operations
Business Context
In the audit management workflow, questionnaires are used to assess various compliance and operational metrics. The Formula Builder automates the scoring process by allowing administrators to define rules that evaluate questionnaire responses and assign grades accordingly. This automation improves consistency, reduces manual effort, and enables scalable assessment processes.
System Architecture
High-Level Architecture
The Formula Builder follows a component-based architecture with Redux state management, implementing a hybrid MVC pattern with clear separation of concerns:
graph TB A[Browser] --> B[React Router] B --> C[FormulaBuilderPage] B --> D[FormulaListPage] C --> E[FormulaBuilder Component] D --> F[FormulaList Component] E --> G[FormulaCondition Components] E --> H[Modal Components] G --> I[CategorizedCheckboxSelection] J[Redux Store] --> K[Formulas Reducer] J --> L[Global State] M[Services Layer] --> N[Formula API] M --> O[Questionnaire API] N --> P[Backend API] O --> P Q[External Libraries] --> R[react-beautiful-dnd] Q --> S[react-select] Q --> T[@nimbly-technologies/nimbly-common]
Architectural Patterns
1. Component-Based Architecture
- Modular React components with clear responsibilities
- Container/Presentational component separation
- Reusable utility components
2. Redux State Management
- Centralized state for formula list management
- Local component state for form data and UI interactions
- Unidirectional data flow with predictable state updates
3. Service Layer Pattern
- Dedicated service files for API interactions
- Abstracted business logic utilities
- Clear separation between UI and data access
4. Route-Based Code Splitting
- Lazy loading of formula components
- Performance optimization through loadable components
- Reduced initial bundle size
Component Structure
Component Hierarchy
Pages/
├── FormulaBuilderPage (Route Container)
│ └── FormulaBuilder (Main Component)
│ ├── FormulaCondition (Draggable Condition)
│ │ └── CategorizedCheckboxSelection
│ ├── ModalDeleteFormula
│ ├── QuestionnaireChangeModal
│ ├── CancelModal
│ └── DeleteModal
└── FormulaListPage (Route Container)
└── FormulaList (Main Component)
├── FormulaListEmpty
└── ModalDeleteFormula
Core Components
FormulaBuilder (src/components/QuickVisitationReport/FormulaBuilder.tsx)
The main container component that manages the formula creation and editing workflow. It handles:
- Formula state management (rules, conditions, actions)
- Questionnaire selection and question loading
- Drag-and-drop functionality for rules and conditions
- Validation and form submission
- Modal state management
Key Features:
- Dynamic rule and condition creation
- Real-time validation with error display
- Questionnaire change confirmation
- Drag-and-drop reordering with
react-beautiful-dnd - Comprehensive error handling and user feedback
FormulaCondition (src/components/QuickVisitationReport/FormulaCondition.tsx)
Represents individual conditions within a rule, providing:
- Condition type selection (green flags, red flags)
- Operator selection (equals, greater than, between, etc.)
- Question selection through categorized checkboxes
- Value input with validation
- Drag-and-drop functionality
Rendering Logic: The component dynamically renders input fields based on the selected operator:
- Single value input for standard operators
- Lower/upper bound inputs for “between” operators
- Question selection interface for flag-based conditions
FormulaList (src/components/QuickVisitationReport/FormulaList.tsx)
Manages the formula listing interface with:
- Paginated formula display with sorting capabilities
- Formula metadata (name, applied questionnaire, created by, modified by)
- Edit/delete action buttons
- Empty state handling
- Loading skeleton components
Modal Components
The module includes several specialized modal components:
- ModalDeleteFormula - Confirmation dialog for formula deletion
- QuestionnaireChangeModal - Warning when changing questionnaire selection
- CancelModal - Confirmation when canceling formula creation/editing
- DeleteModal - Generic delete confirmation for rules/conditions
Data Models & Types
Core Interfaces
Formula Structure
interface Formula {
formulaId: string;
name: string;
rules: Rule[];
questionnaireIndexIDs: string[];
createdAt: Date;
createdBy: string;
updatedBy: string;
updatedAt: Date | any;
}Rule Structure
interface Rule {
conditions: (Condition | Option)[];
actions: Action[];
showDeleteTooltip: boolean;
}Condition Structure
interface Condition {
id: string;
leftOperand: Option;
rightOperand: RightOperand | string | number;
questions: Option[];
comparator: Option;
}Right Operand for Range Values
interface RightOperand {
lowerBound: string | number;
upperBound: string | number;
}Enumerations
Condition Types
enum ConditionType {
TOTAL_GREEN_FLAGS = 'Total count of GREEN answers',
TOTAL_RED_FLAGS = 'Total count of RED answers',
TOTAL_YELLOW_FLAGS = 'Total count of YELLOW answers',
ALWAYS_TRUE = 'always-true',
}Comparator Operations
enum Comparator {
IS_EQUAL = 'Is equal to',
IS_NOT_EQUAL = 'Is not equal to',
LESS_THAN_OR_EQUAL = 'Less than or equal to',
GREATED_THAN_OR_EQUAL = 'Greater than or equal to',
LESS_THAN = 'Less than',
GREATER_THAN = 'Greater than',
IN_BETWEEN = 'In between',
NOT_IN_BETWEEN = 'Not in between',
AND = 'And',
OR = 'Or',
}Type Safety Patterns
The module implements comprehensive type guards for runtime type checking:
const isCondition = (object: any): object is Condition => {
return 'id' in object;
};
const isOption = (object: any): object is Option => {
return 'label' in object || false;
};Business Logic
Formula Evaluation Engine
The Formula Builder implements a sophisticated rule engine that processes questionnaire responses through a binary tree evaluation system.
Condition Processing Flow
- Flag Aggregation: Count questionnaire responses by category (green, red, yellow flags)
- Condition Evaluation: Process each condition using the specified comparator
- Boolean Logic: Combine conditions using AND/OR operators
- Rule Matching: Find the first rule where all conditions evaluate to true
- Grade Assignment: Apply the action value from the matching rule
Binary Tree Architecture
The system uses the ConditionEncoderP1 service from @nimbly-technologies/nimbly-common to manage complex boolean expressions:
const tempEncoder = new services.ConditionEncoderP1();
rule.conditions.forEach((condition: Condition | Option, index: number) => {
if (!isCondition(condition)) {
tempOperator = condition.value;
return;
}
const formattedCondition = conditionDataFormatter(condition, selectedQuestionnaire);
if (!index) return tempEncoder.addCondition(formattedCondition);
tempEncoder.addCondition(formattedCondition, tempOperator);
});Validation Logic
Form Validation (src/components/QuickVisitationReport/utils/formulaBuilderFormValidation.ts)
The module implements multi-level validation:
- Formula Level: Checks for formula name and questionnaire selection
- Rule Level: Validates that each rule has conditions and actions
- Condition Level: Ensures all condition fields are populated
- Value Level: Validates numeric inputs and text formats
Validation Functions:
export const validateAlphaNumericWithSpace = (value: string) => {
const alphaNumeric = /^[ a-zA-Z0-9]*$/g;
return alphaNumeric.test(value);
};
export const validateNumeric = (value: string) => {
const numeric = /^(\s*|\d+)$/;
return numeric.test(value);
};Data Transformation
Condition Data Formatter (src/components/QuickVisitationReport/utils/conditionDataFormatter.ts)
Transforms UI-friendly condition objects to API format:
export const conditionDataFormatter = (condition: Condition, selectedQuestionnaire: Option) => {
const tempQuestions: any[] = [];
condition.questions.forEach((question: Option) => {
tempQuestions.push({
questionnaireIndexID: selectedQuestionnaire?.value,
questionIndex: question?.value?.toString(),
});
});
const tempCondition = {
id: condition.id,
leftOperand: condition.leftOperand.value,
rightOperand: condition.rightOperand,
comparator: condition.comparator.value,
questions: tempQuestions,
};
return tempCondition;
};API Integration
Core API Endpoints
The Formula Builder integrates with multiple API endpoints for comprehensive data management:
| Endpoint | Method | Purpose | Implementation |
|---|---|---|---|
/questionnaires/formulas | POST | Create new formula | src/services/QuickVisitationReport/formula.ts:13-35 |
/questionnaires/formulas/{id} | PUT | Update existing formula | src/services/QuickVisitationReport/formula.ts:13-35 |
/questionnaires/formulas/{id} | GET | Retrieve formula by ID | src/services/QuickVisitationReport/formula.ts:37-55 |
/questionnaires/formulas | GET | List formulas (paginated) | src/reducers/formulas/formulas.actionSaga.ts:6-42 |
/questionnaires/formulas/{id} | DELETE | Delete formula | src/components/QuickVisitationReport/utils/deleteFormula.ts:7-23 |
/questionnaires/questionnaireIndexes/active/no-formula/minified | GET | Get available questionnaires | src/services/QuickVisitationReport/formula.ts:57-74 |
/questionnaires/questionnaireIndexes/{id}/latest | GET | Get questionnaire questions | src/services/questionnaire.service.ts:82-99 |
Authentication Pattern
All API requests use Firebase Authentication with Bearer tokens:
const authToken = await getToken();
const options = {
method: 'GET',
headers: {
Authorization: authToken,
'Content-type': 'application/json',
},
};Service Integration
Formula Service (src/services/QuickVisitationReport/formula.ts)
Create/Update Formula:
export const createOrUpdateFormula = async (payload: CreateFormulaPayload) => {
try {
const authToken = await getToken();
const url = payload.formulaId
? `${apiURL}/questionnaires/formulas/${payload.formulaId}`
: `${apiURL}/questionnaires/formulas`;
const method = payload.formulaId ? 'PUT' : 'POST';
const options = {
method,
headers: {
Authorization: authToken,
'Content-type': 'application/json',
},
body: JSON.stringify(payload.data),
};
const response = await fetch(url, options);
const { data } = await response.json();
return data;
} catch (error) {
return error;
}
};Questionnaire Service (src/services/questionnaire.service.ts)
Fetch Questions by Questionnaire ID:
export const fetchQuestionsByQuestionnaireIndexID = async (questionnaireIndexId: string) => {
try {
const fetchToken = await getToken();
const fetchOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: fetchToken,
},
};
const fetchURL = `${apiURL}/questionnaires/questionnaireIndexes/${questionnaireIndexId}/latest`;
const responseData = await fetch(fetchURL, fetchOptions);
const result = await responseData.json();
return result.data;
} catch (error) {
console.error(error);
}
};CRUD Operations
Create Operations
Formula Creation Workflow
- Route Navigation: User navigates to
/admin/formula-builder/new - Component Initialization: FormulaBuilder component loads with default rule
- Questionnaire Selection: User selects questionnaire from dropdown
- Question Loading: System fetches questions for selected questionnaire
- Rule Building: User creates conditions using drag-and-drop interface
- Validation: Real-time validation of all form fields
- Submission: Formula data is transformed and sent to API
- Success Handling: User receives confirmation and is redirected to list
Key Implementation Details:
- Unified API endpoint handles both create and update operations
- Validation occurs at multiple levels (form, rule, condition, value)
- Complex data transformation from UI format to API format
- Optimistic updates with error rollback
Read Operations
Formula List with Pagination
The FormulaList component implements server-side pagination with sorting:
const fetchFormulas = (tableParams) => {
dispatch(fetchFormulaAsync.request(tableParams));
};
useEffect(() => {
dispatch(fetchFormulaAsync.request(tableParams));
}, [tableParams]);Features:
- Server-side pagination with configurable page size (16 items)
- Sortable columns (name, questionnaire, created by, modified by)
- Loading skeleton during data fetch
- Empty state handling
Individual Formula Retrieval
For editing, formulas are retrieved and transformed back to UI format:
const setupEditFormula = async () => {
const formulaRules = await getFormulaById(params?.formulaId);
const tempRawRules = formulaRules.rules.map((rule: any) => {
return {
conditions: deepFirstSearch(rule.rootCondition),
actions: rule.actions,
showDeleteTooltip: false,
};
});
// Additional transformation logic...
};Update Operations
In-Place Editing
The FormulaBuilder supports in-place editing with:
- State Hydration: Existing formula data populates form fields
- Real-time Updates: Changes are immediately reflected in UI
- Validation: Continuous validation during editing
- Questionnaire Changes: Confirmation modal for questionnaire changes
- State Synchronization: Local state updates trigger re-validation
Drag-and-Drop Updates
Rules and conditions can be reordered using react-beautiful-dnd:
const handleOnFormulaDragEnd = (result: DropResult) => {
const { source, destination } = result;
if (!destination || source.index === destination.index) return;
let tempRules = [...rules];
const tempDestination = tempRules[destination.index];
tempRules[destination.index] = tempRules[source.index];
tempRules[source.index] = tempDestination;
setRules(tempRules);
};Delete Operations
Formula Deletion
Formula deletion involves confirmation dialog and API cleanup:
const handleDeleteFormula = async () => {
if (!modalDeleteState.formulaId!) return;
try {
await deleteFormula(modalDeleteState!.formulaId!);
toast.success(/* success message */);
dispatch(fetchFormulaAsync.request(tableParams));
setModalDeleteState({ isShowModal: false, formulaId: '' });
} catch (e) {
toast.error(t('message.questionnairePage.formulaDetedFailed'));
}
};Rule and Condition Deletion
Individual rules and conditions can be deleted with proper cleanup:
const handleDeleteCondition = (ruleIndex: number, conditionIndex: number) => {
let tempRules = [...rules];
let index = conditionIndex;
if (conditionIndex) index = conditionIndex - 1;
tempRules[ruleIndex].conditions.splice(index, 2);
setRules(tempRules);
};State Management
Redux Architecture
The Formula Builder uses a hybrid state management approach:
Global State (Redux)
Formulas Reducer (src/reducers/formulas/formulas.reducer.ts):
const initialState: Formulas = {
error: null,
paginatedFormulas: [],
totalItem: 0,
isLoading: false,
};Actions (src/reducers/formulas/formulas.action.ts):
fetchFormulaAsync.request- Initiate formula list fetchfetchFormulaAsync.success- Update state with fetched formulasfetchFormulaAsync.failure- Handle fetch errorssetLoading- Update loading statesetPage- Update current page
Saga Integration (src/reducers/formulas/formulas.actionSaga.ts):
export function* fetchFormulas(action: ReturnType<typeof actions.fetchFormulaAsync.request>) {
const { page = 1, limit = 16, sortFields = '', sortDirections = '' } = action.payload;
try {
yield put(actions.setLoading(true));
const token: string = yield call(API.getFirebaseToken);
const queryString = new URLSearchParams({ page: String(page), limit: String(limit), sortFields, sortDirections });
const url = `${apiURL}/questionnaires/formulas?${queryString}`;
const response: any = yield call(API.get, url, token);
if (response && response.status === 200) {
const result: any = yield response.json();
yield put(actions.fetchFormulaAsync.success({
paginatedFormulas: result.data.docs,
totalItem: result.data.totalDocs,
}));
return;
}
yield put(actions.fetchFormulaAsync.failure({ error: response.message }));
} catch (err) {
yield put(actions.fetchFormulaAsync.failure({ error: '' }));
}
}Local Component State
Formula Builder State:
- Formula name and metadata
- Rules and conditions array
- Selected questionnaire
- Question options
- Modal visibility states
- Validation states
- Loading states
State Management Pattern:
const [rules, setRules] = useState<Rule[]>([initialRule]);
const [formulaName, setFormulaName] = useState<string>('');
const [selectedQuestionnaire, setSelectedQuestionnaire] = useState<Option | null>(null);
const [isErrorValidation, setIsErrorValidation] = useState<boolean>(false);Data Flow Architecture
graph TD A[User Action] --> B{Action Type} B -->|List Operation| C[Redux Action] B -->|Form Operation| D[Local State Update] C --> E[Saga Middleware] E --> F[API Call] F --> G[Redux State Update] G --> H[Component Re-render] D --> I[State Validation] I --> J[UI Update] H --> K[User Interface] J --> K
Routing & Navigation
Route Configuration
Admin Routes (src/routes/admin-routes.js):
<Route
exact
path="/admin/formula-builder"
component={FormulaListPage}
withValidation
access={RoleResources.ADMIN_QUESTIONNAIRE_ALL}
feature={Features.CUSTOMIZABLE_QUESTIONNAIRE}
/>
<Route
exact
path="/admin/formula-builder/:formulaId"
component={FormulaBuilderPage}
withValidation
access={RoleResources.ADMIN_QUESTIONNAIRE_ALL}
feature={Features.CUSTOMIZABLE_QUESTIONNAIRE}
/>Access Control
Multi-layered Security:
- Role-based Access:
RoleResources.ADMIN_QUESTIONNAIRE_ALL - Feature Flags:
Features.CUSTOMIZABLE_QUESTIONNAIRE - Route Validation:
withValidation={true}
Navigation Patterns
Programmatic Navigation:
const handleOnClickAddFormula = () => {
history.push('/admin/formula-builder/new');
};
const handleOnClickEditFormula = (formulaId: string) => {
history.push(`/admin/formula-builder/${formulaId}`);
};Back Navigation:
const handleOnBackButton = () => {
history.goBack();
};Route Parameters
The :formulaId parameter supports:
"new"- Create new formula mode{formulaId}- Edit existing formula mode
User Interface
Design System
The Formula Builder implements a consistent design system with:
Styled Components
Base Components (src/components/QuickVisitationReport/Formula.style.ts):
export const FormulaWrapper = styled.div`
width: 100%;
min-height: 100vh;
animation: 150ms ${fadeIn} ease-out;
overflow-y: scroll;
padding: 0;
background-color: #ffffff;
display: flex;
flex: 1;
flex-direction: column;
justify-content: flex-start;
align-items: center;
`;Interactive Elements
Drag-and-Drop Interface:
The module uses react-beautiful-dnd for intuitive drag-and-drop functionality:
- Rule reordering within formula
- Condition reordering within rules
- Visual feedback during drag operations
Form Controls:
- Custom styled React Select components
- Validated input fields with error states
- Button components with loading states
Responsive Design
The interface adapts to different screen sizes:
@media (max-width: 991px) {
display: none; // Hide on mobile devices
}User Experience Patterns
Loading States
Skeleton Components:
const _renderRowLoading = () => {
const classNames = ['col-name loading', 'col-applied-on loading', /* ... */];
return (
<tr>
{classNames.map((name) => (
<td key={name} className={name}>
<SkeletonContainer>
<LoadingSkeleton />
</SkeletonContainer>
</td>
))}
</tr>
);
};Empty States
Formula List Empty State:
if (!paginatedFormulas.length) {
return (
<FormulaListEmpty
title={t('pageQuestionnaire.formula.tableEmptyTitle')}
subTitle={t('pageQuestionnaire.formula.tableEmptyDescription')}
/>
);
}Confirmation Dialogs
Modal components provide clear confirmation workflows:
- Delete formula confirmation
- Cancel editing confirmation
- Questionnaire change warning
Validation & Error Handling
Validation Strategy
Multi-Level Validation
- Input Level: Real-time validation during typing
- Field Level: Validation on blur/change events
- Form Level: Comprehensive validation before submission
- Server Level: API response validation
Validation Implementation
Alphanumeric Validation:
export const validateAlphaNumericWithSpace = (value: string) => {
const alphaNumeric = /^[ a-zA-Z0-9]*$/g;
return alphaNumeric.test(value);
};Numeric Validation:
export const validateNumeric = (value: string) => {
const numeric = /^(\s*|\d+)$/;
return numeric.test(value);
};Form Validation:
export const formIsNotValid = (rules: Rule[]) => {
if (!rules.length) return { errorStatus: true, errorMessage: KindOfError.EMPTY_RULE };
const isErrors = rules.map((rule: Rule) => {
if (!rule.conditions.length) return KindOfError.EMPTY_CONDITION;
const unfilledSection = rule.conditions.some((condition: Condition | Option) => {
if (isOption(condition) && !condition.label) return true;
if (isCondition(condition)) return isConditionAreEmpty(condition);
return false;
});
if (unfilledSection) return KindOfError.EMPTY_FIELD;
if (!rule.actions[0].value || !rule.actions[0].value.trim()) return KindOfError.EMPTY_FIELD;
return null;
});
// Process validation results...
};Error Handling
Error Categories
enum KindOfError {
EMPTY_FIELD = 'emptyField',
EMPTY_RULE = 'emptyRules',
EMPTY_CONDITION = 'emptyConditions',
}Error Display
Toast Notifications:
const renderNotValidForm = (errorMessage: string | null) => {
switch (errorMessage) {
case KindOfError.EMPTY_FIELD:
return toast.error(
<ToastSection toastBackgroundColor="#FF5E6F" toastTextColor="#ffffff">
<div className="title">{t('toast.qvr.formulaBuilder.failTitle')}</div>
<div className="description">{t('toast.qvr.formulaBuilder.emptyFieldDescription')}</div>
</ToastSection>
);
// Additional error cases...
}
};Inline Error Display:
<RenderErrorText
errorValidation={isErrorValidation}
checkedValue={formulaName.trim()}
message={t('label.qvr.formulaBuilder.errorText.formulaName')}
/>Performance & Optimization
Code Splitting
Route-Level Splitting:
const FormulaListPage = loadable(() => import('../components/QuickVisitationReport/FormulaList'));
const FormulaBuilderPage = loadable(() => import('../components/QuickVisitationReport/FormulaBuilder'));Component-Level Splitting:
const FormulaLanding = lazy(() => import('../../components/QuickVisitationReport/FormulaList'));State Optimization
Immutable Updates:
const handleOnChangeConditionSelection = (payload: OnChangePayload) => {
let tempRules = [...rules]; // Shallow copy for immutability
// Perform updates...
setRules(tempRules);
};Efficient Re-rendering:
- React.memo for expensive components
- useMemo for expensive calculations
- useCallback for stable function references
Data Loading Optimization
Parallel API Calls:
const componentDidMount = async () => {
setIsLoading(true);
const questionnaires = await getActiveAndUnusedQuestionnaires();
await setupQuestionnaireOptions(questionnaires);
if (params?.formulaId !== 'new') await setupEditFormula();
setIsLoading(false);
};Pagination:
- Server-side pagination for large datasets
- Configurable page sizes
- Efficient state management for pagination
Dependencies
External Dependencies
| Package | Version | Purpose | Usage in Module |
|---|---|---|---|
@nimbly-technologies/nimbly-common | Latest | Shared business logic and types | Core interfaces, enums, services |
@nimbly-technologies/audit-component | Latest | UI component library | Tooltip, LoadingDots components |
react-beautiful-dnd | ^13.x | Drag and drop functionality | Rule/condition reordering |
react-select | Latest | Advanced select components | Questionnaire/question selection |
react-i18next | Latest | Internationalization | Multi-language support |
react-toastify | Latest | Toast notifications | User feedback and error messages |
styled-components | Latest | CSS-in-JS styling | Component styling |
redux | Latest | State management | Global formula list state |
redux-saga | Latest | Side effect management | Async API operations |
typesafe-actions | Latest | Type-safe Redux actions | Strongly typed actions/reducers |
uuid | Latest | Unique identifier generation | Condition ID generation |
Internal Dependencies
| Module | Purpose | Integration Point |
|---|---|---|
core/presentation/ui/Select/ReactSelectExtended | Custom select component | Dropdown interfaces |
components/global/Layout/Layout | Application layout | Page wrapper |
components/global/Pagination/Pagination | Pagination component | Formula list pagination |
services/questionnaire.service | Questionnaire data access | Question loading |
utils/monitoring/Monitoring | Error tracking and analytics | Error logging |
config/baseURL | API configuration | Endpoint configuration |
reducers/api | Authentication utilities | Token management |
File Structure
Complete File Mapping
📁 Formula Builder Module
├── 📁 Pages/
│ ├── 📄 FormulaLandingPage.tsx - Formula list page wrapper
│ └── 📄 FormulaBuilderPage.tsx - Formula builder page wrapper
│
├── 📁 Components/
│ ├── 📄 FormulaBuilder.tsx - Main formula creation/editing component
│ ├── 📄 FormulaCondition.tsx - Individual condition component
│ ├── 📄 FormulaList.tsx - Formula listing with CRUD operations
│ ├── 📄 FormulaListEmpty.tsx - Empty state display
│ ├── 📄 CategorizedCheckboxSelection.tsx - Question selection UI
│ ├── 📄 ModalDeleteFormula.tsx - Delete confirmation modal
│ ├── 📄 QuestionnaireChangeModal.tsx - Questionnaire change warning
│ ├── 📄 DeleteModal.tsx - Generic delete confirmation
│ ├── 📄 CancelModal.tsx - Cancel confirmation modal
│ ├── 📄 Formula.style.ts - Styled components
│ └── 📄 type.d.ts - Component type definitions
│
├── 📁 Redux/
│ ├── 📄 formulas.reducer.ts - State management
│ ├── 📄 formulas.action.ts - Action creators
│ ├── 📄 formulas.actionSaga.ts - Async operation handling
│ ├── 📄 formulas.actionTypes.ts - Action type constants
│ └── 📄 type.d.ts - Redux type definitions
│
├── 📁 Services/
│ ├── 📄 formula.ts - Formula API integration
│ ├── 📄 questionnaire.service.ts - Questionnaire data access
│ └── 📄 fetchQuestionnaireByProps.ts - Lightweight questionnaire queries
│
├── 📁 Utils/
│ ├── 📄 deleteFormula.ts - Formula deletion utility
│ ├── 📄 formulaBuilderFormValidation.ts - Validation logic
│ ├── 📄 renderErrorText.tsx - Error display component
│ ├── 📄 conditionDataFormatter.ts - Data transformation
│ ├── 📄 deepFirstSearch.ts - Tree traversal utility
│ ├── 📄 generateBorderColor.ts - Dynamic styling utility
│ └── 📄 reactSelectFilterOption.ts - Select filtering logic
│
├── 📁 Assets/
│ ├── 🖼️ back-arrow.svg - Navigation icon
│ ├── 🖼️ delete-icon.svg - Delete action icon
│ ├── 🖼️ drag-and-drop-icon.svg - Drag handle icon
│ └── 🖼️ info-icon.svg - Information tooltip icon
│
└── 📁 Routes/
├── 📄 admin-routes.js - Admin user routing configuration
└── 📄 superadmin-routes.js - Superadmin user routing configuration
Repository Links
All files are available in the GitHub repository: https://github.com/Nimbly-Technologies/audit-admin
Direct File Links:
Mermaid Diagrams
System Architecture Overview
graph TB subgraph "Client Layer" A[Browser] --> B[React Router] B --> C[Formula Builder Pages] B --> D[Formula List Pages] end subgraph "Component Layer" C --> E[FormulaBuilder] D --> F[FormulaList] E --> G[FormulaCondition] E --> H[Modal Components] G --> I[CategorizedCheckboxSelection] end subgraph "State Layer" J[Redux Store] --> K[Formulas Reducer] J --> L[Saga Middleware] M[Local Component State] end subgraph "Service Layer" N[Formula Service] --> O[API Layer] P[Questionnaire Service] --> O Q[Validation Utils] --> R[Business Logic] end subgraph "External Dependencies" S[react-beautiful-dnd] T[react-select] U[@nimbly-technologies/nimbly-common] V[react-i18next] end E -.-> K F -.-> K E -.-> M E -.-> N E -.-> P E -.-> S E -.-> T E -.-> U E -.-> V O --> W[Backend API]
Component Hierarchy
graph TD A[FormulaBuilderPage] --> B[Layout] B --> C[FormulaBuilder] C --> D[FormulaCondition 1] C --> E[FormulaCondition 2] C --> F[FormulaCondition N] D --> G[CategorizedCheckboxSelection] E --> H[CategorizedCheckboxSelection] F --> I[CategorizedCheckboxSelection] C --> J[Modal Components] J --> K[ModalDeleteFormula] J --> L[QuestionnaireChangeModal] J --> M[CancelModal] J --> N[DeleteModal] O[FormulaListPage] --> P[Layout] P --> Q[FormulaList] Q --> R[FormulaListEmpty] Q --> S[ModalDeleteFormula] Q --> T[Pagination]
Data Flow Diagram
sequenceDiagram participant U as User participant C as FormulaBuilder Component participant V as Validation participant S as Service Layer participant A as API participant R as Redux Store U->>C: Create/Edit Formula C->>S: Load Questionnaires S->>A: GET /questionnaires/active A-->>S: Questionnaire List S-->>C: Questionnaire Options U->>C: Select Questionnaire C->>S: Load Questions S->>A: GET /questionnaires/{id}/latest A-->>S: Question List S-->>C: Question Options U->>C: Build Rules/Conditions C->>V: Validate Input V-->>C: Validation Result U->>C: Submit Formula C->>V: Validate Form V-->>C: Validation Passed C->>S: Transform Data S->>A: POST/PUT /formulas A-->>S: Success Response S-->>C: Success C->>R: Refresh Formula List C-->>U: Success Notification
CRUD Operations Flow
graph TD subgraph "CREATE" A1[Navigate to /new] --> A2[Load Questionnaires] A2 --> A3[Build Rules] A3 --> A4[Validate Form] A4 --> A5[POST API] A5 --> A6[Success Notification] end subgraph "READ" B1[List View] --> B2[GET /formulas] B2 --> B3[Display Paginated List] B4[Edit Action] --> B5[GET /formulas/{id}] B5 --> B6[Load Edit Form] end subgraph "UPDATE" C1[Edit Form] --> C2[Modify Data] C2 --> C3[Validate Changes] C3 --> C4[PUT API] C4 --> C5[Update Success] end subgraph "DELETE" D1[Delete Action] --> D2[Confirmation Modal] D2 --> D3[DELETE API] D3 --> D4[Refresh List] end
Business Logic Flow
graph TD A[Formula Evaluation] --> B[Load Questionnaire Responses] B --> C[Count Green/Red/Yellow Flags] C --> D[Process Rules in Order] D --> E{Rule 1 Conditions} E -->|All True| F[Apply Rule 1 Action] E -->|Any False| G{Rule 2 Conditions} G -->|All True| H[Apply Rule 2 Action] G -->|Any False| I{Rule N Conditions} I -->|All True| J[Apply Rule N Action] I -->|Any False| K[Apply Default Action] F --> L[Return Grade] H --> L J --> L K --> L subgraph "Condition Processing" M[Binary Tree Traversal] N[AND/OR Logic Evaluation] O[Comparator Operations] P[Flag Count Matching] end E -.-> M G -.-> M I -.-> M
This comprehensive documentation provides a complete technical overview of the Formula Builder module, covering all aspects of its architecture, implementation, and functionality. The documentation serves as a definitive reference for understanding, maintaining, and extending the Formula Builder system within the Nimbly audit-admin application.