Skip to main content

Water Usage Ratio

A data-entry and reporting tool for Water Usage Ratio (WUR) — how much water is consumed per unit of production. Operators log daily production quantities; the report shows borewell water against production for each day in a range.

At a glance

Route/water_ratioWaterRatioPage (libs/waterRatio/src/WaterRatioPage.jsx)
PermissionWATER_RATIO, in the Efficiency nav group; the route itself is not wrapped
Librarylibs/waterRatio/ — standard dataSource → controller → store → components
Endpoint/waterRatio/one path, three verbs (GET / POST / PUT)
ChartsNone — a form and a table
ExportClient-side xlsx via the shared Download.EXCEL
Default entry dateYesterday
Default report rangeStart of the current month → yesterday
Report range cap30 days
TenancySingle-tenant — the plant layout is hardcoded
What Water Usage Ratio does NOT do
  • It does not compute the per-day WUR. The server returns row.WUR; the browser only rounds it and computes the totals row.
  • It does not accept decimals, despite the input advertising a decimal keyboard.
  • It does not use a separate report endpoint. The report is the same GET over a date range.
  • It does not export CSV. The "Download CSV" button produces an .xlsx.
  • It is not multi-tenant. Lines, bottle sizes, and factors are code constants.
  • It fires no analytics events at all.

How the ratio is built

Summary: The browser only computes production (quantity × factor) and the totals row. The per-day WUR is supplied by the server — the frontend just rounds it to two decimals.


The hardcoded plant layout

Two production lines, five fixed bottle-size fields each, each with its own multiplication factor:

Line (display name)FieldFactorTotals as
Production Line-1250ml OLD Line6Beverage 1
400ml NEW Line9.6
750ml OLD Line18
2250ml OLD Line20.25
2500ml OLD Line15
Production Line-2250ml NEW Line6Beverage 2
500ml NEW Line12
1000ml NEW Line15
1500ml NEW Line18.5
2250ml NEW Line20.25

Total Production is Beverage 1 + Beverage 2, displayed as "Total Production 1 & 2 : n Ltrs".

This layout is not configurable

Field names, factors, and line names all live in helper/layoutFields.js, helper/waterRatioHelper.js, and enum/productionLineEnum.js. Onboarding a second plant means editing code, not configuration.

Wire format — field names are prefixed on the way out

ProductionLineEnum translates each display field into a prefixed API key and back:

Display fieldAPI key
250ml OLD Line (Line 1)P1_250ml OLD Line
2500ml OLD Line (Line 1)P1_2500ml OLD Line
250ml NEW Line (Line 2)P2_250ml NEW Line
2250ml NEW Line (Line 2)P2_2250ml NEW Line

getConvertedName(line, field) maps outward, getOriginalName(key) maps back by scanning both maps. Unrecognised names pass through unchanged, so a typo silently becomes its own key rather than raising an error.


The entry flow

Summary: Mode decides the verb — New Entry posts, Edit puts. Switching to New Entry clears any loaded record so you cannot accidentally re-post an existing day.

AreaWhat it showsAction
Mode selectorNew Entry / EditSwitching to New Entry clears the form
Reading DateRead-only, greyed, fixed time 5:00
Consumed DateDate picker, max = yesterday
Fetch DataLoads one day's recordDisabled in New Entry
Entry formOne numeric field per bottle size, live per-line and grand totalsDigits only
UploadSaves the formDisabled until Total Production is non-zero
Success cardA dismissible bannerGreen on success, red on failure
ReportsOpens the report dialog

The report

SourceThe same GET /waterRatio/ with { startDate, endDate, type: 'monthly' }
DatareportData.dailyData — one row per day
ColumnsThe dynamic production-line fields, then Total Production, Total Borewell Receipt (Ltrs), and WUR
Borewell litresWValue × 1000 — the server sends kilolitres
Per-day WURrow.WUR, rounded to 2 dp — server-computed
Totals row WURtotalBorewellSum / totalProductionSumbrowser-computed
Range cap30 days
Empty state"Click on fetch data to get the report!"
ExportButton reads "Download CSV" but produces Production_Line_Report_{start} to {end}.xlsx

Closing the dialog clears both the range and the fetched data.


Data & APIs

All three operations hit the same path, differing only by verb:

ActionMethodParams sentTrigger
Fetch one dayGET{ startDate, type: 'daily' }Fetch Data in Edit mode
Fetch a rangeGET{ startDate, endDate, type: 'monthly' }Fetch Data in the report dialog
CreatePOST{ date, time: '5:00' } + the form bodyUpload in New Entry
UpdatePUT{ date, time: '5:00' } + the form bodyUpload in Edit

The controller rewrites the params for writes only — startDate becomes date, and a fixed time of 5:00 is always attached. Single-day GETs take response[0]: the API returns an array even for one day.

No analytics events are fired anywhere in this feature.

State

WaterRatioStore (WaterRatioProvider / WaterRatioContext) exposes ratioData, reportData, params, reportParams, selectedMode, operationStatus, isLoading, and the getRatioData / uploadNewData / patchExistingData / hideSuccessCard actions. See State Management and API Call Flow.


Render states

StateWhat the user sees
New Entry selectedBlank form; Fetch Data disabled
Total Production is zeroUpload disabled
Save succeededGreen card, "Data Updated Successfully"
Save failedRed card, "Unable to save data, try in some time"
GET failedNothing — the error is logged and the form simply stays empty
Report not fetched"Click on fetch data to get the report!"; Download disabled
Report closedRange and data reset

Visibility & access

Which apps register the route

/water_ratioWaterRatioPage.

Route-level gating: none

The route element is unwrapped. WATER_RATIO gates the sidebar entry only.

Inside the Efficiency submenu, beside Water Neutrality, True Cost, Labs, and the SCADA entries. Subscription expiry is handled by the global overlay described in Permissions.

The real constraint is the hardcoded layout

Even with the permission, this page is only meaningful for the single beverage plant whose lines, bottle sizes, and multiplication factors are compiled into helper/layoutFields.js, helper/waterRatioHelper.js, and enum/productionLineEnum.js.


Dependencies

Shared store & services

ImportUsed for
apiClient · UrlsGET / POST / PUT against /waterRatio/
DateFormatterFormatting the default entry and report dates
DownloadDownload.EXCEL for the report export

No analytics service is wired into this feature.

Component library

AppDatePickerSelection for both the consumed-date picker and the report range, plus MUI primitives for the form, dialog, table, and the success banner.

Lib-internal

WaterRatioProvider / WaterRatioContext, WaterRatioController (write-param rewriting), WaterRatioDataSource, WaterRatioHelper (modes + factors), layoutFields, ProductionLineEnum.

External npm

xlsx (through the shared download helper), moment, @mui/material, lodash. No charting library — this feature is a form and a table.


Usage examples

Read a loaded day's record

import { useContext } from 'react';
import { WaterRatioContext } from '@aquagen-mf-webapp/waterRatio/store/WaterRatioStore';

function LoadedDay() {
const { ratioData, isLoading } = useContext(WaterRatioContext);
if (isLoading) return null;
// single-day GETs return an array; the store already took element 0
return ratioData ? <span>Record loaded</span> : <span>No record for that day</span>;
}

Fetch a day, then a report range

const { params, setParams, reportParams, setReportParams, getRatioData } =
useContext(WaterRatioContext);

setParams({ startDate: '29/07/2026', type: 'daily' });
await getRatioData({ startDate: '29/07/2026', type: 'daily' });

setReportParams({ startDate: '01/07/2026', endDate: '29/07/2026', type: 'monthly' });
await getRatioData(reportParams, true); // the second argument routes into reportData

Switch mode and save

import { WaterRatioHelper } from '@aquagen-mf-webapp/waterRatio/helper/waterRatioHelper';

const { setSelectedMode, uploadNewData, patchExistingData } =
useContext(WaterRatioContext);

setSelectedMode(WaterRatioHelper.Modes.NEW); // also clears any loaded record
await uploadNewData(formBody); // POST
await patchExistingData(formBody); // PUT

Convert a field name to its wire key

import { ProductionLineEnum } from '@aquagen-mf-webapp/waterRatio/enum/productionLineEnum';

ProductionLineEnum.getConvertedName('Production Line1', '250ml OLD Line');
// 'P1_250ml OLD Line'
ProductionLineEnum.getOriginalName('P2_500ml NEW Line');
// '500ml NEW Line'

Derive litres from an entered quantity

import { WaterRatioHelper } from '@aquagen-mf-webapp/waterRatio/helper/waterRatioHelper';

const factor = WaterRatioHelper.MultiplicationFactor['Production Line1']['400ml NEW Line']; // 9.6
const litres = Number(quantity) * factor;

Troubleshooting

My decimal quantity won't type

The change handler tests /^[0-9]*$/ and discards anything else — silently. The field advertises inputMode: 'decimal', which is misleading; only whole numbers are accepted.

Upload is greyed out

It is disabled until Total Production is non-zero. Since totals are derived from the per-field quantities, an all-zero form can never be submitted.

Fetch Data does nothing

It is disabled in New Entry mode by design — switch to Edit to load a day.

I got an .xlsx but the button said CSV

The button is mislabelled; it calls Download.EXCEL. The file is genuinely a spreadsheet.

The downloaded filename looks broken

It embeds DD/MM/YYYY dates, and browsers rewrite path separators in the download attribute.

The totals row shows Infinity or NaN

The footer divides total borewell by total production with no zero guard. A range with zero production produces this.

The report crashed while rendering

The table calls row.WUR.toFixed(2) with no null check, so a day missing WUR throws.

A new entry reported "Data Updated Successfully"

Both the create and update paths use the same message. It did save.

A fetch failed but nothing told me

Read errors are caught, logged, and swallowed — only writes surface a card. An empty form after Fetch Data may mean a failed request rather than a missing record.


Known issues & gotchas

Verified against the code
IssueWhereImpact
Decimals are silently rejectedThe change handler tests /^[0-9]*$/, yet the field sets inputMode: 'decimal'The mobile keyboard offers a decimal point that the validator throws away, with no message
Mislabelled exportThe button reads "Download CSV" and calls Download.EXCELUsers get an .xlsx they did not expect
Slashes in the export filenameThe filename embeds DD/MM/YYYY datesBrowsers mangle path separators in the download attribute
No zero guard on the totals rowThe footer computes totalBorewellSum / totalProductionSum directlyA range with zero production yields Infinity or NaN in the totals row
Unguarded per-day WURThe table does row.WUR.toFixed(2) with no null checkA day missing WUR throws while rendering the report
"Updated" on createBoth uploadNewData and patchExistingData report "Data Updated Successfully"A brand-new entry is described as an update
Read failures are invisibleThe data source catches, logs, and returns undefined; only writes surface a cardA failed fetch is indistinguishable from a day with no data
Hardcoded single tenantLines, fields, and factors are code constantsCannot be reused for another plant without a code change
Unrecognised field names pass throughgetConvertedName / getOriginalName fall back to the inputA renamed field silently writes to a new, un-prefixed key

Code reference

FileRole
WaterRatioPage.jsxPage shell + provider
dataSource/waterRatioDataSource.jsGET / POST / PUT against /waterRatio/
controller/waterRatioController.jsRewrites write params to date + time: '5:00'
store/WaterRatioStore.jsContext state, mode handling, operation status
components/BuildWaterRatioPage.jsxOperations panel + entry form + totals
components/WaterRatioReportPopup.jsxReport dialog, table build-up, Excel export
components/WaterRatioSuccessCard.jsxSave confirmation banner
helper/layoutFields.jsLine names, field lists, per-line total names
helper/waterRatioHelper.jsModes + multiplication factors
enum/productionLineEnum.jsDisplay-name to P1_ / P2_ key mapping