feat(status-page): display active maintenance banner and monitor maintenance alert (#2679) - #3833
Conversation
There was a problem hiding this comment.
Pull request overview
Adds visibility of active scheduled maintenance to public status pages and the monitor detail UI, backed by new backend payload fields and maintenance-window time calculations.
Changes:
- Backend: compute and return
activeMaintenancesfor public status-page payloads based on active maintenance windows. - Frontend: render a
MaintenanceBanneron status pages and a maintenance warningAlerton the monitor details page. - Tests/i18n: add unit tests for maintenance-window end calculation and status-page payload, plus new English translation strings.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| server/src/utils/maintenanceWindow.ts | Adds getActiveWindowEnd() helper for active maintenance ETA calculation. |
| server/test/unit/utils/maintenanceWindow.test.ts | Adds unit tests covering getActiveWindowEnd() for one-time and recurring windows. |
| server/src/domain/status-pages/status-page.type.ts | Extends public status-page payload types with activeMaintenances. |
| server/src/domain/status-pages/status-page.service.ts | Queries maintenance windows for status-page monitors and includes activeMaintenances in the public payload. |
| server/test/unit/services/statusPageService.test.ts | Updates service construction and adds coverage for activeMaintenances inclusion/omission. |
| server/src/config/services.api.ts | Wires maintenanceWindowsRepository into StatusPageService. |
| client/src/Types/StatusPage.ts | Mirrors the new activeMaintenances payload type on the client. |
| client/src/Pages/Uptime/Details/index.tsx | Shows a warning banner when the monitor is in maintenance status. |
| client/src/Pages/StatusPage/Status/themes/shared/MaintenanceBanner.tsx | New themed banner that lists affected services and displays maintenance ETA. |
| client/src/Pages/StatusPage/Status/themes/shared/BaseStatusPage.tsx | Injects MaintenanceBanner into the base status-page layout. |
| client/src/Pages/StatusPage/Status/index.tsx | Passes activeMaintenances from API response into the themed status page view. |
| client/src/locales/en.json | Adds translation keys for the banner title/ETA/affected services and the monitor maintenance alert text. |
Suppressed comments (1)
client/src/Pages/StatusPage/Status/themes/shared/MaintenanceBanner.tsx:143
- Using the array index as the React key (
key={idx}) can cause unstable rendering if the list changes. Prefer a stable key derived from the data (e.g., maintenance-window id + service name) for the affected-services chips.
{affectedNames.map((name, idx) => (
<Box
key={idx}
sx={{
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const activeList = windows | ||
| .filter((win) => isWindowActive(win, now)) | ||
| .map((win) => { | ||
| const activeEnd = getActiveWindowEnd(win, now) ?? new Date(win.end); | ||
| const affectedMonitorIds = win.monitorIds.filter((id) => statusPage.monitors.includes(id)); | ||
| return { | ||
| id: win.id, | ||
| name: win.name, | ||
| start: win.start, | ||
| end: activeEnd.toISOString(), | ||
| monitorIds: affectedMonitorIds, | ||
| }; | ||
| }) | ||
| .filter((m) => m.monitorIds.length > 0); |
| {monitor.status === "maintenance" && ( | ||
| <Alert | ||
| severity="warning" | ||
| icon={<Wrench size={20} />} | ||
| sx={{ | ||
| borderRadius: "8px", | ||
| fontWeight: 500, | ||
| }} | ||
| > | ||
| {t("pages.uptime.details.maintenanceAlert")} | ||
| </Alert> | ||
| )} |
| import { formatDateWithTz } from "@/Utils/TimeUtils"; | ||
|
|
||
| interface Props { | ||
| activeMaintenances?: ActiveMaintenanceInfo[]; | ||
| monitors: Monitor[]; | ||
| timezone?: string; | ||
| } | ||
|
|
||
| export const MaintenanceBanner = ({ activeMaintenances, monitors, timezone }: Props) => { | ||
| const { t } = useTranslation(); | ||
| const { tokens, timezone: themeTimezone } = useStatusPageTheme(); | ||
| const effectiveTimezone = timezone || themeTimezone; | ||
|
|
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
ajhollid
left a comment
There was a problem hiding this comment.
The backend changes are solid, however there has been some duplication introduced around calculating maintenance winodws that I think can be cleany resolved.
The front end needs additional work as there are many hardcoded arbitrary values that should be replaced with references to the theme and utility files. Please see my comments in the code review for details. Thank you!
| private settingsService: ISettingsService, | ||
| private monitorsRepository: IMonitorsRepository | ||
| private monitorsRepository: IMonitorsRepository, | ||
| private maintenanceWindowsRepository?: IMaintenanceWindowsRepository |
There was a problem hiding this comment.
This should be required rather than optional
| return false; | ||
| }; | ||
|
|
||
| export const getActiveWindowEnd = (window: MaintenanceWindow, now: Date = new Date()): Date | null => { |
There was a problem hiding this comment.
This is really the same logic as isWindowActive. We don't want to duplicate this math as we introduce the possibility of drift, so why don't we just reimplement isActiveWindow as getActiveWindowEnd(window, now) !== null?
I think that resolves the duplication issue here.
| severity="warning" | ||
| icon={<Wrench size={20} />} | ||
| sx={{ | ||
| borderRadius: "8px", |
There was a problem hiding this comment.
This should be theme.shape.borderRadius
| timezone?: string; | ||
| } | ||
|
|
||
| export const MaintenanceBanner = ({ activeMaintenances, monitors, timezone }: Props) => { |
There was a problem hiding this comment.
This component is full of hardcoded arbitrary values for dimensions, colors etc.
The theme should be used as much as possible for colors and dimensions, as well as the SPACING and LAYOUT util objects. Please see other components for implementation reference
Summary
This PR implements maintenance mode visibility on public status pages and monitor detail pages as requested in #2679.
MaintenanceBannerwhen any monitors attached to the status page are undergoing active scheduled maintenance, detailing affected services and estimated completion time (ETA).getActiveWindowEnd()utility to calculate end times for one-time and recurring maintenance windows.StatusPageService.getPublicStatusPagePayloadto querymaintenanceWindowsRepositoryfor active windows covering the status page monitors.IMaintenanceWindowsRepositoryinservices.api.ts.en.json.Fixes #2679
PR Checklist
Fixes #2679).t(...)translations with keys inen.json.npm run lintand all unit tests (67 test suites, 1262 tests passing).developbranch.