Skip to content

feat(dashboard): adopt <Card> + <Cluster> in AlertsPanel [Phase 1e]#624

Merged
barach6662001-bit merged 1 commit intomainfrom
replit/phase-1e-migrate-alerts-panel
Apr 24, 2026
Merged

feat(dashboard): adopt <Card> + <Cluster> in AlertsPanel [Phase 1e]#624
barach6662001-bit merged 1 commit intomainfrom
replit/phase-1e-migrate-alerts-panel

Conversation

@barach6662001-bit
Copy link
Copy Markdown
Owner

Phase 1e — Adopt <Card> + <Cluster> in AlertsPanel

Continues the dashboard's design-system migration started in Phase 1d. The dashboard AlertsPanel panel now uses the <Card> primitive for its container and <Cluster as="header"> for its title row.

What changed

  • Container<section className={s.card}><Card as="section" radius="lg" aria-label={…}>. All container concerns (background, border, radius, padding, overflow) now come from the primitive.
  • Header<header className={s.header}><Cluster as="header" justify="between" align="center">. The title is now a real <h2>, restoring proper landmark semantics for the panel.
  • Local CSS — removed .card, .card::before, and .header (–22 lines of duplicated container styling). Kept everything alert-specific: row grid, severity tints, count badges, .showAll button.

Why not CardHeader / CardContent / CardFooter?

The new subcomponents from #622 are intentionally opinionated:

Subcomponent Anatomy Fits this panel?
CardHeader vertical Stack of title + description ❌ panel header is horizontal (eyebrow + count badge)
CardContent Stack slot with default gap ❌ content is a single <ul> — wrapper would be empty
CardFooter Cluster justify="end" actions row ❌ "show all" is a full-width hairline-bordered control

Forcing them here would either alter the visual hierarchy or add empty wrapper divs. They remain available for panels whose anatomy matches them (multi-block content cards, right-aligned action footers).

Tests

frontend/src/components/dashboard/__tests__/AlertsPanel.test.tsx9 tests:

  • empty state returns null
  • one row per non-zero count, sorted by severity (critical → info)
  • panel renders as <section aria-label> with <h2> title
  • total count badge equals number of rows
  • each row has the correct severity class for the tint
  • clicking a row calls navigate(route) with the right path
  • 5-row boundary case does not render the "show all" footer
  • Card adoption smoke-test (data-variant="subtle", var(--radius-lg))

Local verification

  • tsc -b: clean
  • eslint: 0 errors (warning count unchanged)
  • vitest run src/design-system src/components/dashboard: 67/67 passing (9 new + 25 DS + others)
  • Full test + build run delegated to CI.

Phase 1 progress

Branch: replit/phase-1e-migrate-alerts-panel off main@8ece50b.

  Migrates the dashboard AlertsPanel from a hand-rolled `s.card` rule
  to the design-system <Card> primitive. The horizontal title/badge
  header is rebuilt with <Cluster as="header" justify="between">.
  The alert-specific affordances (row grid, severity tints, count
  badges, "show all" footer button) remain in the local CSS module —
  they are not generic enough to belong in the primitive layer yet.

  Why not CardHeader/CardContent/CardFooter?
  - CardHeader is a vertical Stack of title+description; this panel
    has a horizontal eyebrow + count-badge layout, so <Cluster as=
    "header"> is a closer fit.
  - The content slot is a single <ul>; wrapping it in CardContent
    (a Stack) would add an empty flex container with no value.
  - The "show all" button is a full-width hairline-bordered control,
    not a justify-end actions row, so CardFooter does not apply.
  These subcomponents are kept available for panels whose anatomy
  matches them (PageHeader-style sections, multi-block content cards,
  right-aligned action footers).

  Accessibility improvement: the panel title is now a real <h2>
  inside the <section aria-label>, instead of a styled <span>.

  CSS deltas:
  - Removed .card (background, border, radius, padding, overflow)
  - Removed .card::before (decorative gradient hairline)
  - Removed .header (replaced by <Cluster justify="between">)
  Net: -22 lines of duplicated container styling.

  Tests (frontend/src/components/dashboard/__tests__/AlertsPanel.test.tsx):
  - empty state returns null when all counts are 0
  - one row per non-zero count, sorted by severity (critical → info)
  - panel renders as <section aria-label> with <h2> title
  - total count badge equals number of rows
  - each row has a severity-specific class for the tint
  - clicking a row calls navigate(route)
  - 5-row boundary case does not render the "show all" footer
  - Card adoption smoke-test (data-variant="subtle", radius=lg)

  Verification (local):
  - frontend tsc -b: clean
  - frontend lint: 0 errors (warning count unchanged)
  - vitest design-system + dashboard subset: 67/67 passing
@barach6662001-bit barach6662001-bit merged commit 16cf2ad into main Apr 24, 2026
3 checks passed
@barach6662001-bit barach6662001-bit deleted the replit/phase-1e-migrate-alerts-panel branch April 24, 2026 11:46
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cfc4b4f71

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

<header className={s.header}>
<span className={s.title}>{dash.needsAttention}</span>
<Card as="section" radius="lg" aria-label={dash.needsAttention}>
<Cluster as="header" justify="between" align="center" gap="2">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep alerts header on a single row

Switching the header to Cluster changed its flex behavior from non-wrapping to wrapping by default, so on narrow layouts or longer localized needsAttention strings the count badge can drop to a second line. The previous header styling kept title and badge in one horizontal row, and this panel’s anatomy depends on that for consistent scanability. Set nowrap on this Cluster (or otherwise disable wrapping) to preserve the prior behavior.

Useful? React with 👍 / 👎.

barach6662001-bit added a commit that referenced this pull request Apr 24, 2026
…[Phase 1f] (#625)

Finishes the DashboardV2 row-card migration. Both remaining sibling
  panels (OperationsTimeline in row 4, UpcomingPanel in row 5) now use
  the design-system <Card> primitive instead of the local `s.card`
  wrapper. Combined with Phase 1d (WarehouseSnapshot) and Phase 1e
  (AlertsPanel), every dashboard surface in this row is now driven by
  the same primitive.

  Wrapper migration (DashboardV2.tsx)
  -----------------------------------
  - <div className={s.card}><OperationsTimeline …/></div> → <Card>…</Card>
  - <div className={s.card}><UpcomingPanel …/></div>     → <Card>…</Card>

  Both sites are pure wrapper swaps. <Card> defaults
  (variant=subtle, radius=xl=16px, padding=5=20px, bordered=true) are
  an exact drop-in for the legacy `.card` rule, so the visual contract
  (background, border, radius, padding) is preserved. The internal
  list/item content of each panel is untouched.

  CSS dead-code removal (DashboardV2.module.css)
  ----------------------------------------------
  With both consumers gone, these rules become unreferenced and are
  deleted (≈39 lines):
  - .card                       (background, border, radius, padding, transition)
  - .card:hover                 (subtle background lift)
  - .card::before               (decorative top hairline)
  - .cardHeader                 (flex justify-between row, never used in DashboardV2.tsx)
  - .cardLink, .cardLink:hover  (eyebrow link button, never used in DashboardV2.tsx)
  A short tombstone comment is left in place so future readers see why
  the section is empty and where to look (Card primitive path).

  Out of scope
  ------------
  - No route changes
  - No API changes
  - No business-logic changes
  - No refactor of inner panel markup
  - No new dependencies
  - No Card highlight prop introduction
  - No new tests (spec: only update if coverage exists; none did)

  Verification (local)
  --------------------
  - frontend tsc -b: clean
  - frontend lint: 0 errors (warning count unchanged)
  - vitest design-system + dashboard subset: 67/67 passing
  - Full test + build run delegated to CI.

  Phase 1 status after this PR
  ----------------------------
  - ✅ 1a — TS tokens (#616)
  - ✅ 1b — Layout + Typography primitives (#620)
  - ✅ 1c — PageHeader migration (#621)
  - ✅ 1d — Surface + Card primitives + first dashboard adoption (#622)
  - ✅ 1e — AlertsPanel adoption (#624)
  - ✅ 1f — OperationsTimeline + UpcomingPanel adoption (this PR)

Co-authored-by: replit-agent <agent@replit.local>
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.

1 participant