Skip to content

feat(frontend): shared EmptyState and PageHeader components - #4014

Merged
Dalanir merged 3 commits into
mainfrom
feat/page-header-empty-state
Nov 1, 2026
Merged

Dalanir merged 3 commits into
mainfrom
feat/page-header-empty-state

Conversation

@Dalanir

@Dalanir Dalanir commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A UI audit found page-level states and panel headers hand-rolled separately on each page. This adds two small shared components and moves the clear duplicates onto them.

  • EmptyState (src/components/EmptyState.vue): props title, description?, icon? (component), iconClass?; slot #action. Renders an h2 (same level as before), icon is aria-hidden.
    • Replaces 24 identical not-found / preview-disabled blocks on app, bundle, channel and device pages.
    • The existing AppPageNotFound (mounted through AppPageFrame) now uses EmptyState, and the 8 pages that inlined a copy of it now render <AppPageNotFound />.
  • PageHeader (src/components/PageHeader.vue): props title, description?; slots #badges (next to the title), default (under the description), #actions (right side). Renders an h2 (same level as before).
    • Used by OrgNativeBuildStatsPanel, DeliveryLatencyPanel, OrgNotificationStatsPanel (dashboard tabs) and NativeReleaseStatsPanel (observe → native).

Visual change: only the empty state changes slightly. The description now wraps at max-w-md and is centered, as the preview-disabled variant already was, and the descriptions use one colour (text-slate-600, where AppPageNotFound used -500). PageHeader output looks the same as before.

src/pages/settings/** and the settings layout are not touched, to avoid conflicts with #4005.

Visual changes

Before (left/top) vs after (right/bottom), rendered from the real markup and component with src/styles/style.css.

EmptyState: channel not found (light / dark)

nf-light
nf-dark

PageHeader: dashboard Notifications tab (light / dark)

ph-light
ph-dark

Follow-ups (not in this PR)

  • In-card list empty states with mixed heading levels (h3/h4/p) and sizes: BuildTable #empty, OrgNativeBuildStatsPanel no-builds, bundle dependencies "no native dependencies", observe.compatibility empty, observe.errors no insights, observe.native no data, WebhookDeliveryLog no deliveries, UnpaidState, dashboard.vue "no apps yet" overlay. Covering these needs either a heading-level choice or picking one level, which is a separate decision.
  • Card headers on bundle dependencies/manifest (h3) and the notifications page (h1 + badge + refresh): same layout as PageHeader, but a different heading level.
  • ReleaseLivePanel header (uses xl: breakpoints, title hidden when dense).
  • settings/organization/AuditLogs.vue no-org state: skipped because of feat(settings): profile menu, danger zones and DaisyUI buttons #4005.
  • BundleInstallStatsPanel non-dense mode (header and stat grid) can't be reached in prod: its only mount (LiveReleaseDashboard) passes dense. It could be deleted in its own PR.

Test plan

  • bun run lint: only warnings, both pre-existing on main (AppOnboardingSetupChecklist.vue)
  • bun run typecheck (pre-commit)
  • bun run test:unit: 461 files / 5193 tests pass
  • data-testid selectors and the "App not found" heading role used by Playwright are unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added reusable page headers and empty-state layouts across dashboards and app pages.
    • Standardized missing-app, bundle, channel, and device messages while retaining their existing descriptions and navigation actions.

Dalanir and others added 3 commits November 1, 2026 18:12
Replace 24 hand-rolled not-found / preview-disabled blocks on app,
bundle, channel and device pages with one EmptyState component.
App-not-found pages reuse the existing AppPageNotFound, now built on
EmptyState.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dashboard and observe panels each hand-rolled the same title,
description, badges and actions row. Move it into PageHeader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 77e32ade-1325-403f-9121-83acef8d6da6
Commits

Reviewing files that changed from the base of the PR and between 1f530e0 and 6235b55.

Files selected for processing: 28
  • src/components.d.ts
  • src/components/EmptyState.vue
  • src/components/PageHeader.vue
  • src/components/dashboard/AppPageNotFound.vue
  • src/components/dashboard/DeliveryLatencyPanel.vue
  • src/components/dashboard/NativeReleaseStatsPanel.vue
  • src/components/dashboard/OrgNativeBuildStatsPanel.vue
  • src/components/dashboard/OrgNotificationStatsPanel.vue
  • src/pages/app/[app].builds.vue
  • src/pages/app/[app].bundle.[bundle].dependencies.vue
  • src/pages/app/[app].bundle.[bundle].history.vue
  • src/pages/app/[app].bundle.[bundle].manifest.vue
  • src/pages/app/[app].bundle.[bundle].preview.vue
  • src/pages/app/[app].bundle.[bundle].vue
  • src/pages/app/[app].bundles.vue
  • src/pages/app/[app].channel.[channel].devices.vue
  • src/pages/app/[app].channel.[channel].history.vue
  • src/pages/app/[app].channel.[channel].preview.vue
  • src/pages/app/[app].channel.[channel].statistics.vue
  • src/pages/app/[app].channel.[channel].vue
  • src/pages/app/[app].channels.vue
  • src/pages/app/[app].device.[device].deployments.vue
  • src/pages/app/[app].device.[device].logs.vue
  • src/pages/app/[app].device.[device].vue
  • src/pages/app/[app].devices.vue
  • src/pages/app/[app].notifications.vue
  • src/pages/app/[app].observe.compatibility.vue
  • src/pages/app/[app].observe.errors.vue
Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

Walkthrough

The change adds reusable EmptyState and PageHeader components. App pages use the shared empty state for missing resources and disabled previews. Dashboard panels use the shared header for titles, descriptions, badges, and controls.

Changes: Empty-state views

Layer / File(s) Summary
Shared empty-state component and app fallback
src/components/EmptyState.vue, src/components.d.ts, src/components/dashboard/AppPageNotFound.vue
Adds EmptyState with optional description and icon props and an action slot. AppPageNotFound uses it while retaining its translated content and navigation.
Route empty-state adoption
src/pages/app/[app].builds.vue, src/pages/app/[app].bundles.vue, src/pages/app/[app].channels.vue, src/pages/app/[app].devices.vue, src/pages/app/[app].notifications.vue, src/pages/app/[app].observe.*.vue, src/pages/app/[app].bundle.[bundle].*.vue, src/pages/app/[app].channel.[channel].*.vue, src/pages/app/[app].device.[device].*.vue
Replaces inline missing-app and missing-resource layouts with AppPageNotFound or EmptyState. Existing text, icons, actions, navigation targets, and route conditions are retained as described.
Dashboard header component and integration
src/components/PageHeader.vue, src/components.d.ts, src/components/dashboard/*StatsPanel.vue
Adds PageHeader with title and description props and default, badges, and actions slots. Four dashboard panels use it for their existing header content and controls.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Refactor

Suggested reviewers: riderx

Merge Risk: Minimal · up to 6235b

The shared components consolidate existing empty states and panel headers without an identified behavioral regression. The change is mergeable subject to normal build and test checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly identifies the main change: shared frontend EmptyState and PageHeader components.
Description check Passed The description includes a clear summary, visual-change screenshots, implementation scope, follow-ups, and test results. It does not include the template’s separate Checklist section or detailed manua…
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes a clear summary, visual-change screenshots, implementation scope, follow-ups, and test results. It does not include the template’s separate Checklist section or detailed manual reproduction steps, but it is mostly complete and relevant.

Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 1 files. (27 skipped: 27 unsupported.)

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/components/EmptyState.vue

Parsing error: Unexpected token {

src/components/PageHeader.vue

Parsing error: Unexpected token description

src/components/dashboard/DeliveryLatencyPanel.vue

Parsing error: Unexpected token {

  • 23 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@codspeed

codspeed Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing feat/page-header-empty-state (6235b55) with main (1f530e0)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

@Dalanir
Dalanir merged commit 3904080 into main Nov 1, 2026
@Dalanir
Dalanir deleted the feat/page-header-empty-state branch November 1, 2026 18:40

This branch was previously deployed

1 inactive deployment
deepsec-pr — 6235b552 Deployed Nov 1, 2026 by Dalanir
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