Skip to main content

Notification Inbox

Summary

Two surfaces show a user their notifications: an always-present header bell with a drawer, and a full inbox screen with filters and per-item actions. Both read the legacy monolith paths.

Audience

Engineers, QA, support, employees, administrators and architects.

Reference Content

The verified reference material for this topic is set out in the sections below.

Header bell and drawer

Rendered by the portal shell on every page. The bell carries an unread badge that displays the count, capping the display at 99 with a plus indicator above that.

Data loading: on mount the shell issues two parallel calls — the full notification list and the unread count — each with an independent failure fallback so one failing does not blank the other. The unread count falls back to counting unread items in the list when the count call returns nothing usable.

Polling: the shell re-runs both calls on a fixed 60-second interval, cleared on unmount. This is the only automatic refresh anywhere in the notification UI; there is no push, no server-sent events and no websocket.

Drawer contents: a header showing the unread count, a mark-all-read action disabled when the count is zero, the list, and a view-all button that closes the drawer and navigates to the inbox route.

Inbox screen

Load: a single call for the full list on mount. A failure sets an error message and clears the list rather than leaving stale data.

Refresh: an explicit refresh button, disabled while loading or refreshing, plus a pull-to-refresh gesture. The gesture is implemented with touch handlers that only arm when the page is scrolled within four pixels of the top and only fire when the pull exceeds 72 pixels — so it cannot trigger accidentally mid-scroll.

Unread badge: computed from the loaded items rather than from a separate count call, and capped at 99 for display.

Filters

Seven filter chips render in a horizontally scrollable strip: all, unread, attendance, leave, payroll, expense and exit. The unread chip additionally shows a count when non-zero.

Filtering is entirely client-side over already-loaded items. No filter value is sent to the server, so filters operate only over the page the server returned.

Categorisation

Category is inferred from text, not from structured data. The screen concatenates the event key, title and message, lowercases the result, and matches substrings in a fixed order: attendance, leave, expense, payroll, exit, then interview, falling through to a general category.

Each category supplies a label, an icon and a colour tone. Two categories refine their label further by looking for a rejection substring, and payroll refines to a payslip label when it finds a slip substring.

Two consequences follow. The interview category is reachable by inference but has no filter chip, so those items are visible only under all or unread. And because matching is ordered substring matching over free text, a message mentioning more than one domain is categorised by whichever term appears earliest in the fixed order rather than by its actual subject.

Actions

ActionBehavior
Open an itemMarks read, then navigates if an action URL is present
Mark as readPer-item button, rendered only for unread items
Mark all readDisabled when nothing is unread
RefreshButton and pull gesture

Optimistic updates. Every write call is followed by local state mutation and every call swallows its error. A failed mark-read still renders as read until the next load — the user is not told the write failed.

Item identity. Because the legacy backend merges two tables and tags each row with a source discriminator, the screen keys its list by source and identifier combined, and matches on both when updating state. The source is also forwarded as a query parameter on the mark-read call so the server updates the correct table.

Action URL handling: a URL beginning with the API prefix has that prefix stripped; a URL beginning with a protocol triggers a full page navigation; anything else is routed client-side with a leading slash ensured.

Empty and error states

Three distinct states render: a loading message, an error panel when the load failed, and an empty state with an icon that varies its wording depending on whether the unread filter is active.

Self-service variant

An employee-only user receives a generic list component instead, configured to show title, message, read flag and creation date, reading a gateway aggregation route. It has no filters, no categorisation, no mark-read action and no pull-to-refresh.

Classification

Implemented.

Requires confirmation

Whether text-based categorisation is intended to be replaced by a structured category field requires confirmation.

See Also

Keywords

  • Notification database
  • Notification Inbox
  • Draft database documentation

Source References

  • UI/salary-ui/apps/client-hrms-portal/src/pages/NotificationsPage.tsx
  • UI/salary-ui/apps/client-hrms-portal/src/components/AppLayout.tsx
  • UI/salary-ui/apps/client-hrms-portal/src/ess/EssPages.tsx
  • Controllers/NotificationsController.cs

Revision Information

  • Status: Draft
  • Last reviewed: 2026-07-21
  • Review cycle: Quarterly