Skip to main content

Maintenance Log Frontend - Fourteenth Week: Manager/Admin Flows, Create Pages, and Session Expiry Handling

Jesper Andersen
Author
Jesper Andersen
Maintenance Log - This article is part of a series.
Part : This Article

Devlog Week 14: Manager/Admin Flows, Create Pages, and Session Expiry Handling
#

This entry covers the manager/admin-facing workflows (create pages and domain admin actions), and ends with a centralized error-handling + session expiry (token expiration) flow to keep UX consistent across the app.

What Changed
#

  • AssetAdminActions extracted as a domain-specific admin component (separate from employee AdminActions)
  • Inline confirmation pattern added for destructive actions (“Are you sure?” → Yes/Cancel)
  • CreateAsset page added and protected behind MANAGER+
  • CreateEmployee page added and protected behind MANAGER+ with role options derived from the current user’s role
  • Drawer menu updated to show manager routes conditionally and replace placeholder routes
  • Centralized HTTP error handling added to apiRequest() with status-based messaging
  • Session expiry redirect flow implemented using sessionStorage
  • Network error handling improved (fetch TypeError mapped to a user-friendly message)

AssetAdminActions: Intentional Separation + Inline Confirm UX
#

There are now two admin-action components:

  • AdminActions (employee/admin operations)
  • AssetAdminActions (asset/admin operations)

Even though there is some similarity, this is intentional separation by domain, not accidental duplication. Keeping them separate avoids coupling unrelated concerns and makes future asset-specific actions easier to extend without affecting employee logic.

The component uses an inline confirmation pattern:

  • first click enters a confirming state
  • UI swaps to “Are you sure?” with Yes/Cancel

In AssetDetail, the component is rendered with key={asset?.id} so it remounts when the asset changes. That resets the confirmation state automatically.

Updates are applied optimistically through an onAssetUpdated callback so the detail page reflects changes immediately.

CreateAsset: MANAGER+ Route + Practical Defaults
#

CreateAsset is a separate page protected behind MANAGER+.

  • fields: name, description, active
  • active defaults to true as a sensible UX default
  • on success, it navigates to the new asset detail page if the API returns an id (fallback to asset list otherwise)

CreateEmployee: Role-Restricted Options (Defence in Depth)
#

CreateEmployee is also MANAGER+ and posts to the backend POST /auth/register endpoint.

Role options are computed with useMemo based on the authenticated user’s role:

  • managers can only create AUTHENTICATED and TECHNICIAN
  • admins can create all roles

This is frontend enforcement of a backend gap (defence in depth): managers should not be able to create admins, even if the backend does not validate that properly yet.


DrawerMenu Updates: Real Routes + Role-Conditional Links#

Manager+ routes are now conditionally rendered via a canViewManagerRoutes flag.

  • links added for Create Asset and Create Employee
  • placeholder links replaced with real routes

This keeps navigation aligned with permissions and avoids dead-end pages.


Centralized Error Handling (Last): Consistent Messages + Session Expiry
#

To make failures predictable, error handling was centralized in the shared API layer.

HTTP Error Mapping in apiRequest
#

A readBackendErrorMessage helper reads JSON error payloads and extracts a message field when available.

apiRequest() then maps statuses into consistent UI-facing errors:

  • 401 → redirect to login with a “session expired” message
  • 403 → permission denied
  • 404 → resource not found
  • 409 / 400 → show backend message (validation/conflict)
  • 503 → service unavailable
  • >= 500 → generic server error
  • fallback → backend message or a generic error including the status code

Session Expiry / Token Expiration Flow
#

To handle expired tokens cleanly (and avoid half-broken state):

  • a redirectToLoginWithSessionExpiredMessage() helper performs a full page reload via window.location.assign("/login")
  • sessionStorage is used as a simple message between the redirect and the login page
  • the redirect helper guards against loops when already on /login
  • shared constants (SESSION_EXPIRED_MESSAGE_KEY, SESSION_EXPIRED_MESSAGE) live in a dedicated sessionMessages.js module
  • Login.jsx reads the flash message on mount and clears it immediately after displaying

Network Error Handling
#

A TypeError thrown from fetch is treated as a network/CORS/unreachable-backend scenario, and is converted into a user-friendly “Unable to reach server” message.


Up Next
#

  • Small refactors to reduce repetition (where it stays intentional by domain)
  • Identify any backend validation gaps that should be enforced server-side
  • Add quick UX polish passes (empty states and copy consistency)
Maintenance Log - This article is part of a series.
Part : This Article