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

The module includes several specialized modal components:

  1. ModalDeleteFormula - Confirmation dialog for formula deletion
  2. QuestionnaireChangeModal - Warning when changing questionnaire selection
  3. CancelModal - Confirmation when canceling formula creation/editing
  4. 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

  1. Flag Aggregation: Count questionnaire responses by category (green, red, yellow flags)
  2. Condition Evaluation: Process each condition using the specified comparator
  3. Boolean Logic: Combine conditions using AND/OR operators
  4. Rule Matching: Find the first rule where all conditions evaluate to true
  5. 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:

  1. Formula Level: Checks for formula name and questionnaire selection
  2. Rule Level: Validates that each rule has conditions and actions
  3. Condition Level: Ensures all condition fields are populated
  4. 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:

EndpointMethodPurposeImplementation
/questionnaires/formulasPOSTCreate new formulasrc/services/QuickVisitationReport/formula.ts:13-35
/questionnaires/formulas/{id}PUTUpdate existing formulasrc/services/QuickVisitationReport/formula.ts:13-35
/questionnaires/formulas/{id}GETRetrieve formula by IDsrc/services/QuickVisitationReport/formula.ts:37-55
/questionnaires/formulasGETList formulas (paginated)src/reducers/formulas/formulas.actionSaga.ts:6-42
/questionnaires/formulas/{id}DELETEDelete formulasrc/components/QuickVisitationReport/utils/deleteFormula.ts:7-23
/questionnaires/questionnaireIndexes/active/no-formula/minifiedGETGet available questionnairessrc/services/QuickVisitationReport/formula.ts:57-74
/questionnaires/questionnaireIndexes/{id}/latestGETGet questionnaire questionssrc/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

  1. Route Navigation: User navigates to /admin/formula-builder/new
  2. Component Initialization: FormulaBuilder component loads with default rule
  3. Questionnaire Selection: User selects questionnaire from dropdown
  4. Question Loading: System fetches questions for selected questionnaire
  5. Rule Building: User creates conditions using drag-and-drop interface
  6. Validation: Real-time validation of all form fields
  7. Submission: Formula data is transformed and sent to API
  8. 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:

  1. State Hydration: Existing formula data populates form fields
  2. Real-time Updates: Changes are immediately reflected in UI
  3. Validation: Continuous validation during editing
  4. Questionnaire Changes: Confirmation modal for questionnaire changes
  5. 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 fetch
  • fetchFormulaAsync.success - Update state with fetched formulas
  • fetchFormulaAsync.failure - Handle fetch errors
  • setLoading - Update loading state
  • setPage - 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:

  1. Role-based Access: RoleResources.ADMIN_QUESTIONNAIRE_ALL
  2. Feature Flags: Features.CUSTOMIZABLE_QUESTIONNAIRE
  3. Route Validation: withValidation={true}

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

  1. Input Level: Real-time validation during typing
  2. Field Level: Validation on blur/change events
  3. Form Level: Comprehensive validation before submission
  4. 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

PackageVersionPurposeUsage in Module
@nimbly-technologies/nimbly-commonLatestShared business logic and typesCore interfaces, enums, services
@nimbly-technologies/audit-componentLatestUI component libraryTooltip, LoadingDots components
react-beautiful-dnd^13.xDrag and drop functionalityRule/condition reordering
react-selectLatestAdvanced select componentsQuestionnaire/question selection
react-i18nextLatestInternationalizationMulti-language support
react-toastifyLatestToast notificationsUser feedback and error messages
styled-componentsLatestCSS-in-JS stylingComponent styling
reduxLatestState managementGlobal formula list state
redux-sagaLatestSide effect managementAsync API operations
typesafe-actionsLatestType-safe Redux actionsStrongly typed actions/reducers
uuidLatestUnique identifier generationCondition ID generation

Internal Dependencies

ModulePurposeIntegration Point
core/presentation/ui/Select/ReactSelectExtendedCustom select componentDropdown interfaces
components/global/Layout/LayoutApplication layoutPage wrapper
components/global/Pagination/PaginationPagination componentFormula list pagination
services/questionnaire.serviceQuestionnaire data accessQuestion loading
utils/monitoring/MonitoringError tracking and analyticsError logging
config/baseURLAPI configurationEndpoint configuration
reducers/apiAuthentication utilitiesToken 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

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.