Account Management
Comprehensive account and settings management system for configuring organization details, managing users, setting up SSO authentication, configuring alert emails, scheduling reports, and manually entering flowmeter readings.
Overview
The Account Management module provides three main sections:
- Account Info - View and manage categories, units, and thresholds
- Account Settings - Configure organization details, users, alerts, reports, and SSO (password-protected)
- Manual Node Entry - Manually enter flowmeter readings for offline or virtual devices
Location: libs/manageAccount/
Routes:
/manage_account- Landing page/manage_account/account_info- Account information/manage_account/account_settings- Account settings (password-protected)/manage_account/manual_node_entry- Manual flowmeter entry
Permissions:
ACCOUNT_SETTINGS- Required for account settings accessMANUAL_NODE_ENTRY- Required for manual flowmeter entry
Key Features
1. Account Landing Page
Central Hub with Three Cards:
Account Info Card
- Icon: ErrorOutlineOutlined
- Description: "Contains information such as Categories, Units and their respective thresholds"
- Action: Navigate to Account Info page
- Accessible: All users
Account Settings Card
- Icon: Settings
- Badge: "SSO Registration Here"
- Description: "Password required to view and edit settings"
- Password Verification: Requires current password to access
- Action: Verify password → Navigate to Account Settings
- Accessible: Users with
ACCOUNT_SETTINGSpermission, OTP login users excluded
Manual Node Entry Card (Optional)
- Visible: Only with
MANUAL_NODE_ENTRYpermission - Description: "Manually enter flowmeter readings"
- Action: Navigate to Manual Entry page
Additional Info:
- Displays company logo
- Shows joining date: "Joined Aquagen: DD/MM/YYYY"
- Next payment due date (hidden by default)
2. Account Info Page
View and manage account configuration data organized by service categories.
Features
Navigation Panel (Left Sidebar):
- Service category tabs (Consumption, Quality, Stock, etc.)
- Auto-scroll highlighting based on scroll position
- Point count badges showing number of units per category
- Back button to return to landing page
Main Content Area:
- Category-wise unit listings with expandable sections
- Unit details:
- Unit name
- Unit ID
- Associated thresholds (daily, hourly, quality parameters)
- Last updated timestamp
- Editable threshold values (if enabled)
- Save/Cancel buttons for changes
Password-Protected Save:
- Click Save → Enter password dialog
- Password verification before applying changes
- Success notification after save
- Auto-refresh data after update
Unsaved Changes Warning:
- Browser prompt when navigating away with unsaved changes
- Confirmation dialog before exit
Data Structure
AccountInfoPage: {
SERVICE_CATEGORY_1: {
name: "Consumption",
data: [
{
column1: "Category Name",
column2: [
{
unitName: "Borewell 1",
unitId: "UNIT_123",
threshold: 1000,
hourlyThreshold: 50,
lastUpdated: "25/02/2026 10:30 AM"
}
]
}
]
}
}
3. Account Settings Page
Comprehensive settings management with 5 sections (password-protected).
Password Verification
- Enter current password on landing page
- Validates credentials before allowing access
- OTP users cannot access account settings
- Session-based verification (resets on page reload)
Section 1: Account Details
Organization Information:
- Industry Name: Organization display name
- Industry Id: Unique identifier
- Address: Full address with street, city, state, ZIP
- Contact Person: Primary contact name
- Email: Organization email
- Phone: Contact number
- Website: Organization website URL
- Logo Upload: Company logo (image file upload)
Editable Fields:
- All text fields editable inline
- Image upload with preview
- Real-time validation for email and phone
- Character limits enforced
Section 2: Users Management
User List Table:
- User name, email, role
- Active/Inactive status
- Last login timestamp
- Edit/Delete actions
Add New User:
- Name, email, password
- Role selection (Admin, Manager, Operator, Viewer)
- Permission assignment (33+ individual permissions)
- Email verification on creation
Edit User:
- Update user details
- Change role and permissions
- Reset password
- Activate/Deactivate account
Delete User:
- Confirmation dialog
- Cannot delete yourself
- Cannot delete last admin
Section 3: Alert Email Configuration
Alert Email Recipients:
- Add multiple email addresses for alert notifications
- Configure alert types per email:
- Consumption alerts
- Leakage detection
- Offline device alerts
- Quality threshold violations
- Energy alerts
- Email validation
- Remove email recipients
Alert Frequency:
- Instant alerts
- Daily digest
- Weekly summary
Section 4: Reports Configuration
Scheduled Reports:
-
Report Types:
- Consumption report
- Quality report
- Stock report
- Alert summary report
- Water balance report
-
Schedule Configuration:
- Daily (specific time)
- Weekly (day and time)
- Monthly (date and time)
-
Email Recipients: Multiple email addresses
-
Format: PDF or Excel
-
Sections: Select report sections to include
Report Preview:
- Preview report before scheduling
- Test email delivery
Section 5: SSO Login Configuration
Single Sign-On Setup:
Supported SSO Providers:
- Microsoft Azure AD (MSAL)
- Google Workspace (OAuth)
Azure AD Configuration:
- Tenant ID
- Client ID
- Client Secret
- Redirect URI
- Domain whitelist for allowed email domains
Google OAuth Configuration:
- Client ID
- Client Secret
- Authorized domains
- Redirect URI
SSO User Management:
- Add SSO email addresses
- Domain-based auto-provisioning
- Map SSO users to AquaGen roles
- Remove SSO users
- Test SSO login
SSO Changes Detection:
- Tracks SSO configuration changes separately
- Warns before navigating away with unsaved SSO changes
- Validates email domains against configured domains
4. Manual Node Entry Page
Manually enter flowmeter readings for offline or virtual devices.
Use Cases
- Offline flowmeters without connectivity
- Virtual nodes without physical sensors
- Backup data entry when sensors fail
- Manual meter readings from field
Features
Device List:
- Shows all manual entry-enabled flowmeters
- Device name, last value, last updated time
- Device type (RS485 or Pulse meter)
Data Entry Fields:
- Date Picker: Select reading date (cannot be before device creation date)
- Time Picker: Select reading time (30-minute intervals, rounded)
- Value Input: Enter meter reading with validation
- Min Time Validation: Cannot enter time before device creation time on same date
Validation Rules:
RS485 Meters:
- Value cannot be less than previous value (cumulative reading)
- Error message: "Value cannot be less than the previous value"
Pulse Meters:
- Value cannot be negative (incremental pulses)
- Error message: "You cannot enter negative values"
Save Behavior:
- Save button enabled only when valid values entered
- Confirm dialog before submitting
- Success/error message after save
- Auto-refresh device list after successful save
Unsaved Changes Protection:
- Browser warning before page close
- Dialog when clicking Back with unsaved entries
- Option to save or discard changes
Device Display:
Unit Name: Borewell 1
Last Updated: 1250 Kilo Liters on 24/02/2026 03:30 PM
[Date Picker] [Time Picker] [Value Input]
Architecture
Data Flow
Key Components
1. AccountStoreContextProvider
- Purpose: Global state management for account data
- Location:
libs/manageAccount/src/store/AccountStore.js - State Variables:
accountScreenData- All account datalastUpdatedTime- Last data refresh timeupdatedOperations- Pending changes arrayisVerifiedForAccountSetting- Password verification statusisVerifiedForManualNodeEntry- Manual entry verificationisLoading- Loading state
Context Value:
{
accountScreenData: {
joiningDate: "01/01/2024",
AccountInfoPage: {...},
AccountSettingPage: {
industryName: "ABC Corp",
email: "info@abc.com",
users: [...],
alertEmails: [...],
reports: [...],
sso: [...]
}
},
isLoading: false,
isVerifiedForAccountSetting: false,
fetchAccountData: () => {},
updateAccountInfo: () => {},
fetchLastUpdatedTime: (path) => {},
clearLastUpdatedTime: () => {}
}
2. AccountLandingPage
- Purpose: Main entry point with three action cards
- Location:
libs/manageAccount/src/subPages/AccountLandingPage.jsx - Features:
- Account Info card (all users)
- Account Settings card (password-protected)
- Manual Node Entry card (permission-based)
- Company logo display
- Joining date display
3. AccountInfoPage
- Purpose: View/edit categories and units
- Location:
libs/manageAccount/src/subPages/AccountInfoPage.jsx - Features:
- Left navigation panel
- Category sections
- Unit tables with thresholds
- Password-protected save
- Unsaved changes warning
4. AccountSettingPage
- Purpose: Organization and system settings
- Location:
libs/manageAccount/src/subPages/AccountSettingPage.jsx - Features:
- 5 section tabs (Account, Users, Alerts, Reports, SSO)
- Scroll-based active section highlighting
- Save/Cancel buttons
- Changes detection
- Navigation blocker for unsaved changes
- Responsive mobile view with tabs
5. ManualNodePage
- Purpose: Manual flowmeter data entry
- Location:
libs/manageAccount/src/subPages/ManualNodePage.jsx - Features:
- Device list with last readings
- Date/time pickers
- Value input with validation
- Save/cancel with confirmation
- Unsaved changes warning
6. ManualNodeStore
- Purpose: State management for manual node entry
- Location:
libs/manageAccount/src/store/ManualNodeStore.js - State Variables:
manualNodeData- List of manual entry devicesnewValues- Pending entries objectshowMessage- Error/success messagessaveButtonDisable- Validation statefullScreenLoader- Loading state
API Integration
Get Account Data
GET /accounts/details
Response: {
joiningDate: "01/01/2024",
AccountInfoPage: {
SERVICE_CATEGORY_1: {
name: "Consumption",
data: [...]
}
},
AccountSettingPage: {
industryName: "ABC Corp",
industryId: "IND_123",
address: "123 Main St, City, State 12345",
email: "info@abc.com",
phone: "+1234567890",
website: "www.abc.com",
logo: "https://...",
users: [...],
alertEmails: [...],
reports: [...],
sso: [...]
}
}
Update Account Info
PATCH /accounts/info
Request Body: {
operations: [
{
op: "replace",
path: "/units/UNIT_123/threshold",
value: 1200
},
{
op: "replace",
path: "/units/UNIT_123/hourlyThreshold",
value: 60
}
]
}
Response: {
status: 200,
message: "Account info updated successfully"
}
Update Account Settings
POST /accounts/settings
Request Body: {
industryName: "ABC Corp",
address: "123 Main St",
email: "info@abc.com",
phone: "+1234567890",
website: "www.abc.com",
users: [...],
alertEmails: [...],
reports: [...],
sso: [...]
}
Response: {
status: 200,
message: "Settings updated successfully"
}
Upload Logo
POST /accounts/settings/logo
Content-Type: multipart/form-data
Request: FormData with 'image' field
Response: {
status: 200,
logoUrl: "https://storage.com/logos/abc-corp.png"
}
Get Manual Node Data
GET /virtualNodes/data
Response: {
data: [
{
unitId: "UNIT_123",
unitName: "Borewell 1",
value: 1250.5,
flowFactor: 1000, // 1 = Liters, 1000 = Kilo Liters
meterType: "RS485", // or "PULSE"
createdOn: "24/02/2026 03:30 PM"
}
]
}
Post Manual Node Data
POST /virtualNodes/data
Request Body: {
entries: [
{
unitId: "UNIT_123",
value: 1300.5,
date_time: "25/02/2026T10:30:00"
}
]
}
Response: {
status: 200,
message: "Manual entries saved successfully"
}
Usage Examples
1. Navigate to Account Settings with Password
import { useContext, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { AccountStoreContext } from '@aquagen-mf-webapp/manageAccount/store/AccountStore';
import { LoginController } from '@aquagen-mf-webapp/components/pages/login/loginController';
function AccountSettingsAccess() {
const accountStore = useContext(AccountStoreContext);
const navigate = useNavigate();
const [password, setPassword] = useState('');
const [error, setError] = useState(false);
const handleAccessSettings = async () => {
const username = appStore?.loginData?.username;
if (username && password) {
const response = await LoginController.defaultLogin(username, password);
if (response) {
accountStore.setIsVerifiedForAccountSetting(true);
navigate('./account_settings');
setError(false);
} else {
setError(true);
}
}
};
return (
<TextField
type="password"
value={password}
error={error}
helperText={error ? "Wrong password" : ""}
onChange={(e) => setPassword(e.target.value)}
/>
);
}
2. Update Account Info with Operations
const updateThreshold = (unitId, newThreshold) => {
const operations = [
{
op: "replace",
path: `/units/${unitId}/threshold`,
value: newThreshold
}
];
await AccountController.updateAccountInfo({ operations });
await accountStore.fetchAccountData(); // Refresh
};
3. Add SSO Email
const addSSOEmail = () => {
const newSSOUser = {
email: "user@company.com",
domain: "company.com",
provider: "azure", // or "google"
isNew: true,
isEdited: false,
isDeleted: false
};
setFormData(prev => ({
...prev,
sso: [...prev.sso, newSSOUser]
}));
setChangesHappened(true);
};
4. Manual Node Entry with Validation
import { ManualNodeHelper } from '@aquagen-mf-webapp/manageAccount/helper/manualNodeHelperInstance';
const handleManualEntry = (nodeData, enteredValue) => {
// Validate RS485 meter (cumulative)
if (
enteredValue < nodeData.value &&
nodeData.meterType === ManualNodeHelper.i.deviceTypeEnum.RS485.id
) {
setError('Value cannot be less than the previous value');
return;
}
// Validate Pulse meter (incremental)
if (
enteredValue < 0 &&
nodeData.meterType === ManualNodeHelper.i.deviceTypeEnum.PULSE.id
) {
setError('You cannot enter negative values');
return;
}
// Valid entry
const entry = {
unitId: nodeData.unitId,
value: Number(enteredValue),
date_time: `${date.format('DD/MM/YYYY')}T${time.format('HH:mm')}:00`
};
manualNodeStore.setNewValues(prev => ({
...prev,
[entry.unitId]: entry
}));
};
5. Block Navigation with Unsaved Changes
import { useBlocker } from 'react-router-dom';
const blocker = useBlocker(({ currentLocation, nextLocation }) => {
if (currentLocation.pathname !== nextLocation.pathname) {
if (changesHappened || ssoChangesHappened) {
setIsBlocked(true);
return true; // Block navigation
}
}
setIsBlocked(false);
return false; // Allow navigation
});
// Show exit confirmation dialog
<AccountSettingsExitCard
open={isBlocked}
onExit={() => {
blocker.proceed(); // Allow blocked navigation
}}
onStay={() => {
blocker.reset(); // Cancel blocked navigation
}}
/>
Manual Node Helper Configuration
ManualNodeHelper.i.deviceTypeEnum = {
RS485: {
id: 'RS485',
displayName: 'RS485 Meter',
valueType: 'Cumulative Reading',
validation: 'Cannot decrease'
},
PULSE: {
id: 'PULSE',
displayName: 'Pulse Meter',
valueType: 'Pulse Count',
validation: 'Cannot be negative'
}
};
// Round time to nearest 30 minutes
ManualNodeHelper.i.roundToNearest30Minutes = (momentTime) => {
const minutes = momentTime.minutes();
const roundedMinutes = minutes < 30 ? 0 : 30;
return momentTime.clone().minutes(roundedMinutes).seconds(0);
};
// Get minimum allowed time based on device creation
ManualNodeHelper.i.getMinTime = (createdOn, selectedDate) => {
if (selectedDate.isSame(createdOn, 'day')) {
return createdOn; // Same day: cannot enter before creation time
}
return moment().startOf('day'); // Different day: any time allowed
};
// Get value type with unit
ManualNodeHelper.i.getValueTypeWithUnit = (meterType, flowFactor) => {
const unit = flowFactor === 1000 ? 'Kilo Liters' : 'Liters';
return `Value (${unit})`;
};
SSO Helper Configuration
// Initialize SSO users with tracking fields
SSOHelper.initializeSSOUsers = (ssoUsers) => {
return ssoUsers.map(user => ({
...user,
isNew: false,
isEdited: false,
isDeleted: false
}));
};
// Check if SSO changes happened
SSOHelper.checkSSOChanges = (formData, settingData) => {
const currentSSO = formData.sso || [];
const originalSSO = settingData?.sso || [];
// Check for new users
if (currentSSO.some(user => user.isNew)) return true;
// Check for edited users
if (currentSSO.some(user => user.isEdited)) return true;
// Check for deleted users
if (currentSSO.some(user => user.isDeleted)) return true;
// Check for count mismatch
if (currentSSO.length !== originalSSO.length) return true;
return false;
};
Best Practices
1. Always Verify Password for Settings
useEffect(() => {
if (!accountStore.isVerifiedForAccountSetting) {
navigate(`../../${NavigationHelper.i.routes.MANAGE_ACCOUNT}`);
return;
}
return () => {
accountStore.setIsVerifiedForAccountSetting(false); // Reset on unmount
};
}, []);
2. Track Changes for Exit Warning
const [changesHappened, setChangesHappened] = useState(false);
// Mark changes on any field edit
const handleFieldChange = (field, value) => {
setFormData(prev => ({ ...prev, [field]: value }));
setChangesHappened(true);
};
// Reset after successful save
const handleSave = async () => {
await saveChanges();
setChangesHappened(false);
};
3. Validate Email Inputs
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const handleEmailAdd = (email) => {
if (!validateEmail(email)) {
setError('Invalid email format');
return;
}
addEmail(email);
};
4. Handle Image Uploads Properly
const handleLogoUpload = async (file) => {
if (!file) return;
// Validate file type
if (!file.type.startsWith('image/')) {
setError('Please upload an image file');
return;
}
// Validate file size (e.g., max 5MB)
if (file.size > 5 * 1024 * 1024) {
setError('Image size must be less than 5MB');
return;
}
const formData = new FormData();
formData.append('image', file);
const response = await AccountDataSource.accountSettingsLogoUpdate(formData);
if (response) {
setFormData(prev => ({ ...prev, logo: response.logoUrl }));
}
};
5. Debounce Password Verification
import { debounce } from 'lodash';
const passVerifyandNavigate = debounce(async (password) => {
if (password.includes(' ')) {
setPasswordError(true);
setErrorMessage("Password shouldn't contain spaces");
return;
}
const response = await LoginController.defaultLogin(username, password);
// Handle response...
}, 500); // Wait 500ms after last keystroke
Troubleshooting
Cannot Access Account Settings
Cause: Not verified or OTP login Solution: Check verification status and login type
if (loginType === 'OTP') {
// OTP users cannot access settings
return <Typography>Account settings not available for OTP login</Typography>;
}
if (!accountStore.isVerifiedForAccountSetting) {
navigate('/manage_account');
}
Manual Node Save Disabled
Cause: Validation errors or no changes Solution: Check validation and newValues
const isSaveDisabled =
!_.keys(manualNodeStore.newValues).length || // No entries
manualNodeStore.isLoading || // Currently loading
manualNodeStore.saveButtonDisable; // Validation errors
// Check for validation errors
Object.values(manualNodeStore.newValues).forEach(entry => {
if (hasValidationError(entry)) {
manualNodeStore.setSaveButtonDisable(true);
}
});
SSO Changes Not Detected
Cause: Missing change tracking flags Solution: Ensure isNew/isEdited/isDeleted flags are set
// When adding SSO user
const newUser = {
email: "user@company.com",
isNew: true, // Mark as new
isEdited: false,
isDeleted: false
};
// When editing SSO user
const editUser = (userId) => {
setFormData(prev => ({
...prev,
sso: prev.sso.map(user =>
user.id === userId
? { ...user, isEdited: true } // Mark as edited
: user
)
}));
};
Responsive Design
Desktop (md+)
- Sidebar navigation visible
- Two-column layout (navigation + content)
- Sticky sidebar for easy section access
- Full-width forms
Mobile (xs-sm)
- Tabs at top instead of sidebar
- Single-column layout
- Collapsible sections
- Full-width inputs
- Touch-friendly buttons
Responsive Breakpoint:
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
Analytics Tracking
// Page view
AnalyticsService.sendEvent(AnalyticEvents.PAGE_VIEW, {}, true);
// Account info access
AnalyticsService.sendEvent(AnalyticEvents.ACCOUNT_INFO_CLICK);
// Account settings access
AnalyticsService.sendEvent(AnalyticEvents.ACCOUNT_SETTINGS_CLICK);
// Manual node entry save
AnalyticsService.sendEvent(
AnalyticEvents.MANUAL_NODE_VALUE_CHANGE_SAVE_CLICK,
unitIds
);
Related Documentation
- Permissions - ACCOUNT_SETTINGS and MANUAL_NODE_ENTRY permissions
- Authentication - Login methods and password verification
- Routes - Account management routes
- API & Services - API integration details
Last Updated: February 2026
Module Location: libs/manageAccount/