Skip to content

feat(app): consolidate the alert detail header into the shared row menu - #3016

Closed
jordan-simonovski wants to merge 2 commits into
mainfrom
jordansimonovski/alert-detail-ux
Closed

feat(app): consolidate the alert detail header into the shared row menu#3016
jordan-simonovski wants to merge 2 commits into
mainfrom
jordansimonovski/alert-detail-ux

Conversation

@jordan-simonovski

Copy link
Copy Markdown
Contributor

The alert detail header spread Edit, Delete and a source link across four controls next to the time picker, and offered a different set of actions from the alerts list for the same alert. This moves them behind the same overflow menu the list uses.

What changed

  • Edit, Delete and Terraform export move into the shared row menu. Terraform export is new to this page; it was already in the list.
  • The source link becomes an icon beside the alert name, with a tooltip naming the destination.
  • The properties block separates configuration from provenance: the creator joins the created and updated timestamps in a dimmed line beneath, rather than sitting inline with the alert's settings.

Key decisions

Shared menu over a page-specific one. The page's own edit modal, delete handler and confirmation are deleted in favour of the list's. Two surfaces maintaining parallel action sets is how they drifted apart in the first place. The menu takes an optional dateRange (this page has a picked range for the preview chart; a list row does not) and an onDeleted callback so the page can navigate away.

Source link next to the title, not in the actions. It navigates to the alert's subject, so it belongs with the identity rather than the verbs acting on the alert. The menu's own source-link item is suppressed here to avoid offering it twice.

Impact

Frontend only. The delete confirmation on this page changes from ConfirmDeleteMenu to the shared useConfirm dialog, and deletion now returns you to /alerts.

This branch sits on top of #3015 and its diff currently includes that commit; the diff shrinks to this page's changes once #3015 merges. Review the second commit (consolidate the alert detail header…) in isolation, or wait for the rebase.

Implementation detail
  • Delete keeps the same query invalidations (alerts, saved-search, dashboards) as the removed handler; navigation moved into onDeleted.
  • Known limitation, unchanged by this PR: neither delete path invalidates the single-alert query key, so back-navigation renders cached data until the refetch 404s.
  • The edit modal now mounts per row on the alerts list. Its queries are key-shared so there is no fetch storm, but the modal state lives inside a virtualised row — Mantine's scroll lock makes unmount-while-open unlikely, though hoisting one modal above the virtualiser would remove the risk entirely.
  • Full app suite passes (3385).

…ource icons

The alerts page row menu now opens the alert editor, so changing a threshold
no longer means navigating into the alert first. The modal needs a range for
its threshold preview; a list row has none, so it derives one from the alert's
interval when the modal opens.

Each row's source icon gains a tooltip and accessible label naming what the
alert watches, a new filter narrows the list by that source, and free-text
search matches it too. All three read one getAlertSourceLabel helper so their
wording cannot drift. Named 'alert source' rather than 'type' (taken by
detection type) or bare 'source' (reads as a data source).

The creator moves out of the shared properties line into its own dimmed
sub-line: it is provenance, not configuration, and at equal weight it pushed
the line into a second row that broke mid-phrase. Remaining segments no longer
wrap mid-phrase. Team settings tabs gain icons.
Edit, Delete and Terraform export move behind the overflow menu the alerts
list already uses, so the header stops spreading four buttons across the top
and the two surfaces cannot drift apart on which actions an alert offers. The
menu gains an optional dateRange (the detail page has a picked range for the
edit modal's threshold preview, where a list row does not) and an onDeleted
callback so the page can navigate away once the alert is gone.

The link to what the alert watches becomes an icon beside the alert name: it
navigates to the alert's subject, so it belongs with the identity rather than
with the verbs acting on the alert.

The properties block separates configuration from provenance, moving the
creator down to join the created and updated timestamps in one dimmed line.
@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 16b0e48

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Minor
@hyperdx/api Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 28, 2026 4:28am
hyperdx-storybook Ready Ready Preview Aug 28, 2026 4:28am

Request Review

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Aug 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 356 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 8
  • Production lines changed: 356 (+ 70 in test files, excluded from tier calculation)
  • Branch: jordansimonovski/alert-detail-ux
  • Author: jordan-simonovski

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR consolidates alert edit, delete, source navigation, and Terraform export behavior around the shared row menu while adding source filtering and clearer alert metadata presentation.

  • Adds edit support, detail-page deletion callbacks, and preview date ranges to the shared alert row menu.
  • Adds source labels, source filtering, and source-aware free-text matching to the alerts list.
  • Moves creator and timestamp provenance out of the configuration summary.
  • Adds icons to team settings tabs and expands focused alert-component tests.

Confidence Score: 4/5

The PR appears safe to merge, with non-blocking cleanup recommended for semantic color usage and eager per-row edit-modal mounting.

The shared action and filtering flows preserve their functional contracts, while the remaining accepted concerns are theme consistency and avoidable closed-modal work in virtualized alert rows.

Files Needing Attention: packages/app/src/AlertDetailPage.tsx, packages/app/src/components/alerts/AlertRowMenu.tsx

Important Files Changed

Filename Overview
packages/app/src/components/alerts/AlertRowMenu.tsx Centralizes edit, export, and delete behavior, but unconditionally mounts a query-consuming edit modal for every rendered row.
packages/app/src/AlertDetailPage.tsx Replaces dedicated detail actions with the shared menu and a title source link; the new ActionIcon bypasses semantic color tokens.
packages/app/src/AlertsPage.tsx Adds URL-backed source filtering and source-label search without an established functional defect.
packages/app/src/components/alerts/AlertDetailProperties.tsx Separates creator and timestamp provenance from alert configuration while retaining defensive time formatting.
packages/app/src/utils/alerts.ts Adds a shared, deterministic human-readable label for supported and unresolved alert sources.
packages/app/src/TeamPage.tsx Adds icons to existing team settings tabs without changing their navigation behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Detail[Alert detail page] --> Menu[Shared AlertRowMenu]
  List[Alerts list row] --> Menu
  Menu --> Edit[EditAlertModal]
  Menu --> Export[Terraform export]
  Menu --> Confirm[Delete confirmation]
  Confirm --> Delete[Delete alert]
  Delete --> Invalidate[Invalidate alert and source caches]
  Invalidate --> Callback[Optional onDeleted callback]
  Callback --> Alerts[Alerts list route]
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (1): Last reviewed commit: "feat(app): consolidate the alert detail ..." | Re-trigger Greptile

<ActionIcon
component={Link}
href={alertUrl}
variant="subtle"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Raw palette color bypasses theme

The new source-link ActionIcon uses the raw gray Mantine color rather than a semantic theme token, making this control inconsistent across themes and future palette changes.

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

Comment on lines 197 to +203
{/* Outside the dropdown, which unmounts on close. */}
<EditAlertModal
alert={alert}
opened={editOpened}
onClose={() => setEditOpened(false)}
dateRange={previewRange}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Closed modals mount per row

Every rendered alert row now mounts an EditAlertModal even while closed, so its source queries, form state, watched values, and preview calculation run for each visible virtualized row. Mounting the modal on demand would avoid this hidden work and reduce its coupling to row virtualization.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@github-actions

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 322 passed • 1 skipped • 1204s

Status Count
✅ Passed 322
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@github-actions

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found. No P0/P1 ship-blockers survived re-grading — the change is a frontend-only UI consolidation with no data, auth, or contract impact. The recommendations below are worth addressing before merge.

🟡 P2 -- recommended

  • packages/app/src/components/alerts/AlertRowMenu.tsx:201 -- EditAlertModal now mounts once per alerts-list row, and its useSavedSearch/useSource queries and full react-hook-form instance run per row even while the modal is closed, adding per-row cost to the list that was just virtualized for performance.
    • Fix: Gate the modal mount on editOpened (e.g. {editOpened && <EditAlertModal … />}) or gate its queries' enabled on the open state, mirroring the existing enabled: terraformOpened pattern.
    • correctness, performance
  • packages/app/src/components/alerts/AlertRowMenu.tsx:70 -- the edit modal's open state lives inside a virtualized row, so a background useAlerts refetch or a filter change that reorders or drops the row unmounts the modal and silently discards any unsaved edits.
    • Fix: Lift the "which alert is being edited" state above the virtualizer and render a single EditAlertModal keyed by that id.
    • julik-frontend-races, adversarial, correctness
  • packages/app/src/AlertsPage.tsx:42 -- the new source filter, allSources derivation, and free-text source matching have no test coverage; no AlertsPage test file exists, so this new user-facing filtering logic is entirely unverified.
    • Fix: Add an AlertsPage test covering source-filter selection, partial-word source search, and the URL-seeded-source-not-in-list branch.
    • testing, maintainability, project-standards
🔵 P3 nitpicks (4)
  • packages/app/src/AlertsPage.tsx:84 -- free-text source search uses word.startsWith(q), so a single-character query silently matches every alert of a source type (typing d pulls in all "Dashboard tile" alerts).
    • Fix: Match the full source label with includes, or require a minimum query length before source-word matching applies.
  • packages/app/src/components/alerts/AlertRowMenu.tsx:96 -- the derivedRange useMemo lists editOpened in its dependency array purely to force recompute but never reads it, requiring an eslint-disable that could mask a genuinely missing dependency if the memo grows.
    • Fix: Drop the useMemo and compute const previewRange = dateRange ?? intervalToDateRange(alert.interval) inline, since the function is pure and cheap.
  • packages/app/src/components/alerts/AlertDetails.tsx:213 -- the inline alert.createdBy.name || alert.createdBy.email fallback is duplicated here and in AlertDetailProperties.tsx instead of reusing the existing getAlertCreatorLabel helper.
    • Fix: Reuse getAlertCreatorLabel(alert) in both sites so creator formatting stays consistent.
  • packages/app/src/AlertsPage.tsx:42 -- the alertSource URL query param stores the human-readable label ("Dashboard tile") rather than the AlertSource enum, so changing the label wording later would break bookmarked or shared filter URLs.
    • Fix: Persist the enum value in the query param and use getAlertSourceLabel only for display in the Select.

Pre-existing (not counted toward verdict)

  • packages/app/src/components/alerts/AlertRowMenu.tsx:126 -- deletion invalidates the alerts, saved-search, and dashboards query keys but not the single-alert query key, so navigating back to a just-deleted alert's detail page renders cached data until the refetch 404s. Both the old and new delete paths share this behavior; it is not introduced by this diff.
    • julik-frontend-races, adversarial

Reviewers (11): correctness, adversarial, kieran-typescript, julik-frontend-races, testing, maintainability, project-standards, performance, agent-native, learnings-researcher, previous-comments.

Testing gaps:

  • No test asserts the new onDeleted callback fires on successful delete and not on the failure/catch path.
  • No test distinguishes the explicit dateRange prop from the interval-derived fallback passed to EditAlertModal (the current test mock discards all props but opened).
  • AlertDetailProperties' new "Created by …" prefix branch and AlertDetails.tsx's source-icon/tooltip and creator sub-line are untested.
  • No integration test covers the AlertDetailPage row-menu wiring (delete → router.push('/alerts'), suppressed source-link item, source-link icon).

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

Labels

review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant