Status code 5000 is Checkmate's internal sentinel for "no HTTP response
received" (DNS failure, connection refused, timeout, TLS error, etc.).
Showing the raw number to admins is confusing — the value isn't a real
HTTP code and means nothing without context.
This change replaces "5000" with "Down" across all check/incident
surfaces, and adds a hover tooltip that explains the underlying cause
based on the stored error message. Real HTTP codes (2xx/3xx/4xx/5xx)
are unchanged.
## Changes
- Add client/src/Utils/statusCode.ts with formatStatusCode,
getStatusCodeTooltip, getStatusCodeValueType, extractErrorCode, and a
message-to-subtype parser (timeout / dns / refused / tls / reset).
- Wire into 6 render sites: Checks table, Uptime details Checks table,
GeoChecks table + map popup, Incidents table, Incident details dialog.
- Add common.statusCode.* i18n keys in en.json with subtype-specific
descriptions. Tooltip appends the raw error code (ECONNREFUSED,
ETIMEDOUT, CERT_HAS_EXPIRED, etc.) when present in the message.
## Tooltip examples
- "queryA ECONNREFUSED www.example.com" →
"The server refused the connection (port closed or service not listening). (ECONNREFUSED)"
- "connect ETIMEDOUT 1.2.3.4:443" →
"The server didn't respond in time (request timed out). (ETIMEDOUT)"
## Notes
- No DB or server changes; statusCode field stays 5000 in storage.
- Other locales fall back to English keys until translated.
Anonymous visitors to a public status page have no persisted Redux
state, so ThemedHeatmap and ThemedHistogram tooltips always rendered
timestamps in the hardcoded "America/Toronto" default — regardless of
the timezone the operator configured. Even on the same domain,
ThemedHeatmap and ThemedHistogram had no code path to consume the
operator-configured statusPage.timezone returned by the API.
- Add SYSTEM_TIMEZONE module constant and a small resolveTimezone()
helper to TimeUtils.ts. formatDateWithTz's timezone parameter is now
optional and defaults to the system tz, so callers don't need to
rebuild the prop -> browser -> UTC chain inline.
- Add a `timezone` field to StatusPageThemeContextValue so any themed
component on the public page can read the page-wide tz without prop
drilling (parallels how `theme` and `mode` are already exposed).
- Wire statusPage.timezone into StatusPageThemeProvider from
Pages/StatusPage/Status/index.tsx (both the public route and the
admin preview).
- ThemedHeatmap and ThemedHistogram now read tz from the context
instead of useSelector(state.ui.timezone) — that selector was
unreliable for anonymous visitors and removing it cuts a Redux
subscription on the public page.
- Replace the inline `data?.timezone || Intl.DateTimeFormat()...`
expression in useStatusPageForm.ts with resolveTimezone().
Closes#3628.
- Button.tsx: contained variant's hover now uses HOVER.DARKEN=0.06
instead of MUI's default 16%, which was visibly heavy on the green
primary buttons across the app. Also fixes a subtle bug where the
earlier hover sx merge order silently dropped the boxShadow:'none'
reset. Added a HOVER token group to constants.ts so the darken value
has a single source of truth.
- TextInput.tsx: forward the caller's sx to the wrapper Stack when a
fieldLabel is present (previously it was applied to the inner
TextField, so flex:1 etc. didn't propagate to the visible width).
Inner sx now also explicitly width:100% so wrapped inputs don't
collapse to content width when their parent is a flex row.
- FieldLabel.tsx: drop the hardcoded fontSize:'14px' and use
variant=body1 (=13px from theme) to match the rest of the app's
body typography.
The sidebar selected-icon colour was sharing primary.main, which read too dark in dark mode. Introduce palette.sidebar.accent (brand green in light mode, a lighter green in dark mode) and route NavItem through it so primary stays brand-green for buttons and the rest of the UI.
Annotate the hex literals used when callers omit theme colours so the intent (defensive MUI default match) is obvious instead of looking like arbitrary values.
- Introduce a reusable eyebrow Typography variant in the theme (13px, 500 weight, uppercase, 0.08em tracking) for section headings
- Apply it to ConfigBox titles in /settings and to "Job queue" / "Failed jobs" in /logs
- Drop the page header weight from 600 to 400 with tighter tracking so titles read lighter and more refined
Renames the primary palette token from blueBlueWave to brandGreen and points it at #13715B in both light and dark themes, so primary buttons, sidebar selection, slider rings, switch tracks etc. follow the brand instead of the old blue.
- Switch: 36x20 contained pill with 6px right padding so labels keep their distance even inside Stacks
- Radio: theme-level override, 20px ring with a fixed-size inner dot — checked state no longer animates a scale change that pushed labels around
- Slider: white thumb with primary-colour ring, 4px track, soft hover/active glow
- Drop the custom RadioInput icon overrides that bypassed the theme and caused the size jiggle
- MuiOutlinedInput hover override: was inheriting MUI's default
white outline in dark mode (too bright). Now uses an alpha-23%
white in dark and text.primary in light.
- TextInput: -webkit-autofill now repaints the input bg with
background.paper (instead of Chrome's harsh blue/yellow autofill
highlight), text in text.primary, with the standard 5000s-transition
trick to suppress the brief flash. Works in both modes.
## Changes
- Replace MUI default blue-tinted dark background with neutral grays
(#151518 for default, #1c1c21 for paper/cards)
- Remove decorative blue/purple SVG blob overlay from dark mode background
## Benefits
- Dark mode now has a clean, neutral gray appearance instead of a blue tint