Skip to main content

Water Balance

An interactive flow diagram that accounts for water moving through a site — sources, tanks, treatment, consumption, and recycled loops — rendered with React Flow. It answers "where did the water go, and does it add up?"

  • Route: /water_balance/graphWaterBalance (libs/waterBalance/src/WaterBalancePage.jsx)
  • Permission: WATER_BALANCE (gates the sidebar entry — the route element itself isn't wrapped; see Permissions)
  • Library: libs/waterBalance/ — standard dataSource → controller → store → components layering

The flow at a glance

The diagram wires typed nodes (tanks, pumps, valves, processes, meters) together with typed edges (raw vs recycled flows). A simplified physical flow:

Summary: Solid lines are raw water, dashed lines are recycled water (per the in-app legend). Consumption flows carry animated arrows. The node types and layout come entirely from the backend response — the frontend only maps each type string to a component.


Two views, two granularities

Both are switched client-side from already-fetched data — only a date change triggers a refetch.

ControlOptionsBackend keyNotes
View (WaterBalanceGraphType)Overview (SUMMARY) · Detailed (DETAIL)summary / diagramOverview = summary tiles; Detailed = the physical flow diagram
Granularity (WaterBalanceDateType)Day (HOUR) · Month (DATE)Drives the date-picker mode; changing it refetches

Multi-page sites expose a page dropdown (selectedPageId); the default page is pages.physical. Switching view or page remounts the graph (React Flow key = pageId_graphType), which resets any open node popups.


Node types — which node is which

The backend gives each node a type string; WaterBalanceGraphComponents.jsx maps it to a component (nodeTypes):

Detailed view (physical diagram)

typeComponentRepresentsRenders & stateOn click
CARDConsumptionNodeConsumption / meter readingValue + label; blinks red when limit reached; greyed offlineOpens the source unit's detail (new tab)
TANKWaterTankNodeStorage tankAnimated level + threshold lines; blinks red at threshold; detail only at zoom ≥ 0.75Opens the unit's stock page
OVALOvalNodeSource / output labelValue + label (pill)
PROCESSProcessNodeTreatment / process stepValue + label (box)
PUMPPumpNodePumpIcon only
VALVEValveNodeValve / flow-meterIcon only

Overview (summary tiles)

typeComponentRepresentsRenders & stateOn click
SUMMARY_DATASummaryDataNodeSummary metric tileValue + unit + label; formula tooltip
SUMMARY_DETAILSummaryDetailNodeGrouped metric (many units)Group value; blinks red if any child hits its limitToggles a dropdown of member units (each row opens that unit's page)
WATER_BALANCEWaterBalanceNodeHeadline balance figureValue as a percentage
SUMMARY_DIFFERENCESummaryDifferenceNodeDifference / reconciliationValue, no unit (pill)

Every node draws four source + four target Handles (one per side, hidden via markerStyle), and the graph uses connectionMode="loose" so edges can attach anywhere.


Edge types — which edge is which

Mapped in WaterBalanceGraphComponents.jsx (edgeTypes):

typeComponentAppearanceMeaning
customFlowEdgeFree poly-line through backend controlPoints, solid #006183Raw-water flow along a custom path
stepCustomStepEdgeOrthogonal smooth-step, solidRaw-water flow (grid-routed)
dashStepCustomDashEdgeSmooth-step, dashed (12 12)Recycled-water flow
  • Edges flagged data.isConsumption overlay AnimatedArrow — arrow glyphs that travel along the path (speed scales with distance) to visualize live flow.
  • In the downloaded image, custom/step are swapped to a non-animated step edge (CustomStepEdgeWithoutAnimation) so the still frame is clean; dashStep is kept.

Data & APIs

Water-balance data (the only data endpoint)

dataSource/waterBalance.jscontroller/waterBalanceContoller.js → store init():

EndpointGET waterBalance/data (Urls.waterBalanceData)
Paramsdate1 (DD/MM/YYYY), type (HOUR | DATE) — passed as query params
Triggered byinitial load · date change · date-type change · a 5-minute auto-refresh, only while viewing today

Overview↔Detailed and the page dropdown do not refetch — they re-render from the data already in the store.

Response shape (response.data.pages) — a map keyed by page id, always including a physical page:

{
"data": {
"pages": {
"physical": {
"id": "physical",
"siUnit": "kl",
"summary": { "nodes": [ /* typed nodes */ ], "edges": [ /* typed edges */ ] },
"diagram": { "nodes": [ ... ], "edges": [ ... ] }
}
// …other pages
}
}
}

The graph reads pages[selectedPageId][summary|diagram].nodes / .edges, and the single siUnit (default kl) is appended to values across the tiles.

Analytics events

EventWhen
PAGE_VIEWOn page mount
WATER_BALANCE_DATE_CHANGEWhen the date or granularity changes

(Both no-op when analytics is disabled — the backwards analyticsEnv flag; see Configuration & Security.)

State (WaterBalanceStore)

A React Context (WaterBalanceDataProvider / WaterBalanceDataContext) exposing balanceData ({ data: pages, siUnit }), graphType, params, isLoading, selectedPageId, openInfoPopup, openDownloadPopup, downloadContainerRef, and fullScreenLoader. See State Management for the pattern and API Call Flow for how the request is made.


Edge cases & guards

The feature handles several non-obvious states:

  • No diagram configured → when data has loaded but the current page+view has zero nodes, ContactForDiagram overlays a static message asking the user to request a diagram via the issue-reporting form. It makes no API/mail call.
  • Loading → a centered loader renders while isLoading; the view toggle is disabled and the download popup only mounts after load.
  • Fetch failure is swallowed → the data source catch logs and returns undefined; the store guards on it, so a failed request simply leaves the graph empty (no error UI) and falls through to the empty/contact state.
  • Auto-refresh is scoped to "today" → the 5-minute refresh interval is only created when the selected date is the current day.
  • constantDate override → if the app supplies a fixed date, it seeds and keeps params.date1 in sync (used by contexts that pin a date).
  • Limit / threshold alerts → consumption cards, tanks, and grouped summary tiles turn red and blink when a limit or storage threshold is reached.
  • Zoom-gated tank detail → tank threshold lines/values only appear at zoom ≥ 0.75.
  • View/page remount → switching view or page forces a fresh React Flow mount, resetting internal node state (e.g. an open summary-detail dropdown).

Export

The download control opens a dialog containing a static, non-interactive copy of the diagram (pan/zoom/drag/select disabled, fitView, still edges). The image is produced with html-to-image (toPng) over the popup's container ref (high pixelRatio, <style> nodes filtered out), then downloaded as a PNG. The filename encodes the date, the page id, and the logged-in username. A full-screen loader covers the capture. (The export is an image of the diagram — there is no data-table export currently wired in.)


Info popup / legend

The info control opens WaterBalanceInfoPopup, whose legend depends on the active view (infoMenuKey):

  • Overview (SUMMARY_INFO) — combined view, difference, zoom hint (Ctrl + scroll).
  • Detailed (DETAIL_INFO) — raw water, recycled water, limit reached, 90% filled, storage tank, no-flow-meter, pump, valve.

React Flow version

The diagram is built on reactflow v11 (the legacy package name; imports come from 'reactflow', e.g. ReactFlow, Handle, Position, BaseEdge, getSmoothStepPath, useStore).

Going forward: React Flow v12 ships as the renamed @xyflow/react package. A future upgrade would swap the import path (reactflow@xyflow/react) and adopt its minor API changes. Until then, keep new water-balance work on the v11 reactflow imports so the graph stays consistent.


Underlying libraries

PurposeLibrary
Flow diagramreactflow (v11)
Image exporthtml-to-image
UI / theming@mui/material (MUI 7), @emotion/react (blink keyframes)
Icons@mui/icons-material, @iconify/react
Datesmoment
Utilitieslodash

Exact versions live in package.json.


Code reference

FileRole
WaterBalancePage.jsxPage shell, provider, empty-state wiring
dataSource/waterBalance.jsThe waterBalance/data call
controller/waterBalanceContoller.jsFetch orchestration
store/WaterBalanceStore.jsContext state + refetch/auto-refresh logic
enums/waterBalanceEnums.jsView + granularity enums
components/BuildWaterBalanceGraph.jsxReact Flow host, view toggle, page dropdown, controls
components/WaterBalanceGraphComponents.jsxnodeTypes / edgeTypes maps
components/nodes/*, summaryNodes/*The node components
components/edges/*, animated/AnimatedArrow.jsxThe edge components
components/WaterBalanceDownloadPopup.jsxExport dialog
components/WaterBalanceInfoPopup.jsxLegend
components/ContactForDiagram.jsxNo-diagram fallback