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
| Action | Behavior |
|---|---|
| Open an item | Marks read, then navigates if an action URL is present |
| Mark as read | Per-item button, rendered only for unread items |
| Mark all read | Disabled when nothing is unread |
| Refresh | Button 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.
Related Articles
See Also
Keywords
- Notification database
- Notification Inbox
- Draft database documentation
Source References
UI/salary-ui/apps/client-hrms-portal/src/pages/NotificationsPage.tsxUI/salary-ui/apps/client-hrms-portal/src/components/AppLayout.tsxUI/salary-ui/apps/client-hrms-portal/src/ess/EssPages.tsxControllers/NotificationsController.cs
Revision Information
- Status: Draft
- Last reviewed: 2026-07-21
- Review cycle: Quarterly