Skip to main content

Water Flow Monitoring

Real-time monitoring of water flow across every source/flow point, with consumption trends, per-unit drill-down, two-unit comparison, water-loss (leakage) detection, and daily-limit threshold alerts.


Overview

The Water Flow feature (internally the source_category / SOURCE_CATEGORY) tracks water movement across configured flow points. It renders per-category consumption cards, supports hourly/daily/monthly/yearly/custom time windows, detects water loss (stable-flow anomalies), and flags units that cross a daily limit.

Location: libs/monitoring/src/pages/flow/

Route: nested under the monitoring parent route (see below). The flow category landing page is registered at source_category/SOURCE_CATEGORY.

Registered in: production, demo, rwi, uwms. Not registered in lakepulse.

Permission required: the Water Flow sidebar entry is gated by the SOURCE_CATEGORY service tag; the parent Monitoring menu is gated by WATER_MONITORING (see Visibility Parameters).

Routes

PathElementPurpose
monitoring/source_category/SOURCE_CATEGORY (index)FlowCategoryPageFlow category landing page
monitoring/source_category/SOURCE_CATEGORY/:detailCategoryId/:unitIdFlowUnitPagePer-unit flow detail
monitoring/source_category/compare_unitsFlowUnitComparePageTwo-unit comparison
monitoring/:detailCategoryId/:unitIdConsumptionDetailConsumption drill-down (production & demo only)
  • Code Reference: apps/production/src/routes/routes.js, apps/demo/src/routes/routes.js, apps/rwi/src/routes/routes.js, apps/uwms/src/routes/routes.js
  • Note: rwi and uwms redirect the monitoring index to source_category/SOURCE_CATEGORY and do not register the ConsumptionDetail drill-down route (apps/rwi/src/routes/routes.js, apps/uwms/src/routes/routes.js).

Key Features

1. Real-Time Data Monitoring

  • Auto-refresh: background refresh on an interval from constants.refreshDuration.
  • Time-window aware: hourly / daily / monthly / yearly / custom.
  • Multi-subcategory: each source subcategory renders its own consumption card.
  • Code Reference: libs/monitoring/src/dataProvider/WaterMonitoringFlowDataProvider.jsx

2. Time-Based Analysis

WaterFlowEnum.TimeSelection = {
DAY: 'DAY',
WEEK: 'WEEK',
MONTH: 'MONTH',
YEAR: 'YEAR',
CUSTOM: 'CUSTOM',
};
  • Code Reference: libs/monitoring/src/enums/waterFlowEnums.js
  • Note: the UI "Week" selection maps to a CUSTOM param range internally; the provider converts between the visible selection and the query param type (WaterMonitoringFlowDataProvider.jsx).

3. Visualization Types

Graph Types (only two exist in the enum):

WaterFlowEnum.GraphType = {
BAR: 'BAR',
LINE: 'LINE',
};

View Types:

WaterFlowEnum.ViewType = {
TOTAL_FLOW: 'total_flow',
FLOW_RATE: 'flow_rate',
};
  • Code Reference: libs/monitoring/src/enums/waterFlowEnums.js
  • A pie chart is used inside the unit-detail page (WaterFlowPieChart.jsx) but is not a member of GraphType.

4. Unit Status & Alerts

Status metadata comes from WaterFlowEnum.UnitStatus (libs/monitoring/src/enums/waterFlowEnums.js):

Water Loss / Leakage Detection

  • Badge label: Water Loss Detected (not "Leakage Detected")
  • Background: #FFF0F0 · Border/Text: #8C0000
  • Icon: assets.images.leakageDetectedIcon
  • Detection: stable-flow anomaly alerts (AlertType.STABLE_FLOW = 'stable_flow_alert')

Threshold / Daily Limit

  • Badge label: Reached Daily Limit
  • Background: #FFF1ED · Border/Text: assets.colors.red (#DB6447)

Offline

  • Badge label: Units Offline
  • Background: #F5F5F5 · Border/Text: #747474

5. Status Grouping (not a filter API)

The controller groups units per subcategory during processing. Each returned subcategory carries thresholdCrossedUnits, offlineUnits, leakageDetectedUnits, stableFlowEnabledCount, leakageEnabled, and totalUnitsCount.

  • Code Reference: libs/monitoring/src/controller/monitoringController.js
  • Note: the active provider (WaterMonitoringFlowDataProvider) does not expose unitFilterConfig / setUnitFilterConfig. Those belong to the legacy FlowDataProvider (libs/monitoring/src/dataProvider/FlowDataProvider.jsx), which is not wired into the current FlowCategoryPage.

Architecture

Data Flow

FlowCategoryPage
↓ (wraps)
WaterMonitoringFlowDataProvider (Context) ← reads MonitoringStoreContext for persistent date params
↓ init()
MonitoringController.getCategoryDataWithOrdering(categoryId, params, rearranged)

MonitoringController.processCategoryData() → leakage enrichment + status grouping

MonitoringDataSource.getCategoryDataV2()

apiClient.get(Urls.categoryDataV2 → 'deviceDataV2')

Context state (consumptionData, labelData) → FlowIndividualCategoryCard
  • Code Reference: WaterMonitoringFlowDataProvider.jsx, monitoringController.js, dataSource/monitoring.js

Key Components

1. WaterMonitoringFlowDataProvider

  • Location: libs/monitoring/src/dataProvider/WaterMonitoringFlowDataProvider.jsx
  • Exposes: isLoading (alias loading), consumptionData, labelData, params, setParams, categoryId, visibleTimeSelection, setVisibleTimeSelection, rearrangedSubCategories, setRearrangedSubCategories
  • Note: categoryId is hard-coded to StandardCategoryTypeUppercase.SOURCE_CATEGORY (:25).

2. MonitoringController

  • Location: libs/monitoring/src/controller/monitoringController.js
  • Methods: getCategoryDataWithOrdering(), processCategoryData(), prepareUnits(), formatFlowGraphData(), getCompareData(), getLeakageAlertsData(), getUnitGranularData(), getCategoryGranularData()

3. MonitoringDataSource

  • Location: libs/monitoring/src/dataSource/monitoring.js
  • Methods: getCategoryDataV2(), getCompareData(), getLeakageAlertsData(), getUnitGranularData(), getCategoryGranularData()

4. MonitoringStore (MonitoringStoreContext)

  • Location: libs/monitoring/src/store/MonitoringStore.js
  • Role: shared monitoring cache + persistent date params (persistentDateParams, setPersistentDateParams), leakage-alerts cache. Provided app-wide by MonitoringStoreContextProvider in libs/components/src/pages/layouts/MainLayout.jsx.

5. Active page components

  • FlowCategoryPage (libs/monitoring/src/pages/flow/FlowCategoryPage.jsx)
  • WaterFlowHeader (.../pages/flow/components/WaterFlowHeader.jsx) — time selection + rearrange trigger
  • FlowIndividualCategoryCard (.../pages/flow/flowCategoryPage/components/FlowIndividualCategoryCard.jsx) — one card per subcategory
  • RearrangePopover (.../pages/flow/components/RearrangePopover.jsx) — drag-to-reorder subcategories
  • Note: BuildFlowPageLayout.jsx / FlowGraphComponent.jsx exist but belong to the legacy FlowDataProvider path and are not rendered by the current FlowCategoryPage.

API Integration

All endpoints go through apiClient with URL keys from libs/shared/src/services/api/urls.js.

Category flow data (V2)

GET deviceDataV2          // Urls.categoryDataV2  (urls.js)
Params: { category: 'SOURCE_CATEGORY', type: 'DAY', date1: '25/02/2026', date2: '25/02/2026' }

Water-loss / leakage alerts

GET alerts/unitGraph      // Urls.getLeakageAlertsData  (urls.js)
Params: { unitIds: 'UNIT_1,UNIT_2', date1, date2, type, alertType: 'stable_flow_alert' }
  • Only fetched when at least one unit has meta.alertsConfig.alertEnabled.stable_flow (monitoringController.js).

Two-unit comparison

GET /deviceDataV2/compare // Urls.compareData  (urls.js)

Unit granular data

GET /granular/unit        // Urls.granularUnitData  (urls.js)
Params: { date1, date2, unitId }

Usage Examples

Access flow data in a component

import { useContext } from 'react';
import { WaterMonitoringFlowDataContext } from '@aquagen-mf-webapp/monitoring';

function FlowComponent() {
const flowStore = useContext(WaterMonitoringFlowDataContext);
const { consumptionData, isLoading, params, setParams } = flowStore;

if (isLoading) return <Loader />;
if (!consumptionData?.data?.subCategories?.length) return <NoData />;

return consumptionData.data.subCategories.map((sub) => (
<div key={sub.id}>{sub.displayName}</div>
));
}

Change the time window

import { WaterFlowEnum } from '@aquagen-mf-webapp/monitoring';

flowStore.setParams({ type: WaterFlowEnum.TimeSelection.MONTH, date1, date2 });
// setParams === updatePersistentParams: also persists to MonitoringStore + localStorage

Reorder subcategories (drag & drop)

flowStore.setRearrangedSubCategories(newOrder);
// persisted via WaterMonitoringFlowHelper.writeRearrangedSubCategories (user-specific localStorage key)

Water-Loss (Leakage) Detection

  1. Each unit may carry meta.alertsConfig.alertEnabled.stable_flow (or legacy meta.leakageEnabled).
  2. processCategoryData() collects unit IDs with stable-flow enabled and calls getLeakageAlertsData() (alerts/unitGraph).
  3. LeakageDetectionHelper.updateUnitsWithLeakageData() marks affected units (hasLeakageDetected).
  4. LeakageDetectionHelper.getUnitStatusIcon() resolves the visual status from WaterFlowEnum.UnitStatus.
  • Code Reference: monitoringController.js; libs/monitoring/src/helper/LeakageDetectionHelper.js

Threshold (Daily Limit) Management

// Resolve the active threshold for the current time type
const activeThreshold = WaterFlowUtils.resolveThresholdForType(params, unit.meta);

// Determine whether a unit exceeded its limit
const isAbove = WaterFlowUtils.isAboveThreshold({ ...unit, safeValue: unit.value }, params.type);
  • Code Reference: monitoringController.js; libs/monitoring/src/helper/waterFlowUtils.js
  • findThresholdCrossedAt() walks the cumulative graph to report the crossing timestamp (monitoringController.js).

Edge Cases

ScenarioBehaviorReference
LoadingFlowCategoryPage shows a centered CustomLoader; isLoading starts trueFlowCategoryPage.jsx, WaterMonitoringFlowDataProvider.jsx
Empty / no dataRenders GenericInfo with a CloudOff icon and "Data Not Found" when consumptionData.data.subCategories is emptyFlowCategoryPage.jsx
API error / failureinit() wraps the fetch in try/catch, logs "Error initializing water monitoring data", and clears the loader in finally; previous data is retainedWaterMonitoringFlowDataProvider.jsx
Offline unitGrouped into offlineUnits; status resolves to UnitStatus.OFFLINE (Units Offline, grey #747474)monitoringController.js; waterFlowEnums.js
Null / undefined valuesUnits sorted with optional chaining on value; Formatter.valueFormatter(unit?.value) guards safeValue; graph reducer uses point?.y ?? point?.value ?? 0monitoringController.js
Background refreshInterval refresh calls init(false) (no loader flicker); only user/date changes call init(true)WaterMonitoringFlowDataProvider.jsx
Date param sourceInitial params come from URL navDate/navType, then LocalDB commonDate, then today; navDate/navType are stripped from the URL after readMonitoringStore.js
Custom vs WeekUI "Week" is stored as CUSTOM param type and mapped back for displayWaterMonitoringFlowDataProvider.jsx
Rearrange corruptionIf the stored order resolves to zero valid subcategories, the localStorage key is cleared and original order restoredmonitoringController.js
Rainwater / UWMS contextWhen isFromRainwater / isFromUwms, the page auto-redirects to the matching subcategory's first unit detailFlowCategoryPage.jsx
Leakage fetch failureThe leakage-alerts call is wrapped in its own try/catch; failure silently skips enrichment, category data still rendersmonitoringController.js
Permission deniedSidebar entry renders locked and routes to /feature_locked/MONITORING/FLOWsee Visibility Parameters

Dependencies

Shared store / services

  • AppStoreContextloginData, constantDate, isFromRainwater, isFromUwms, isUwmsContextActive (@aquagen-mf-webapp/shared/store/AppStore)
  • MonitoringStoreContext — shared cache + persistent date params (libs/monitoring/src/store/MonitoringStore.js)
  • apiClient, Urls, LocalDBInstance/LocalDBKeys (@aquagen-mf-webapp/shared/services)
  • AnalyticsService, AnalyticEvents (@aquagen-mf-webapp/shared/services, .../enums)

Controllers / data sources / helpers

  • MonitoringController, MonitoringDataSource
  • WaterMonitoringFlowHelper (selection + rearrange persistence), WaterFlowUtils, LeakageDetectionHelper, WaterFlowCompareHelper
  • Formatter (@aquagen-mf-webapp/shared/utils/formatter)

Navigation

  • NavigationHelper / navHelperInstance — route + lockedPath definitions (libs/shared/src/helper/navHelperInstance.js)

Shared component libs

  • @aquagen-mf-webapp/componentsFixedBar, GenericInfo, CustomLoader, If, SearchComponent, SubPageWrapper

External npm

  • react 19, react-router-dom, @mui/material + @mui/icons-material (CloudOff), lodash, moment, @iconify/react
  • Charts use recharts (see the graph components under pages/flow/**).

Visibility Parameters

Which apps register the feature: production, demo, rwi, uwms. Not lakepulse.

Permission tags

  • Parent Monitoring menu (METRICS): permissionId: 'WATER_MONITORING' (navHelperInstance.js). Note PermissionController.isPermitted returns true for WATER_MONITORING unconditionally (PermissionController.js), so the parent menu is effectively always visible.
  • Water Flow sidebar entry (SOURCE_PAGE): permissionId: StandardCategoryTypeUppercase.SOURCE_CATEGORY'SOURCE_CATEGORY', lockedPath: '/feature_locked/MONITORING/FLOW' (navHelperInstance.js).

How permission is derived: getAllUserPermissions() pushes each loginData.services[].categoryId into the permission list (PermissionController.js). A user with a SOURCE_CATEGORY service is therefore permitted for the Water Flow entry.

Sidebar lock state (SidebarAccess)

  • SOURCE_PAGE specifies no explicit access, so it uses the default LOCKED — shown as locked when the permission is missing; super users bypass (navHelperInstance.js).
  • LOCKED items route to their lockedPath (/feature_locked/MONITORING/FLOWFeatureLockedProductPage, apps/*/routes.js feature_locked/:pageKey/:featureId).

Super-user exception: SUPER_USER grants every tag except those starting with DISABLE_, plus ACCOUNT_SETTINGS and UWI_DASHBOARD (PermissionController.js).

PermissionWrapper usage: PermissionWrapper (libs/components/src/permissionWrapper/PermissionWrapper.jsx) wraps children in If/IfNot around PermissionController.isPermitted(tag, appStore.loginData, ignoreSuperUser); supports negate and ignoreSuperUser.

isDemo / subscription: the flow pages themselves are not directly gated by constants.isDemo; nav entries expose isDemoOption: false. Demo behavior is driven by the demo app registering the same routes.


Analytics Integration

import { AnalyticsService } from '@aquagen-mf-webapp/shared/services';
import { AnalyticEvents } from '@aquagen-mf-webapp/shared/enums';

AnalyticsService.sendEvent(AnalyticEvents.PAGE_VIEW, {}, true); // on mount
AnalyticsService.sendEvent(AnalyticEvents.FLOW_DATE_CHANGE, { params }); // on each init()
  • Events: PAGE_VIEW (page_view), FLOW_DATE_CHANGE (flow_date_change), FLOW_FILTER_APPLY (flow_filter_apply) — libs/shared/src/enums/analyticsEnum.js
  • Code Reference: WaterMonitoringFlowDataProvider.jsx

Troubleshooting

Page shows "Data Not Found"

consumptionData.data.subCategories is empty for the selected date/type. Verify the account has SOURCE_CATEGORY services and try a different date window.

Water-loss badge never appears

The unit lacks meta.alertsConfig.alertEnabled.stable_flow; the alerts call is skipped when no unit enables stable-flow (monitoringController.js).

Subcategory order looks wrong / resets

A stale rearrange order in localStorage resolved to zero valid subcategories and was cleared (monitoringController.js).

Graph not refreshing

Confirm constants.refreshDuration is set; the interval is cleared on unmount and re-created when params change (WaterMonitoringFlowDataProvider.jsx).



Last Updated: July 2026 Module Location: libs/monitoring/src/pages/flow/