Permissions & Access Control
Complete guide to the permission system, permission wrappers, and role-based access control in AquaGen Web Application.
Overview
AquaGen implements a Role-Based Access Control (RBAC) system to manage user permissions and feature access across the application.
Key Features:
- 33+ permission types for granular control
- Permission wrapper components for UI elements
- Permission controller for programmatic checks
- Group permissions for batch assignments
- Super user override capabilities
- Service-based permissions
Components:
- Permission Enums: All available permissions
- Permission Controller: Business logic for permission checks
- Permission Wrapper: React component for conditional rendering
- Permission Popup: Browser permission dialogs
Permission Architecture
Core Files
| File | Purpose | Location |
|---|---|---|
| permissions.js | Permission definitions | libs/shared/src/enums/permissions.js |
| PermissionController.js | Permission logic | libs/shared/src/controller/permission/PermissionController.js |
| PermissionWrapper.jsx | React wrapper component | libs/components/src/permissionWrapper/PermissionWrapper.jsx |
| PopupPermission.jsx | Browser popup dialog | libs/components/src/permissionPopup/PopupPermission.jsx |
Data Flow
All Available Permissions
Permission Enums
Location: libs/shared/src/enums/permissions.js:1-33
Core Permissions
| Permission | Purpose | Usage |
|---|---|---|
DEFAULT | Default user access | Assigned if no permissions specified |
SUPER_USER | Admin access | Grants access to all features (except disabled ones) |
Feature Permissions
Dashboard & Monitoring
| Permission | Feature | Description |
|---|---|---|
LANDING_PAGE | Dashboard | Main landing page access |
WATER_MONITORING | Monitoring | Water data monitoring (always accessible) |
DAILY_SUMMARY | Daily Summary | Daily consumption summary |
Water Management
| Permission | Feature | Description |
|---|---|---|
WATER_BALANCE | Water Balance | Water balance graphs and analysis |
WATER_NEUTRALITY | Water Neutrality | Sustainability tracking |
WATER_RATIO | Water Ratio | Water usage ratios |
Water Indexes
| Permission | Feature | Description |
|---|---|---|
RWI | Rain Water Index | Rainwater harvesting index |
RWI_EFFICIENCY_SAVINGS | RWI Savings | RWI efficiency and savings display |
UWI | Urban Water Index | Urban water usage index |
UWI_DASHBOARD | UWI Dashboard | Special UWI dashboard view |
UWI_SHOW_ENERGY_SAVING | UWI Energy | Energy savings in UWI |
WRI | Water Risk Index | Water risk assessment |
WRI_MACRO_LEVEL | WRI Macro | Macro-level WRI view |
WRI_MICRO_LEVEL | WRI Micro | Micro-level WRI view |
GWI | Ground Water Index | Ground water monitoring |
SCADA & HMI
| Permission | Feature | Description |
|---|---|---|
SCADA_EDITOR | SCADA Editor | Create/edit SCADA diagrams |
SCADA_VIEWER | SCADA Viewer | View SCADA diagrams |
WEB_HMI | Web HMI | Human-Machine Interface |
AI & Analytics
| Permission | Feature | Description |
|---|---|---|
AQUAGPT | AquaGPT | AI-powered insights and chat |
NLABS | AquaGen Labs | Experimental features |
Alerts & Notifications
| Permission | Feature | Description |
|---|---|---|
ALERTS | Alerts | Alert viewing and management |
ENERGY_ALERT | Energy Alerts | Energy-specific alerts |
Account Management
| Permission | Feature | Description |
|---|---|---|
ACCOUNT_SETTINGS | Account Settings | User account settings |
MANUAL_NODE_ENTRY | Manual Entry | Manual data entry for nodes |
Energy & Efficiency
| Permission | Feature | Description |
|---|---|---|
EFFICIENCY | Efficiency | Efficiency metrics (always accessible) |
EFFICENCY | Efficiency (typo) | Duplicate for backward compatibility |
DISABLE_ENERGY_TOGGLE_BUTTON | Energy Toggle | Disable energy toggle (negative permission) |
Reports
| Permission | Feature | Description |
|---|---|---|
NOT_GENERAL_REPORT | Report Access | Exclude from general reports |
Group Permissions
| Permission | Includes | Description |
|---|---|---|
GROUP_OPERATIONS | LATEST_DATA | Group of operational permissions |
Permission Controller
Methods
Location: libs/shared/src/controller/permission/PermissionController.js
getAllUserPermissions()
Gets all permissions for a user, including expanded group permissions.
Signature:
getAllUserPermissions(loginData)
Parameters:
loginData(Object): User login data from authentication
Returns:
Array<string>: Array of permission strings
Logic:
- Extract service permissions (from
loginData.services) - Check if permissions array exists
- If no permissions, return
['DEFAULT'] - Process each permission:
- If starts with
GROUP_, expand group permissions - Otherwise, add individual permission
- If starts with
- Return formatted array
Example:
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
const userPermissions = PermissionController.getAllUserPermissions(loginData);
// Returns: ['LANDING_PAGE', 'WATER_BALANCE', 'AQUAGPT', ...]
Code Reference: libs/shared/src/controller/permission/PermissionController.js:3-26
isPermitted()
Checks if a user has a specific permission.
Signature:
isPermitted(tag, loginData, ignoreSuperUser = false)
Parameters:
tag(string): Permission to checkloginData(Object): User login dataignoreSuperUser(boolean): Whether to ignore SUPER_USER privilege (default: false)
Returns:
boolean:trueif permitted,falseotherwise
Special Rules:
-
SUPER_USER Override:
- SUPER_USER has access to ALL features
- Exceptions:
- Permissions starting with
DISABLE_ ACCOUNT_SETTINGSUWI_DASHBOARD
- Permissions starting with
-
Always Accessible:
WATER_MONITORING- Always returns trueEFFICENCY- Always returns true
-
Ignore Super User:
- When
ignoreSuperUser = true, super user privilege is ignored - Useful for specific permission checks
- When
Example:
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
// Check if user can access AquaGPT
const canAccessAquaGPT = PermissionController.isPermitted(
Permissions.allPermission.AQUAGPT,
loginData
);
if (canAccessAquaGPT) {
// Show AquaGPT feature
}
// Check permission without super user override
const hasExactPermission = PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
loginData,
true // Ignore super user
);
Code Reference: libs/shared/src/controller/permission/PermissionController.js:28-48
Permission Wrapper Component
Overview
PermissionWrapper is a React component that conditionally renders children based on user permissions.
Location: libs/components/src/permissionWrapper/PermissionWrapper.jsx
Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | required | Permission to check |
children | ReactNode | required | Content to render if permitted |
negate | boolean | false | Reverse the permission check |
ignoreSuperUser | boolean | false | Ignore super user privilege |
Basic Usage
Show content if user has permission:
import PermissionWrapper from '@aquagen-mf-webapp/components/permissionWrapper/PermissionWrapper';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
function MyComponent() {
return (
<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<AquaGPTButton />
</PermissionWrapper>
);
}
Result: <AquaGPTButton /> only renders if user has AQUAGPT permission.
Negate Mode
Hide content if user has permission:
<PermissionWrapper
tag={Permissions.allPermission.UWI_DASHBOARD}
negate={true}
>
<StandardDashboard />
</PermissionWrapper>
Result: <StandardDashboard /> only renders if user does NOT have UWI_DASHBOARD permission.
Ignore Super User
Check exact permission, ignoring super user:
<PermissionWrapper
tag={Permissions.allPermission.SCADA_EDITOR}
ignoreSuperUser={true}
>
<ScadaEditorButton />
</PermissionWrapper>
Result: <ScadaEditorButton /> only renders if user has exact SCADA_EDITOR permission, even if they're a super user.
Real-World Examples
Example 1: Dashboard View Toggle
File: libs/dashboard/src/DashboardPage.jsx:173-181
<Container sx={{ pt: 2 }}>
{/* Show standard dashboard if user does NOT have UWI_DASHBOARD */}
<PermissionWrapper
tag={Permissions.allPermission.UWI_DASHBOARD}
negate
>
<BuildDashboardPage />
</PermissionWrapper>
{/* Show UWI dashboard if user HAS UWI_DASHBOARD */}
<PermissionWrapper tag={Permissions.allPermission.UWI_DASHBOARD}>
<BuildUWIDashboardPage />
</PermissionWrapper>
</Container>
Logic: Users see either standard OR UWI dashboard, not both.
Example 2: Navigation Menu Items
<PermissionWrapper tag={Permissions.allPermission.SCADA_EDITOR}>
<MenuItem onClick={() => navigate('/scadaEditor')}>
SCADA Editor
</MenuItem>
</PermissionWrapper>
<PermissionWrapper tag={Permissions.allPermission.SCADA_VIEWER}>
<MenuItem onClick={() => navigate('/scadaViewer')}>
SCADA Viewer
</MenuItem>
</PermissionWrapper>
Result: Menu items only appear if user has respective permissions.
Example 3: Feature Buttons
<PermissionWrapper tag={Permissions.allPermission.MANUAL_NODE_ENTRY}>
<AppButton
value="Manual Entry"
onClick={handleManualEntry}
/>
</PermissionWrapper>
<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<AppButton
value="Water Balance"
onClick={handleWaterBalance}
/>
</PermissionWrapper>
Example 4: Disable Features
<PermissionWrapper
tag={Permissions.allPermission.DISABLE_ENERGY_TOGGLE_BUTTON}
negate
>
<ToggleButton
value="energy"
onChange={handleEnergyToggle}
/>
</PermissionWrapper>
Result: Toggle button hidden if user has DISABLE_ENERGY_TOGGLE_BUTTON permission.
Component Code
import { useContext } from 'react';
import If from '@aquagen-mf-webapp/components/logical/If';
import IfNot from '@aquagen-mf-webapp/components/logical/IfNot';
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';
function PermissionWrapper({ tag, children, negate = false, ignoreSuperUser = false }) {
const appStore = useContext(AppStoreContext);
if (negate) {
return (
<IfNot
condition={PermissionController.isPermitted(tag, appStore.loginData, ignoreSuperUser)}
>
{children}
</IfNot>
);
}
return (
<If condition={PermissionController.isPermitted(tag, appStore.loginData, ignoreSuperUser)}>
{children}
</If>
);
}
export default PermissionWrapper;
Code Reference: libs/components/src/permissionWrapper/PermissionWrapper.jsx:1-27
Programmatic Permission Checks
Using PermissionController Directly
For non-JSX scenarios (e.g., inside functions, conditions):
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
import { useContext } from 'react';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';
function MyComponent() {
const appStore = useContext(AppStoreContext);
const handleAction = () => {
// Check permission programmatically
if (PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
appStore.loginData
)) {
// User has permission - proceed
openScadaEditor();
} else {
// User lacks permission - show error
showPermissionError();
}
};
return <button onClick={handleAction}>Edit SCADA</button>;
}
Multiple Permission Checks
const appStore = useContext(AppStoreContext);
// Check multiple permissions
const hasWaterFeatures =
PermissionController.isPermitted(Permissions.allPermission.WATER_BALANCE, appStore.loginData) &&
PermissionController.isPermitted(Permissions.allPermission.WATER_NEUTRALITY, appStore.loginData);
const hasAnyWaterIndex =
PermissionController.isPermitted(Permissions.allPermission.RWI, appStore.loginData) ||
PermissionController.isPermitted(Permissions.allPermission.UWI, appStore.loginData) ||
PermissionController.isPermitted(Permissions.allPermission.WRI, appStore.loginData);
Getting All User Permissions
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';
function DebugPermissions() {
const appStore = useContext(AppStoreContext);
const allPermissions = PermissionController.getAllUserPermissions(appStore.loginData);
return (
<div>
<h3>Your Permissions:</h3>
<ul>
{allPermissions.map(permission => (
<li key={permission}>{permission}</li>
))}
</ul>
</div>
);
}
Login Data Structure
Permission Data in Login Response
When user logs in, permissions are received in the response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"loginData": {
"userId": "12345",
"name": "John Doe",
"email": "user@example.com",
"services": [
{
"categoryId": "WATER_MONITORING",
"serviceName": "Water Monitoring"
},
{
"categoryId": "ENERGY",
"serviceName": "Energy Management"
}
],
"permissions": [
"SUPER_USER",
"LANDING_PAGE",
"WATER_BALANCE",
"AQUAGPT",
"SCADA_VIEWER",
"WRI",
"GROUP_OPERATIONS"
]
}
}
Accessing Permissions in Components
import { useContext } from 'react';
import { AppStoreContext } from '@aquagen-mf-webapp/shared/store/AppStore';
function MyComponent() {
const appStore = useContext(AppStoreContext);
// Access raw permissions
const rawPermissions = appStore.loginData?.permissions || [];
// Access services
const userServices = appStore.loginData?.services || [];
console.log('User permissions:', rawPermissions);
console.log('User services:', userServices);
}
Permission Patterns & Best Practices
1. Component-Level Protection
Wrap entire components:
import PermissionWrapper from '@aquagen-mf-webapp/components/permissionWrapper/PermissionWrapper';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
function App() {
return (
<div>
<PublicComponent />
<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<AquaGPTPage />
</PermissionWrapper>
<PermissionWrapper tag={Permissions.allPermission.SCADA_EDITOR}>
<ScadaEditorPage />
</PermissionWrapper>
</div>
);
}
2. Element-Level Protection
Wrap buttons, links, or UI elements:
<Box>
<Typography>Dashboard Actions</Typography>
<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<Button>View Water Balance</Button>
</PermissionWrapper>
<PermissionWrapper tag={Permissions.allPermission.MANUAL_NODE_ENTRY}>
<Button>Manual Entry</Button>
</PermissionWrapper>
</Box>
3. Conditional Rendering Logic
Combine with other conditions:
<PermissionWrapper tag={Permissions.allPermission.ALERTS}>
{hasActiveAlerts && (
<AlertBanner alerts={activeAlerts} />
)}
</PermissionWrapper>
4. Negative Permissions
Show content when permission is NOT present:
{/* Show basic features if NOT super user */}
<PermissionWrapper tag={Permissions.allPermission.SUPER_USER} negate>
<BasicFeatures />
</PermissionWrapper>
{/* Show admin features if IS super user */}
<PermissionWrapper tag={Permissions.allPermission.SUPER_USER}>
<AdminFeatures />
</PermissionWrapper>
5. Multiple Wrappers
Nest wrappers for AND logic:
<PermissionWrapper tag={Permissions.allPermission.SCADA_EDITOR}>
<PermissionWrapper tag={Permissions.allPermission.SUPER_USER}>
<AdminScadaFeatures />
</PermissionWrapper>
</PermissionWrapper>
Result: Only renders if user has BOTH permissions.
6. Route Protection
Protect entire routes:
import PermissionWrapper from '@aquagen-mf-webapp/components/permissionWrapper/PermissionWrapper';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
const routes = [
{
path: '/dashboard',
element: (
<PermissionWrapper tag={Permissions.allPermission.LANDING_PAGE}>
<DashboardPage />
</PermissionWrapper>
)
},
{
path: '/aquagpt',
element: (
<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<AquaGPTPage />
</PermissionWrapper>
)
}
];
Super User Behavior
What is a Super User?
A user with the SUPER_USER permission has access to ALL features in the application.
Characteristics:
- Bypasses most permission checks
- Gets access to all features automatically
- Cannot be restricted from most features
Super User Exceptions
Super user does NOT automatically grant access to:
1. Disabled Features
Permissions starting with DISABLE_ are respected:
// Super user CANNOT access if DISABLE_ENERGY_TOGGLE_BUTTON is set
<PermissionWrapper tag={Permissions.allPermission.DISABLE_ENERGY_TOGGLE_BUTTON} negate>
<EnergyToggleButton />
</PermissionWrapper>
2. Account Settings
Super user must have explicit ACCOUNT_SETTINGS permission:
// Super user needs explicit permission
<PermissionWrapper tag={Permissions.allPermission.ACCOUNT_SETTINGS}>
<AccountSettingsPage />
</PermissionWrapper>
3. UWI Dashboard
Super user must have explicit UWI_DASHBOARD permission:
// Super user needs explicit permission
<PermissionWrapper tag={Permissions.allPermission.UWI_DASHBOARD}>
<UWIDashboardPage />
</PermissionWrapper>
Ignoring Super User
Use ignoreSuperUser prop to check exact permissions:
<PermissionWrapper
tag={Permissions.allPermission.SCADA_EDITOR}
ignoreSuperUser={true}
>
<ScadaEditorFeature />
</PermissionWrapper>
Result: Even super users must have exact SCADA_EDITOR permission.
Group Permissions
What are Group Permissions?
Group permissions allow assigning multiple permissions at once.
Definition: libs/shared/src/enums/permissions.js:35-37
const groupPermission = {
GROUP_OPERATIONS: [allPermission.LATEST_DATA]
};
How Group Permissions Work
When a user has a group permission:
- Permission starts with
GROUP_ - PermissionController expands it to individual permissions
- All group members are added to user's permissions
Example:
User has:
permissions: ['GROUP_OPERATIONS']
Expands to:
formattedPermissions: ['LATEST_DATA']
Adding Group Permissions
To create new group:
- Edit
libs/shared/src/enums/permissions.js - Add to
groupPermissionobject:
const groupPermission = {
GROUP_OPERATIONS: [allPermission.LATEST_DATA],
GROUP_WATER_FEATURES: [
allPermission.WATER_BALANCE,
allPermission.WATER_NEUTRALITY,
allPermission.WATER_RATIO
],
GROUP_SCADA: [
allPermission.SCADA_EDITOR,
allPermission.SCADA_VIEWER
]
};
Browser Permission Popup
PopupPermission Component
Purpose: Display dialog when browser blocks popups or redirects.
Location: libs/components/src/permissionPopup/PopupPermission.jsx
Usage:
import PopupPermission from '@aquagen-mf-webapp/components/permissionPopup/PopupPermission';
function MyComponent() {
const [popupOpen, setPopupOpen] = useState(false);
const handleOpenNewTab = () => {
const newWindow = window.open('/report', '_blank');
if (!newWindow) {
// Popup blocked - show dialog
setPopupOpen(true);
}
};
return (
<>
<button onClick={handleOpenNewTab}>Open Report</button>
<PopupPermission
open={popupOpen}
handleClose={() => setPopupOpen(false)}
/>
</>
);
}
Dialog Content:
- Title: "Pop-ups and Redirection Blocked"
- Message: "Please update the popup setting to allow new tab to open."
- Action: "Cancel" button
Code Reference: libs/components/src/permissionPopup/PopupPermission.jsx:11-31
Testing Permissions
Test Different Permission Levels
1. Super User:
{
"permissions": ["SUPER_USER"]
}
Result: Access to all features
2. Basic User:
{
"permissions": ["LANDING_PAGE", "WATER_MONITORING"]
}
Result: Limited feature access
3. No Permissions:
{
"permissions": []
}
Result: Only DEFAULT permission
4. Group Permissions:
{
"permissions": ["GROUP_OPERATIONS"]
}
Result: Expands to group members
Debug Permission State
import { PermissionController } from '@aquagen-mf-webapp/shared/controller/permission/PermissionController';
import { Permissions } from '@aquagen-mf-webapp/shared/enums';
function DebugPermissions() {
const appStore = useContext(AppStoreContext);
// Get all permissions
const allPerms = PermissionController.getAllUserPermissions(appStore.loginData);
// Check specific permissions
const checks = {
isSuperUser: allPerms.includes('SUPER_USER'),
hasAquaGPT: PermissionController.isPermitted(
Permissions.allPermission.AQUAGPT,
appStore.loginData
),
hasScadaEditor: PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
appStore.loginData
)
};
console.log('All permissions:', allPerms);
console.log('Permission checks:', checks);
return <div>{JSON.stringify(checks, null, 2)}</div>;
}
Common Patterns
Pattern 1: Feature Toggle
<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<WaterBalanceFeature />
</PermissionWrapper>
Pattern 2: Conditional Navigation
const navigate = useNavigate();
const appStore = useContext(AppStoreContext);
const handleNavigation = (route, permission) => {
if (PermissionController.isPermitted(permission, appStore.loginData)) {
navigate(route);
} else {
showErrorMessage('Access Denied');
}
};
Pattern 3: Dynamic Menu
const menuItems = [
{ label: 'Dashboard', permission: Permissions.allPermission.LANDING_PAGE },
{ label: 'AquaGPT', permission: Permissions.allPermission.AQUAGPT },
{ label: 'SCADA Editor', permission: Permissions.allPermission.SCADA_EDITOR }
];
return (
<Menu>
{menuItems.map(item => (
<PermissionWrapper key={item.label} tag={item.permission}>
<MenuItem>{item.label}</MenuItem>
</PermissionWrapper>
))}
</Menu>
);
Pattern 4: Button Visibility
<Stack direction="row" spacing={2}>
<PermissionWrapper tag={Permissions.allPermission.WATER_BALANCE}>
<Button>Water Balance</Button>
</PermissionWrapper>
<PermissionWrapper tag={Permissions.allPermission.AQUAGPT}>
<Button>AquaGPT</Button>
</PermissionWrapper>
<PermissionWrapper tag={Permissions.allPermission.MANUAL_NODE_ENTRY}>
<Button>Manual Entry</Button>
</PermissionWrapper>
</Stack>
Error Handling
Permission Denied
const appStore = useContext(AppStoreContext);
const handleProtectedAction = () => {
if (!PermissionController.isPermitted(
Permissions.allPermission.SCADA_EDITOR,
appStore.loginData
)) {
// Show error
alert('You do not have permission to access this feature');
return;
}
// Proceed with action
performAction();
};
Missing Login Data
const appStore = useContext(AppStoreContext);
if (!appStore.loginData) {
// User not logged in
navigate('/login');
return null;
}
// Safe to check permissions
const hasPermission = PermissionController.isPermitted(
Permissions.allPermission.AQUAGPT,
appStore.loginData
);
Complete Permission List
All 33 Permissions
DEFAULT // Default user
SUPER_USER // Admin access
GROUP_OPERATIONS // Operations group
LANDING_PAGE // Dashboard
ALERTS // Alerts
WATER_BALANCE // Water balance
DAILY_SUMMARY // Daily summary
WATER_NEUTRALITY // Water neutrality
AQUAGPT // AquaGPT
RWI // Rain Water Index
UWI // Urban Water Index
GWI // Ground Water Index
UWI_DASHBOARD // UWI Dashboard
SCADA_EDITOR // SCADA Editor
SCADA_VIEWER // SCADA Viewer
WRI // Water Risk Index
WRI_MACRO_LEVEL // WRI Macro
WRI_MICRO_LEVEL // WRI Micro
DISABLE_ENERGY_TOGGLE_BUTTON // Disable energy toggle
UWI_SHOW_ENERGY_SAVING // UWI energy savings
RWI_EFFICIENCY_SAVINGS // RWI efficiency
EFFICIENCY // Efficiency
EFFICENCY // Efficiency (typo)
WATER_MONITORING // Water monitoring
MANUAL_NODE_ENTRY // Manual entry
NOT_GENERAL_REPORT // Report exclusion
WATER_RATIO // Water ratio
ENERGY_ALERT // Energy alerts
ACCOUNT_SETTINGS // Account settings
WEB_HMI // Web HMI
NLABS // AquaGen Labs
Next Steps
- Review Authentication for login and user data
- Check Routes for route protection
- See Components for UI component permissions
Last Updated: February 2026 Maintained By: AquaGen Development Team