The sidebar listed twelve items in a flat column ordered roughly by when each
feature was added, so nothing signalled that Uptime, Pagespeed and
Infrastructure are the same kind of thing, or that Tags and Proxies are
configuration rather than views. Adding a new monitor type made this worse:
the natural place for a new entry is the end of the list, three rows below the
item it most resembles.
Groups the menu into Monitoring, Alerting, Reporting and Configuration.
`getMenu` now returns groups of items rather than a flat array; the sidebar
renders an eyebrow label per group.
When collapsed to 64px there is no room for a label, so the labels are
replaced by dividers between groups — the grouping survives as rhythm.
Settings moves from the middle of the list to the end of Configuration.
Adds four `components.sidebar.menu.groups.*` keys across all 19 locales.
Also corrects the i18n note in CLAUDE.md, which claimed translations are
managed through PoEditor.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
Clickable surfaces across the app set `cursor: pointer` but gave no visual
response on hover, so rows and cards read as static text.
Two separate causes:
- `DataTable` never passed MUI's opt-in `hover` prop on `TableRow`, which is
`false` by default, so no table in the app shaded on hover. One change here
covers all 16 page tables (Uptime, Incidents, Tags, Team, Notifications,
Maintenance, Proxies, Status pages, Checks, Geo checks, Infrastructure,
PageSpeed and the three Logs tables).
- `IconButton` gates its hover style behind `props => !props.disableRipple`,
and the theme sets `disableRipple: true` globally, so the row action gear
and the pagination arrows lost their background. `MenuItem` is unaffected —
its hover sits on the base style — so action menu items already worked and
are left alone.
Adds a `HOVER.ROW` (0.025) constant and an `action.rowHover` palette token,
defined in both light and dark palettes so the overlay direction is correct on
each surface. Kept separate from MUI's shared `action.hover` so menu items,
which read that token, are not restyled as a side effect.
Also fixes `cursor` on expandable rows, which showed `default` despite being
clickable to expand.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
Two small pre-existing bugs in the infrastructure gauges.
Arc overdraw. Gauge computed strokeLength from the raw `progress` prop
while the label and the fill colour both used `progressWithinRange`,
the value clamped to 0-100. A reading above 100% therefore swept the
arc past a full circle - the offset (circumference - strokeLength)
went negative - while the label still read "100.0%". A negative
reading drew the arc backwards for the same reason. strokeLength now
uses the clamped value, so out-of-range input stops at exactly one
turn and matches what the label and colour already showed. Values in
range are unaffected.
The effect that animates the arc also listed `progress` in its
dependency array while only reading `strokeLength`, so it re-ran the
100ms animation on changes that could not alter the arc (105% to 110%,
say). Now that strokeLength is derived from the clamped value, the
stale dependency is dropped.
Disk key fallback. `disk?.device || 0 + idx` parses as
`disk?.device || (0 + idx)` because + binds tighter than ||, so the
fallback was just `idx` and the leading 0 did nothing. It happened to
produce unique keys, but by accident rather than intent, and a disk
whose device is the empty string collided with index 0. Replaced with
a `disk-${idx}` template so the intent is explicit.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
Adds a GET /monitors/domain/:monitorId endpoint that looks up the
registrable domain's WHOIS expiry date (walking up from subdomains),
and displays it next to the certificate expiry on the uptime details
page.
Fixes#2094
- Adjust `alignItems` to `stretch` and add `maxWidth` (260) to Gauge components in Infrastructure Details page.
- Update German translations for memory titles, temperatures, and labels in locales file.
`RadialAvgResponse` maps the response-time palette to a qualitative word:
const palette = getResponseTimeColor(avg);
const msg: Record<string, string> = {
success: "Excellent",
warning: "Average",
danger: "Poor",
};
...
{msg[palette]}
But `getResponseTimeColor` only ever returns `"success" | "warning" | "error"`
(MonitorUtils.ts) — never `"danger"`. So for the slow case `palette` is
`"error"`, `msg["error"]` is `undefined`, and no label renders. The same
component uses `theme.palette[palette].main`, which works precisely because
`palette` is the real MUI key `"error"`; every other palette helper in
MonitorUtils.ts also uses `"error"`, and `"danger"` appears nowhere else in
the client. Any monitor whose average response time is >= 300 ms shows an
empty label exactly where the "Poor" signal matters most. Use the `error`
key.
## Changes
- Add `client/src/locales/it.json` — Italian translation of all 874 UI
keys, structure mirrors `en.json` exactly. Placeholders (`{{value}}`,
etc.) and inline tag tokens (`<settingsLink>`, `<highlight>`, ...)
are preserved. Product/brand names, technical abbreviations, and
placeholder-style examples (e.g. `you@example.com`) are kept in
English. 116 leaves are intentionally kept verbatim in English
(brand names, protocol names, sample values, port numbers, regex).
- Register `it: "Italiano"` in `LanguageSelector`'s `languageNames`
map so the dropdown shows the localised name; the selector already
picks up any locale present in the i18n resources.
- Fill in the two "How do I..." sections that CONTRIBUTING.md's table
of contents already links to but did not have bodies:
- "Improve the documentation?" — short guidance on doc PRs and
keeping README translations in sync.
- "Help with translations?" — describes the current per-language
JSON workflow (source of truth: `en.json`; improve an existing
language by editing its JSON; add a new one by adding a JSON file
and registering the display name in `LanguageSelector`). Notes
that the older PoEditor sync workflow is no longer authoritative.
## Benefits
- Italian speakers can now use Checkmate in Italian.
- Contributors who follow the "Help with translations?" link from
the CONTRIBUTING TOC land on real, current guidance instead of a
broken anchor.
- Translation quality can be improved incrementally by anyone
editing `it.json` directly; no external tooling required.
The customCSS field existed on the model, types, and form schema but was
never editable, sent to the API, or rendered. Wire it end to end:
- Accept customCSS in the create/update status page body validation and
regenerate the OpenAPI spec
- Add a Custom CSS editor (multiline field) to the status page form and
include the value in the submitted form data
- Support multiline mode in the shared TextInput component
- Inject the saved CSS via a style tag when rendering the status page