Skip to content

Add optional groups for monitors within a status page - #732

Open
Kukks wants to merge 5 commits into
rajnandan1:mainfrom
Kukks:feat/page-monitor-groups
Open

Add optional groups for monitors within a status page#732
Kukks wants to merge 5 commits into
rajnandan1:mainfrom
Kukks:feat/page-monitor-groups

Conversation

@Kukks

@Kukks Kukks commented May 28, 2026

Copy link
Copy Markdown
Contributor

Adds optional groups for monitors within a status page, matching the way Uptime Kuma groups monitors under named sections on a single page.

Each pages_monitors row gets a nullable group_name column. The public page renders ungrouped monitors first (no header — identical to the pre-groups layout) then one bordered section per group with the group name as an h2 header.

Live

Running on this PR at https://status.arklabs.xyz:

The 3 sibling pages (/mainnet, /mutinynet, /signet) are deliberately ungrouped to show backward-compat: a page with no group_name on any monitor renders exactly as it did before this PR.

API

POST /api/v4/pages and PATCH /api/v4/pages/{path} now accept either of:

"monitors": ["my-monitor", "another"]

or

"monitors": [
  { "tag": "api", "group": "Mainnet" },
  { "tag": "node", "group": "Mainnet" },
  { "tag": "telemetry" }
]

Responses also include a group field per monitor. Old clients are unaffected.

Admin UI

Manage → Pages → [page] now renders monitors as group sections with:

  • An "Ungrouped" section that's always shown.
  • "Add group" inline input that creates a local empty section; persists once a monitor is moved into it.
  • Per-monitor "Move to" dropdown listing the other groups (works on touch + desktop). Initial drag-and-drop attempt didn't work on mobile — HTML5 native drag doesn't fire from touch — so the menu replaced it.
  • Removing a group moves its monitors back to "Ungrouped".

Optimistic updates on each move via a new updatePageMonitorGroup /manage/api action; the existing reorderPageMonitors action is reused with a flat tag order.

Backward compatibility

  • Migration is additive — group_name is nullable with no default.
  • Existing pages render identically (single group, name=null, no header).
  • API still accepts monitors: string[]; admin still calls addMonitorToPage the same way (new monitors default to ungrouped).

Out of scope

  • Per-group description / collapsible toggle / explicit group ordering — group order is currently derived from the first monitor of each group's position.

Docker

Combined image (this PR + #731): docker pull kukks/kener:rss-and-groups

Kukks added 5 commits May 28, 2026 18:23
Status pages can now visually group their monitors under named sections
(like Uptime Kuma). Each pages_monitors row gets a nullable group_name
column; UI renders ungrouped monitors first (no header — preserving the
pre-groups layout) then one bordered section per group with the group
name as an h2 header.

API: POST /api/v4/pages and PATCH /api/v4/pages/{path} now accept
either the existing monitors: string[] form, or a richer
monitors: Array<{ tag, group?, position? }> form. Responses also include
a group field per monitor. Old clients are unaffected.

Admin dashboard UI is not updated in this PR — groups can be set via
the public API. Adding admin controls is a sensible follow-up.

Migration is additive (nullable column); existing pages render
identically (single group with name=null).
The Page Monitors editor now renders monitors as group sections with
HTML5 drag-and-drop between groups, an inline 'Add group' control, and
arrow buttons that reorder within a section. The Ungrouped section is
always shown as a drop target so monitors can be returned to the flat
list.

Backend: AddMonitorToPage takes an optional group_name; a new
UpdatePageMonitorGroup controller (and matching repository helper +
dbimpl binding + /manage/api action) sets/clears a single monitor's
group, called optimistically on each drop. Empty groups created via
'Add group' are local-only until a monitor is dragged in.
Firefox (and Chrome in some cases) cancels a drag silently if no data is
set on the dragstart event. We weren't calling dataTransfer.setData(),
so the row's draggable attribute was effectively a no-op there.

Three changes:
1. dragstart now writes the tag as text/plain and sets effectAllowed=move.
2. dragover always calls preventDefault (not gated on the draggedTag state)
   so a drop can still complete if reactive state hasn't flushed.
3. drop falls back to reading the tag from dataTransfer when draggedTag
   is null for any reason.
HTML5 native drag-and-drop doesn't fire from touch events on mobile
browsers, so the previous UX broke for any admin on a phone or tablet.
Swap to a per-row 'Move to' dropdown that works identically on touch
and desktop, matching the existing arrow-button + remove-button pattern
on each row.

- Each monitor row gets a folder-input icon button; clicking it opens
  a menu listing the other groups + Ungrouped (or 'No groups yet'
  guidance when none exist).
- Add group input no longer references project-specific names; the
  placeholder is just 'Group name'.
- Drag handlers, draggable attribute, drop targets, and visual
  drop-target feedback are all removed. Section divs stay for
  structure and the rest of the UX (arrow reorder, remove, add) is
  unchanged.
Every /manage/api action must be allow-listed in allPerms.ts; missing
entries 400 with 'Unknown action' before the dispatcher branch is
reached. The Move-to-group menu was hitting this — clicking a group
optimistically updated the UI then reverted on the failed save.

Tested locally end-to-end with playwright: clicking a group in the
Move-to menu now returns 200, the change persists across reload, and
the public page renders the new group header.
@garadar

garadar commented Aug 3, 2026

Copy link
Copy Markdown

@rajnandan1 the group notion is really great, is it possible to validate this PR ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants