Skip to main content

Dashboard

The app's landing page — a single-fetch snapshot of the facility's water system for a chosen day, laid out as cards. Almost every card is also a shortcut that deep-links into the matching monitoring page.

At a glance

Route/dashboardDashboardPage (libs/dashboard/src/DashboardPage.jsx)
PermissionLANDING_PAGE gates the sidebar entry; the route itself is not wrapped
Variant splitUWI_DASHBOARD — see below
Librarylibs/dashboard/ — standard dataSource → controller → store → components
Main endpointGET /landingPage/userData — feeds every standard card
Extra endpointsGET uwms/stats (STP section only) · mark-alert-read via the shared alerts controller
RefreshEvery 5 min, unconditionally
Date controlNo date picker — a Today / Yesterday toggle
Chartsrecharts donut + shared PieChartHover, DashboardLineGraph, WaterTank + a hand-rolled SVG gauge
What the Dashboard does NOT do
  • It does not fetch per card. One /landingPage/userData response feeds every standard card, sliced client-side. Only the STP section fetches separately.
  • It does not offer a date picker. There is only a Today / Yesterday toggle, and Yesterday reverts itself after 60 seconds.
  • It does not distinguish a backend failure from an unmapped account — both render PageNotFound.
  • A "Not Available" card is not a permission failure; it means the service is absent from your login data.
  • The UWI_DASHBOARD variant does not use a different endpoint. /deviceDataV2 belongs to libs/uwi, a different feature.
Two variants, chosen by permission

The page renders BuildDashboardPage inside a negated PermissionWrapper and BuildUWIDashboardPage inside a positive one, both keyed on UWI_DASHBOARD. So users with that permission get the slimmer UWI dashboard and everyone else gets the standard one. UWI_DASHBOARD is excluded from the super-user auto-grant, so even super users see the standard view unless it is explicitly assigned.


One fetch, many cards

Summary: One call feeds every standard card, sliced client-side. Only the STP section fetches separately. The alert list is a controller-side merge of the general and offline "today" buckets, not a separate request.


What each card renders

CardReadsService gateEmpty copyOn click
Water IntakegraphData.intakeSOURCE_CATEGORY"No Intake"Source monitoring; a legend row opens that unit
Water UsedgraphData.consumptionSOURCE_CATEGORY"No Consumption"Source monitoring; a legend row opens that unit
Water RecycledgraphData.recycledWaterSOURCE_CATEGORY"No Recycled Water"Source monitoring; a legend row opens that unit
Real-Time Water GraphgraphData (whole)receives checkServiceExist
Water StockgraphData via StockSummarySTOCK_CATEGORYStock monitoring
Water Quality IndexgraphData.qualityQUALITY_CATEGORYQuality monitoring
Energy ConsumptiongraphData.energyENERGY_CATEGORYEnergy monitoring (energy nav helper)
Groundwater LevelstoreGROUND_WATER_LEVEL"No Data Found"Ground-water monitoring
RainfallstoreRWI permission, not a service/rwi
STPuwms/stats— (hides itself if empty)UWMS plant view — same tab
AlertsdashboardData.formattedAlertsArray"No alerts found!"Per-category monitoring deep link and marks the alert read
Insightsdummy datademo builds only (constants.isDemo)

The three pie cards share one "View Breakup" / "Collapse Breakup" toggle — it expands all three at once.

Standard layout, region by region
  • Left column
    • A bordered group holding Water Intake · Water Used · Water Recycled plus the shared breakup toggle
    • Real-Time Water Graph (≈63%) beside Water Stock (≈32%)
    • Water Quality Index · Energy Consumption · a stacked pair of Groundwater and Rainfall
    • The STP section
  • Right rail (fixed 300 px from lg up)
    • {Today|Yesterday}'s Alerts
    • Insights — only in demo builds
UWI dashboard variant

Driven by the same /landingPage/userData response, using UWI-specific components (DashboardUWIGraphSummary, DashboardUWIQualityDetails, DashboardUWIEnergyCard): Real-Time Water Graph, a groundwater strip, current water quality, energy, Stock, {day}'s Intake, {day}'s Consumption, and Alerts.

The UWI dashboard does not hit /deviceDataV2 — that endpoint belongs to libs/uwi, a different feature.


Card availability

Two independent mechanisms decide what a card shows:

Summary: PermissionWrapper and checkServiceExist are separate gates. A missing service produces a sales prompt; a missing permission produces a locked card. Neither is an error state.

MechanismHow it worksResult when it fails
PermissionWrapperSplits the whole page on UWI_DASHBOARD; wraps the Rainfall card on RWIThe negated branch renders — for Rainfall, a dedicated locked card
checkServiceExist(id)loginData.services.some(s => s.standardCategoryId === id)useContactSales renders "Not Available" + a "Get More Details" link and the contact-sales overlay

Day selection

There is no date picker. A single switcher drives params.date.

Summary: Yesterday is deliberately temporary — a 60-second countdown snaps the page back to Today, and the countdown restarts whenever a refresh lands.

The header reads Viewing {Today's|Yesterday's} Summary (ddd, MMM D); the countdown carries the tooltip "Automatically reverts to today's summary". Every switch sets isLoadingGraphData, which dims the page.


Data & APIs

ActionMethodPathTrigger
Dashboard dataGET/landingPage/userDataMount, day switch, and every 5 min
STP statsGETuwms/statsThe STP section, on mount / day change
Mark alert readshared alerts controller (updateNotificationRead)Clicking a dashboard alert
Request params and how the response is sliced

Params sent on every dashboard fetch:

ParamValue
dateDD/MM/YYYY
targetIndustryIdindustryId from the stored login response
alertsEnabledtrue
pastThreeMonthsEnabledfalse — and the day switch re-sets it to false
summaryDatatrue
typeMONTH

How the store maps the response:

Store fieldSource
dashboardDatathe whole data object
graphDatadata.summaryData — the source of nearly every card
siUnitdata.siUnit.SOURCE_CATEGORY
monthsDatadata.pastThreeMonths.cardData
formattedAlertsArrayinjected by the controller: the general + offline today alert arrays concatenated
isYesterdayderived by comparing params.date with yesterday

graphData keys consumed by cards: intake, consumption, recycledWater, quality, energy.

Alert click routing

Each row routes by standardCategoryId, always carrying the alert's own date (converted from DD-MM-YYYY to DD/MM/YYYY) and opening in a new tab:

CategoryPath
SOURCE_CATEGORY/monitoring/source_category/{categoryId}/{subCategoryId}/{unitId}
QUALITY_CATEGORY/monitoring/quality_category/{categoryId}/?unitId={unitId} plus &param= when meta.param exists
anything else/monitoring/{standardCategoryId lowercased}/{categoryId}/?unitId={unitId}

After navigating it calls updateNotificationRead with { id, date: date_key, type }. The card header's icon opens /alerts instead. See Alerts.

Analytics

EventPayload
PAGE_VIEW
DASHBOARD_DATE_CHANGEdashboardTrendGraphType: Today or Yesterday
DASHBOARD_CARD_CLICKpage: the GraphCardType (Intake, Consumption, Recycled, Rainwater, Quality, Stock, Borewell)
DASHBOARD_DETAILS_CLICKfired from a legend/unit row
DASHBOARD_ALERT_CLICKcategoryId, clickedTime, alertType, importanceLevel
LEAKAGE_LEARN_MORE_CLICK

Card internals worth knowing

Water Quality Index

Health scoreround(safe / (safe + unSafe) * 100), or 0 when there are no parameters
Bands100 Excellent · ≥ 80 Good · ≥ 60 Fair · below that Poor
GaugeHand-rolled inline SVG — a 70 px circle, 8 px stroke, rotated -90deg, progress via strokeDashoffset. No chart library
Unit orderingUnits with breached parameters first, then offline units before online ones
LegendWithin Safe Range · Crossed Safe Range · Offline

Energy Consumption

Total (with siUnit, defaulting to kWh), a daily average, and a recharts donut built from only the top three entries of energy.subCategoryTotal.

STP section

Three tiles from uwms/statsWater Treated (water_treated_today), Energy Consumed (energy_consumed_today) and Total Energy Saved (total_energy_saved) — each with a trend arrow from its matching *_change_percent. The card also carries an "Energy Savings Formula" explainer (actual power derived from energy over time, energy saved as the gap against a reference power). If the fetch returns nothing, the whole section unmounts.

Leakage banner

When loginData.leakageEnabled is set, a green "Water Loss Detection enabled." strip appears under the title on md+ screens; "Learn more" opens LeakageDetectionDialog.


Render states

StateWhat the user sees
First loadCentred loader at 80vh
Mid-refreshBackdrop loader, the page greyscaled to 70% with pointer events disabled
No data, or needsMapping setThe whole page falls back to PageNotFound
Fetch failedSame as above — the data source swallows the error and logs it
Per cardEmpty copy, "Not Available" sales prompt, or a locked variant (see above)
STP with no dataThe section removes itself
Small screensCards stack; search moves below the title

Visibility & access

Which apps register the route

/dashboardDashboardPage. The page itself is app-agnostic; the variant is chosen by permission rather than by app.

Route-level gating: none

The route element is unwrapped. LANDING_PAGE gates only the sidebar entry, so the URL renders for anyone who reaches it.

The four independent gates

GateMechanismEffect
Sidebar entryLANDING_PAGE permissionHides the nav item
Page variantPermissionWrapper on UWI_DASHBOARD, once negated and once notStandard vs UWI layout
Rainfall cardPermissionWrapper on RWIFalls back to a dedicated locked card
Per-card datacheckServiceExist(category) against loginData.services"Not Available" + contact-sales prompt

UWI_DASHBOARD is excluded from the super-user auto-grant. Subscription expiry is handled by the global overlay described in Permissions.


Dependencies

Shared store & services

ImportUsed for
AppStoreloginData (services, leakageEnabled), setMenuIconEnabled, setSelectedCategory
apiClient · Urls/landingPage/userData
AlertsControllerupdateNotificationRead when an alert row is clicked
AlertsHelperResolving the general / offline response keys the controller merges
UwiDashboardControlleruwms/stats for the STP section
AnalyticsService · AnalyticEventsThe six dashboard events
NavigationHelper · EnergyNavigationHelper · UwmsNavigationHelperCard click-through
useNavigateSearchParamsNew-tab navigation carrying the selected date
LocalDBInstance · LocalDBKeysindustryId for the request params
constantsrefreshDuration, isDemo

Component library

SubPageWrapper, FixedBar, Container, If, Expanded, CustomLoader, BackdropLoader, PageNotFound, GenericInfo, useContactSales + ContactSalesOverlay, PermissionWrapper, SearchComponent, LeakageDetectionDialog, PieChartHover, DashboardLineGraph, WaterTank.

Lib-internal

DashboardDataContext / DashboardDataProvider, DashboardEnum (card types, pie palettes, quality legend, day enum), and the Build* / Dashboard* card components.

External npm

recharts (the energy donut only), moment, lodash, @mui/material, @iconify/react. The quality gauge is hand-rolled inline SVG with no chart library; empty states use Lottie animations through GenericInfo.


Usage examples

Read a card's slice of the single response

import { useContext } from 'react';
import { DashboardDataContext } from '@aquagen-mf-webapp/dashboard/store/DashboardStore';

function IntakeTotal() {
const { graphData, siUnit, isLoading } = useContext(DashboardDataContext);
if (isLoading) return null;
// every standard card reads from graphData, which is response.data.summaryData
return <span>{graphData.intake?.total} {siUnit}</span>;
}

Switch the day, the way the header does

const { params, setParams, isYesterday, setIsLoadingGraphData } = useContext(
DashboardDataContext
);
import { DashboardEnum } from '@aquagen-mf-webapp/dashboard/enum/dashboardEnums';

setIsLoadingGraphData(true); // dims the page while the refetch lands
setParams({
...params,
date: isYesterday
? DashboardEnum.GraphDateType.TODAY.date
: DashboardEnum.GraphDateType.YESTERDAY.date,
pastThreeMonthsEnabled: false,
alertsEnabled: true,
});

Check whether a card should render its data

import { StandardCategoryTypeUppercase } from '@aquagen-mf-webapp/shared/enums';

const checkServiceExist = (id) =>
appStore.loginData.services.some((s) => s.standardCategoryId === id);

const canShowStock = checkServiceExist(
StandardCategoryTypeUppercase.STOCK_CATEGORY
);

Fire the dashboard analytics events

AnalyticsService.sendEvent(AnalyticEvents.PAGE_VIEW, {}, true);
AnalyticsService.sendEvent(AnalyticEvents.DASHBOARD_DATE_CHANGE, {
dashboardTrendGraphType: 'Yesterday',
});
AnalyticsService.sendEvent(AnalyticEvents.DASHBOARD_CARD_CLICK, {
page: DashboardEnum.GraphCardType.STOCK,
});

Troubleshooting

The whole page shows "not found" but the account is fine

BuildDashboardPage falls back to PageNotFound when dashboardData is missing or when dashboardData.needsMapping is set. A swallowed fetch error produces the identical screen, so check the console for Error Fetching Dashboard Data before concluding the account is unmapped.

A card says "Not Available" for a customer who has that feature

That is the service gate, not a permission. The category must appear in loginData.services as standardCategoryId. PermissionWrapper is not involved.

The UWI Consumption card is permanently locked

Known defect — the UWI layout reuses the shared PieChartSummary without passing hasPermission, and the component treats falsy as "no access".

The page keeps reloading while I read yesterday's data

The 5-minute interval has no date check, so it refetches regardless of the selected day. Yesterday additionally auto-reverts after 60 seconds.

Dates look wrong on a long-lived tab

GraphDateType.TODAY.date and .YESTERDAY.date are evaluated once when the enum module loads. A tab open across midnight compares against a stale date.

The STP section vanished

It unmounts itself when uwms/stats returns nothing — that is deliberate, not a failure.

The page crashed during load

The store reads data.pastThreeMonths.cardData and data.siUnit.SOURCE_CATEGORY without optional chaining, while always requesting pastThreeMonthsEnabled: false. A response omitting either block throws inside init.


Known issues & gotchas

Verified against the code
IssueWhereImpact
Unguarded response accessThe store reads data.pastThreeMonths.cardData and data.siUnit.SOURCE_CATEGORY with no optional chaining — while pastThreeMonthsEnabled is always sent as falseIf the backend omits either block the store throws during init, taking the page down
Unguarded session parseparams is initialised with JSON.parse(loginResponse).industryId at provider constructionThrows if the session is missing rather than redirecting
UWI Consumption card is always lockedThe UWI page reuses the shared PieChartSummary but never passes hasPermission, and the component treats falsy as "no access"That card permanently renders the "Not Available" sales state
"Today" is frozen at page loadGraphDateType.TODAY.date / .YESTERDAY.date are computed once when the enum module is evaluatedA tab left open across midnight compares against a stale date
The 5-minute refresh is unconditionalThe store's interval has no date checkIt keeps refetching even while you are looking at Yesterday — unlike Water Balance, which only polls on today
Errors collapse into "not found"The data source catches and logsA backend failure is indistinguishable from an unmapped account
Misleading affordanceDashboardUWIEnergyCard sets cursor: pointer but defines no click handlerLooks clickable, does nothing
Dead codeDashboardSummaryCards.jsx is never imported; the store exposes activePieIndex that nothing consumes; DashboardEnum.DatePickerType is unused and its month entry has a datepPickerType typoNoise when navigating the lib
Fragile double mappingcheckServiceExist re-maps its argument through StandardCategoryTypeUppercase, and callers already pass a mapped valueHarmless only because that enum's keys equal its values — a non-identity entry would silently break gating

Code reference

FileRole
DashboardPage.jsxShell, provider, header, leakage banner, day switcher, variant split
dataSource/dashboard.jsThe /landingPage/userData call
controller/dashboardController.jsMerges the general + offline today alerts
store/DashboardStore.jsContext state, params, 5-min interval
enum/dashboardEnums.jsCard types, pie palettes, quality legend, day enum
components/BuildDashboardPage.jsxStandard layout + service gating
components/BuildUWIDashboardPage.jsxUWI layout
components/DashboardGraphSummary.jsxPieChartSummary + StockSummary, incl. the locked state
components/DashboardQualityDetails.jsxHealth-score gauge + unit rows
components/DashboardEnergyCard.jsxTotals + recharts donut
components/DashboardTrendGraph.jsxReal-time line trend
components/DashboardGroundwaterCard.jsx · DashboardRainwaterCard.jsxThe stacked pair
components/DashboardUWMSSection.jsxSTP tiles from uwms/stats
components/DashboardAlertList.jsxRight-rail alerts + click routing
components/DashboardInsightsList.jsxDemo-only insights