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
| Path | Element | Purpose |
|---|---|---|
monitoring/source_category/SOURCE_CATEGORY (index) | FlowCategoryPage | Flow category landing page |
monitoring/source_category/SOURCE_CATEGORY/:detailCategoryId/:unitId | FlowUnitPage | Per-unit flow detail |
monitoring/source_category/compare_units | FlowUnitComparePage | Two-unit comparison |
monitoring/:detailCategoryId/:unitId | ConsumptionDetail | Consumption 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:
rwianduwmsredirect themonitoringindex tosource_category/SOURCE_CATEGORYand do not register theConsumptionDetaildrill-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
CUSTOMparam 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 ofGraphType.
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 exposeunitFilterConfig/setUnitFilterConfig. Those belong to the legacyFlowDataProvider(libs/monitoring/src/dataProvider/FlowDataProvider.jsx), which is not wired into the currentFlowCategoryPage.
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(aliasloading),consumptionData,labelData,params,setParams,categoryId,visibleTimeSelection,setVisibleTimeSelection,rearrangedSubCategories,setRearrangedSubCategories - Note:
categoryIdis hard-coded toStandardCategoryTypeUppercase.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 byMonitoringStoreContextProviderinlibs/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 triggerFlowIndividualCategoryCard(.../pages/flow/flowCategoryPage/components/FlowIndividualCategoryCard.jsx) — one card per subcategoryRearrangePopover(.../pages/flow/components/RearrangePopover.jsx) — drag-to-reorder subcategories- Note:
BuildFlowPageLayout.jsx/FlowGraphComponent.jsxexist but belong to the legacyFlowDataProviderpath and are not rendered by the currentFlowCategoryPage.
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
- Each unit may carry
meta.alertsConfig.alertEnabled.stable_flow(or legacymeta.leakageEnabled). processCategoryData()collects unit IDs with stable-flow enabled and callsgetLeakageAlertsData()(alerts/unitGraph).LeakageDetectionHelper.updateUnitsWithLeakageData()marks affected units (hasLeakageDetected).LeakageDetectionHelper.getUnitStatusIcon()resolves the visual status fromWaterFlowEnum.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
| Scenario | Behavior | Reference |
|---|---|---|
| Loading | FlowCategoryPage shows a centered CustomLoader; isLoading starts true | FlowCategoryPage.jsx, WaterMonitoringFlowDataProvider.jsx |
| Empty / no data | Renders GenericInfo with a CloudOff icon and "Data Not Found" when consumptionData.data.subCategories is empty | FlowCategoryPage.jsx |
| API error / failure | init() wraps the fetch in try/catch, logs "Error initializing water monitoring data", and clears the loader in finally; previous data is retained | WaterMonitoringFlowDataProvider.jsx |
| Offline unit | Grouped into offlineUnits; status resolves to UnitStatus.OFFLINE (Units Offline, grey #747474) | monitoringController.js; waterFlowEnums.js |
| Null / undefined values | Units sorted with optional chaining on value; Formatter.valueFormatter(unit?.value) guards safeValue; graph reducer uses point?.y ?? point?.value ?? 0 | monitoringController.js |
| Background refresh | Interval refresh calls init(false) (no loader flicker); only user/date changes call init(true) | WaterMonitoringFlowDataProvider.jsx |
| Date param source | Initial params come from URL navDate/navType, then LocalDB commonDate, then today; navDate/navType are stripped from the URL after read | MonitoringStore.js |
| Custom vs Week | UI "Week" is stored as CUSTOM param type and mapped back for display | WaterMonitoringFlowDataProvider.jsx |
| Rearrange corruption | If the stored order resolves to zero valid subcategories, the localStorage key is cleared and original order restored | monitoringController.js |
| Rainwater / UWMS context | When isFromRainwater / isFromUwms, the page auto-redirects to the matching subcategory's first unit detail | FlowCategoryPage.jsx |
| Leakage fetch failure | The leakage-alerts call is wrapped in its own try/catch; failure silently skips enrichment, category data still renders | monitoringController.js |
| Permission denied | Sidebar entry renders locked and routes to /feature_locked/MONITORING/FLOW | see Visibility Parameters |
Dependencies
Shared store / services
AppStoreContext—loginData,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,MonitoringDataSourceWaterMonitoringFlowHelper(selection + rearrange persistence),WaterFlowUtils,LeakageDetectionHelper,WaterFlowCompareHelperFormatter(@aquagen-mf-webapp/shared/utils/formatter)
Navigation
NavigationHelper/navHelperInstance— route + lockedPath definitions (libs/shared/src/helper/navHelperInstance.js)
Shared component libs
@aquagen-mf-webapp/components—FixedBar,GenericInfo,CustomLoader,If,SearchComponent,SubPageWrapper
External npm
react19,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). NotePermissionController.isPermittedreturnstrueforWATER_MONITORINGunconditionally (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_PAGEspecifies no explicitaccess, so it uses the defaultLOCKED— shown as locked when the permission is missing; super users bypass (navHelperInstance.js).LOCKEDitems route to theirlockedPath(/feature_locked/MONITORING/FLOW→FeatureLockedProductPage,apps/*/routes.jsfeature_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).
Related Documentation
- Water Stock Levels - Storage tank monitoring
- Water Quality Monitoring - Quality parameter monitoring
- Routes - Application routing for monitoring pages
- Permissions - Access control model
- API & Services - API client configuration
Last Updated: July 2026
Module Location: libs/monitoring/src/pages/flow/