AquaX Frontend Architecture

Owner: Frontend & Architecture
Reviewers: Backend, Product Design, QA, Security, DevOps
Status: Draft - Current-Code Baseline
Version: 0.1
Last Updated: 2026-09-17
Review Cycle: Per frontend routing, state, API, layout or deployment change

1. Purpose And Scope

This document describes the current AquaX web frontend architecture as implemented in web/. It focuses on runtime composition, routing, layout, state management, API integration, authentication, page boundaries, design-system usage, build behavior and known frontend risks.

This document covers the web admin portal only. Mobile architecture is documented separately in docs/current/architecture/mobile-architecture.md.

Sources reviewed:

  • web/package.json
  • web/vite.config.ts
  • web/tsconfig*.json
  • web/src/main.tsx
  • web/src/router.tsx
  • web/src/core/routes/**
  • web/src/core/config/**
  • web/src/core/redux/**
  • web/src/core/hooks/**
  • web/src/core/services/**
  • web/src/core/components/AppShell/**
  • web/src/core/constants/**
  • web/src/pages/**
  • docs/current/architecture/web-architecture.md
  • docs/07_Design System.md

2. High-Level Frontend Context

The AquaX web app is a React dashboard for admin and owner workflows. It consumes the backend REST API through Axios service wrappers and TanStack Query hooks.

Current stack:

Area Current implementation
Framework React 19
Build tool Vite 8
Language TypeScript
UI library Ant Design 6
Styling Tailwind CSS 4, CSS variables, Sass/global CSS
Routing React Router DOM 7
Server state TanStack Query 5
Client state Redux Toolkit
HTTP client Axios
Charts Recharts
Icons lucide-react and Ant Design icons
Validation Yup in selected feature forms

Primary runtime flow:

Route
  -> page/view component
  -> domain hook
  -> domain API service
  -> httpClient
  -> backend /api

3. Source Layout

Current source layout:

web/src/
  assets/
    brand/
    images/
    styles/
  core/
    components/
    config/
    constants/
    hooks/
    interfaces/
    layouts/
    libs/
    locales/
    redux/
    routes/
    services/
  pages/
  main.scss
  main.tsx
  router.tsx

Boundaries:

Folder Responsibility
assets/ Brand assets, images and global style files
core/components/ Shared reusable UI components such as AppShell, AquaTable, StatsGrid
core/config/ Runtime env, app config and Axios client
core/constants/ Route paths, API paths, storage keys, query keys and UI constants
core/hooks/ Domain hooks and cross-cutting hooks
core/interfaces/ TypeScript request/response/domain interfaces
core/layouts/ Main layout composition
core/libs/ Utility/helper functions
core/redux/ Redux store and slices
core/routes/ Public/private route registration and route guards
core/services/ Domain API service wrappers
pages/ Feature pages, feature components, schemas and views

4. Application Bootstrap

web/src/main.tsx builds the provider tree.

Provider order:

StrictMode
  -> ReduxProvider
    -> QueryClientProvider
      -> AntD ConfigProvider
        -> AntD App
          -> RouterProvider

Confirmed configuration:

Concern Current code
Query defaults From appConfig.query
Query retry 1
Query stale time 60_000ms
Refetch on window focus true
AntD locale vi_VN
AntD primary color #006b5f
AntD border radius 8
Font family Inter/system stack
Router createBrowserRouter

5. Build And Runtime Configuration

Vite configuration:

Concern Current code
React plugin @vitejs/plugin-react
Tailwind plugin @tailwindcss/vite
Path alias @app/* -> src/*
Module resolution TypeScript bundler mode
JSX react-jsx

Environment config:

Variable Purpose Default
VITE_APP_NAME App display/config name AquaX Web
VITE_API_BASE_URL Axios base URL /

Production note:

  • VITE_API_BASE_URL should include the backend global prefix, for example https://api.aquax.vn/api.
  • Current web/Dockerfile runs npm run dev; production static serving is not finalized in code.

6. Routing Architecture

Root router:

/
  -> RouteTitleOutlet
    -> publicRoutes
    -> privateRoutes

RouteTitleOutlet updates document.title based on route path.

6.1 Public Routes

Path Page Guard
/login LoginPage PublicOnlyRoute
/forgot-password ForgotPasswordPage PublicOnlyRoute
/reset-password ResetPasswordPage PublicOnlyRoute
/unauthorized Unauthorized RequireAuth

PublicOnlyRoute redirects authenticated users:

Role Redirect
ADMIN /
OWNER /dashboard
other authenticated role /unauthorized

6.2 Private Route Shell

Top-level private shell:

RequireAuth allowedRoles=[ADMIN, OWNER]
  -> MainLayout
    -> AppShell
    -> nested private routes

Role-specific private routes:

Role Current route area
OWNER /dashboard owner/viewer dashboard
ADMIN users, farms, ponds, IoT devices, reports, tickets, activity logs, notifications, settings

Index route:

  • RoleHomeRedirect sends OWNER to /dashboard.
  • Other authenticated users in this shell are sent to /users.

6.3 Confirmed Admin Route Groups

Area Route examples
Users /users, /users/create, /users/:id, /users/:id/edit, /users/assign-technician
Farms /farms, /farms/create, /farms/:id, /farms/:id/edit
Ponds /ponds, /ponds/create, /ponds/:id, /ponds/:id/edit
IoT devices /iot-devices
Tickets /tickets, /tickets/create, /tickets/:id, /tickets/:id/assign, /tickets/:id/close
Reports /reports, /reports/export, /reports/water-quality, /reports/iot-devices, /reports/feeding, /reports/environment, /reports/incidents
Activity logs /activity-logs
Notifications /notifications
Settings /settings, /settings/incident-response

6.4 Placeholder Routes

Some private routes are registered but currently render Developing:

Route Current status
/water-monitoring Placeholder
/feeding Placeholder
/farming-logs Placeholder
/handbook Placeholder
/alerts Placeholder

7. Layout Architecture

MainLayout wraps authenticated pages and provides:

  • AppShell;
  • Outlet for nested pages;
  • session timeout warning modal.

AppShell provides:

  • responsive sidebar;
  • mobile drawer menu;
  • role-filtered navigation;
  • notification bell and popover;
  • user account menu;
  • sidebar collapse persistence;
  • workspace mode for activity logs.

Navigation source:

core/constants/app-shell.ts

APP_SHELL_NAV_ITEMS filters by role. Current visible navigation:

Role Navigation
OWNER Dashboard
ADMIN Users, farms, ponds, IoT devices, reports, tickets, activity logs, notifications, settings

8. State Management Architecture

8.1 Redux

Redux is used for small, client-owned global state.

Current slices:

Slice State
auth accessToken, expiresAt, user
app locale

Auth initial state reads:

  • aquax.auth.token;
  • aquax.auth.user.

Redux rule:

  • Keep authentication and small app preferences in Redux.
  • Do not store large server datasets in Redux.

8.2 TanStack Query

TanStack Query is the main server-state layer.

Current query defaults:

  • retry: 1;
  • staleTime: 60_000;
  • refetchOnWindowFocus: true.

Domain hooks use useQuery, useMutation and query invalidation. Examples:

  • useUser;
  • useFarm;
  • usePond;
  • useCrop;
  • useTicket;
  • useReport;
  • useIotDevice;
  • useNotification;
  • useDashboard;
  • useActivityLog.

Query rule:

  • Pages should call domain hooks.
  • Hooks should call domain services.
  • Services should call httpClient.

9. API Integration Architecture

9.1 Axios Client

core/config/axios.config.ts creates httpClient.

Current config:

Concern Current code
Base URL env.apiBaseUrl
Timeout 15_000ms
Content type application/json
Credentials withCredentials: true
Request auth Adds bearer token from STORAGE_KEYS.AUTH_TOKEN
Error normalization Extracts backend response.data.message

9.2 Refresh Queue

When a request returns 401:

  1. Login and refresh endpoints are excluded from auto-refresh.
  2. If refresh is already running, the failed request is queued.
  3. The client calls /auth/refresh with httpOnly cookie credentials.
  4. On success, the new access token and user are stored locally.
  5. Queued requests replay with the new token.
  6. On refresh failure, local auth is cleared and the browser redirects to /login.

Architecture risk:

  • expiresAt is stored in Redux on login but not persisted in storage.
  • Refresh relies on backend cookie behavior and correct withCredentials/CORS settings.

9.3 Endpoint Constants And Services

Endpoint constants:

core/constants/api-url.ts

Service wrappers:

core/services/*API.ts

Current service domains:

Service Area
authAPI Login, refresh, logout, password reset
userAPI User management
farmAPI Farm management
pondAPI Pond management and tabs
cropAPI Crop lifecycle
ticketAPI Tickets
reportAPI Reports and export
locationAPI Provinces and wards
deviceAPI Device and auto-rule APIs
iotDeviceAPI IoT registration/control APIs
notificationAPI Notifications and device tokens
settingsAPI Incident response settings
activityLogAPI Activity logs and export
dashboardAPI Owner dashboard
parameterThresholdAPI Threshold configuration

Endpoint constants intentionally omit /api; VITE_API_BASE_URL is responsible for the backend prefix.

10. Authentication UX And Session Lifecycle

useAuth owns login/logout/reset-password UX.

Login success:

  1. Persist auth session in storage.
  2. Dispatch setAuthSession.
  3. Show AntD notification.
  4. Redirect by role:
    • ADMIN -> /
    • OWNER -> /dashboard
    • other -> /unauthorized

Logout:

  1. Calls backend logout when possible.
  2. Clears local auth storage.
  3. Dispatches clearAuthSession.
  4. Redirects to /login.

useSessionTimeout:

  • tracks activity events;
  • shows warning modal before idle expiry;
  • calls logout when idle timeout expires;
  • exposes keepAlive.

11. Notification Architecture

Notifications combine polling and server-sent events.

Confirmed behavior:

  • Notification list and stats use TanStack Query.
  • List and stats refetch every 30_000ms.
  • useNotificationRealtime opens an SSE stream while authenticated.
  • SSE request sends:
    • Accept: text/event-stream;
    • Authorization: Bearer <accessToken>;
    • x-client-type: web.
  • It listens for event type notification.created.
  • On relevant event, notification queries are invalidated.
  • Reconnect delay is 5_000ms.
  • Stream stops when document visibility is hidden and reconnects when visible.

UI surfaces:

  • App shell notification bell;
  • notification popover;
  • notifications page.

12. Page Architecture And Feature Status

Current implemented or partially implemented areas:

Area Status
Auth Login, forgot password, reset password, public/private guards
Owner dashboard Owner dashboard route and page
Users List, create, detail, edit, status, delete, audit log, farm scope, assign technician
Farms List, create, edit, detail, tabs and location data
Ponds List, create, edit, detail, crop drawer and pond tabs
IoT devices Registered page with device management UI
Tickets List, create, detail, assign, close and delete flows
Reports Landing, filters, export and report type pages
Activity logs Workspace-style log view
Notifications List/read state/device notification integration
Settings Settings landing and incident response settings

Placeholder or incomplete route areas:

Area Current status
Water monitoring Placeholder route
Feeding Placeholder route
Farming logs Placeholder route
Handbook Placeholder route
Alerts Placeholder route

13. Styling And Design System Integration

Current frontend styling layers:

Layer Files
CSS variables assets/styles/variables.css
Tailwind import and view transitions assets/styles/tailwind.css
Global base styles and animations main.scss
AntD theme tokens main.tsx ConfigProvider
Local feature styles Page/component class names and inline AntD style overrides

Primary design tokens:

Token Current value
Primary #006b5f
Primary container #008378
Secondary #006a63
Surface #f5faf8
Background #fff
Danger #b00000
Border subtle #dde7e3
Base radius 8px
Base font Inter/system sans

Architecture rule:

  • Shared design decisions should land in assets/styles/variables.css, AppShell, shared components or documented constants before duplicating page-local styling.
  • Feature pages may keep local composition styles, but repeated table/filter/card patterns should move into shared components.

14. Type And Data Boundary

Types live under:

core/interfaces/

Current interface files are domain-oriented:

  • auth;
  • user;
  • farm;
  • pond;
  • crop;
  • ticket;
  • report;
  • notification;
  • dashboard;
  • activity log;
  • IoT device;
  • parameter threshold;
  • settings;
  • common types.

Rules:

  • Services should type API input/output at the boundary.
  • Hooks should expose UI-friendly names and mutation/query state.
  • Pages should avoid duplicating backend response parsing logic.

15. Build, CI And Deployment Considerations

Package scripts:

Script Purpose
npm run dev Start Vite dev server with host binding
npm run build Run tsc -b and vite build
npm run lint Run ESLint
npm run preview Preview built output
npm run format Format source
npm run format:check Check formatting

CI:

  • Current CI runs web install, lint and build.

Deployment gap:

  • web/Dockerfile currently runs the Vite dev server.
  • A production web deployment target is not finalized in repo.
  • Recommended production options remain: static hosting/CDN, NGINX static container, or managed frontend hosting.

16. Performance And UX Considerations

Current confirmed optimizations:

  • Query stale time prevents immediate repeated refetch.
  • Notification stream pauses when the browser tab is hidden.
  • Notification list uses content-visibility for list items.
  • Sidebar collapsed state persists in localStorage.
  • Motion respects prefers-reduced-motion.

Current gaps:

  • No route-level lazy loading is confirmed; pages are imported directly in private route config.
  • Bundle analysis is not configured.
  • Large feature pages may grow without code splitting.
  • No frontend performance budget is documented.

17. Accessibility And Internationalization

Confirmed baseline:

  • AntD locale is Vietnamese.
  • App text is primarily Vietnamese.
  • AppShell interactive controls include aria-label in several key places.
  • Keyboard focus classes exist on AppShell controls.
  • Reduced motion media query is present.

Gaps:

  • No formal accessibility test suite is configured.
  • No full i18n framework usage is confirmed despite core/locales existing.
  • Some page-level custom controls should be reviewed for keyboard/focus behavior.

18. Architecture Risks And Action Items

ID Risk Impact Required follow-up
FE-ARCH-01 Production hosting is not finalized; Dockerfile runs dev server Web deployment may be unsuitable for production Create production static build/serve plan
FE-ARCH-02 Placeholder routes exist for several product areas Users may see unfinished screens Keep backlog status visible and replace Developing with real pages when implemented
FE-ARCH-03 No route-level code splitting Bundle may grow as pages expand Add lazy route loading when build size requires it
FE-ARCH-04 API constants can drift from backend controllers Runtime 404/contract bugs Keep 08_API Specification.md, constants and services updated together
FE-ARCH-05 Auth role model currently exposes web routes mainly to ADMIN/OWNER Other roles may be unsupported on web Align with permission matrix before exposing MANAGER/TECHNICIAN/VIEWER web routes
FE-ARCH-06 Styling is split across tokens, Tailwind, AntD overrides and local page styles Inconsistent UI and maintenance cost Promote repeated patterns into shared components/tokens
FE-ARCH-07 Frontend metrics/error tracking not confirmed Production issues may be harder to diagnose Add logging/error monitoring provider when deployment hardens

19. Change Rules

When changing frontend architecture:

  1. Add route paths to core/constants/routes.ts.
  2. Register route in core/routes/public.tsx or private.tsx.
  3. Add sidebar item in core/constants/app-shell.ts only when it should be visible in navigation.
  4. Add/update API endpoint constants in core/constants/api-url.ts.
  5. Add/update service wrapper in core/services.
  6. Add/update domain hook in core/hooks.
  7. Keep server state in TanStack Query and small app/auth state in Redux.
  8. Update docs/08_API Specification.md and this file when contracts or architecture change.
  9. Run npm run lint and npm run build for frontend code changes.

20. Traceability

Source Architecture evidence
web/package.json Stack, dependencies and scripts
web/vite.config.ts Vite plugins and @app alias
web/tsconfig.app.json TypeScript compilation and alias configuration
web/src/main.tsx Provider tree, QueryClient, AntD theme
web/src/router.tsx Browser router root
web/src/core/routes/** Public/private routes, guards and role redirects
web/src/core/config/axios.config.ts HTTP client, token injection and refresh queue
web/src/core/config/env.ts Vite env boundary
web/src/core/redux/** Auth/app client state
web/src/core/hooks/** Domain server-state hooks
web/src/core/services/** Domain API service wrappers
web/src/core/components/AppShell/** Authenticated layout, navigation and notification UI
web/src/core/constants/** Route, API, query, nav and storage constants
web/src/pages/** Feature page implementation status
docs/current/architecture/web-architecture.md Existing web architecture snapshot
docs/07_Design System.md Current UI design baseline