Skip to content

refactor(app): split ServicesDashboardPage into smaller sibling files#2626

Open
brandon-pereira wants to merge 2 commits into
mainfrom
brandon/brandon-split-service-dashboard-page
Open

refactor(app): split ServicesDashboardPage into smaller sibling files#2626
brandon-pereira wants to merge 2 commits into
mainfrom
brandon/brandon-split-service-dashboard-page

Conversation

@brandon-pereira

Copy link
Copy Markdown
Member

Summary

ServicesDashboardPage.tsx had grown to 1,759 lines in a single file — the page shell, three tab views, an endpoint latency chart, a service selector, and shared filter/query helpers all interleaved. This splits it into a ServicesDashboardPage/ directory of smaller, cohesive files so the tabs and helpers can be read and edited in isolation.

This is a pure file-move refactor: no behavior, logic, JSX, or effects changed. Code was relocated verbatim and relative imports rewired.

Line count

Lines
Before — ServicesDashboardPage.tsx (monolith) 1,759
After — largest file (HttpTab.tsx) 699

New structure

packages/app/src/ServicesDashboardPage/
  index.ts                 (3)    barrel — re-exports the original public API only
  types.ts                 (12)   AppliedConfig type
  helpers.ts               (78)   pickSourceConfigFields, buildInFilterCondition,
                                   getScopedFilters, MAX_NUM_SERIES
  ServicesDashboardPage.tsx (446)  page shell, ServiceSelectControlled, dynamic wrapper
  HttpTab.tsx              (699)   HttpTab + EndpointLatencyChart (colocated)
  DatabaseTab.tsx          (517)   DatabaseTab
  ErrorsTab.tsx            (76)    ErrorsTab
  __tests__/ServicesDashboardPage.test.tsx (171)  new smoke test

Drop-in barrel

index.ts re-exports exactly the original public API — the default page (with getLayout), EndpointLatencyChart, and buildInFilterCondition — so every external consumer keeps importing @/ServicesDashboardPage unchanged:

  • pages/services.tsx (default export)
  • components/ServiceDashboardEndpointSidePanel.tsx (EndpointLatencyChart)
  • src/__tests__/ServicesDashboardPage.test.ts (buildInFilterCondition)

Decisions

  • EndpointLatencyChart colocated with HttpTab rather than its own file — HttpTab renders it directly, so splitting them would introduce a cross-file cycle. The barrel still re-exports it for the side panel.
  • Relative imports rewritten to @/ absolute in the moved files (they now sit one level deeper); sibling imports within the directory use ./.
  • One essential smoke test added — the existing buildInFilterCondition unit test does not render the component, so a minimal "it mounts" test was added (heavy children/hooks stubbed). No per-sub-component tests.

Verification

  • npx tsc --noEmit — clean
  • make ci-lint — clean across all 5 projects
  • Unit tests — 4 passed (existing buildInFilterCondition + new smoke test)
  • E2E services-dashboard.spec.ts3 passed (page load, HTTP tab charts, Lucene filtering)

Follow-ups (intentionally not done here)

  • Each tab independently calls useSource + useServiceDashboardExpressions with identical args; could be lifted to a shared hook later (behavior-adjacent, out of scope for a pure move).
  • Two pre-existing lint warnings (as [Date, Date] assertion; a setter-naming warning) were carried over verbatim.

Pure file-move refactor with no behavior change. Breaks the 1,759-line
ServicesDashboardPage.tsx monolith into a ServicesDashboardPage/ directory of
smaller cohesive files (largest resulting file: 699 lines).

An index.ts barrel re-exports the original public API (default page,
EndpointLatencyChart, buildInFilterCondition) so all external consumers resolve
unchanged.
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 85ff7c1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel

vercel Bot commented Jul 10, 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, Comment Jul 10, 2026 9:58pm
hyperdx-storybook Ready Ready Preview, Comment Jul 10, 2026 9:58pm

Request Review

@github-actions github-actions Bot added the review/tier-4 Critical — deep review + domain expert sign-off label Jul 10, 2026
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Large diff: 3590 production lines changed (threshold: 1000)

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 8
  • Production lines changed: 3590 (+ 187 in test files, excluded from tier calculation)
  • Branch: brandon/brandon-split-service-dashboard-page
  • Author: brandon-pereira

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

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR splits the services dashboard page into smaller sibling files. The main changes are:

  • Moved the page shell into ServicesDashboardPage/ServicesDashboardPage.tsx.
  • Moved the HTTP, database, and errors tabs into separate files.
  • Moved shared helpers and types into helpers.ts and types.ts.
  • Added a barrel export to preserve the existing @/ServicesDashboardPage imports.
  • Added a smoke test for rendering the page shell.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
packages/app/src/ServicesDashboardPage/tests/ServicesDashboardPage.test.tsx Adds a page-shell smoke test with an async-safe next/dynamic mock.
packages/app/src/ServicesDashboardPage/index.ts Adds the barrel export for the default page, EndpointLatencyChart, and buildInFilterCondition.
packages/app/src/ServicesDashboardPage/ServicesDashboardPage.tsx Contains the extracted services dashboard page shell and dynamic export.
packages/app/src/ServicesDashboardPage/HttpTab.tsx Contains the extracted HTTP tab and endpoint latency chart.
packages/app/src/ServicesDashboardPage/DatabaseTab.tsx Contains the extracted database tab.
packages/app/src/ServicesDashboardPage/ErrorsTab.tsx Contains the extracted errors tab.
packages/app/src/ServicesDashboardPage/helpers.ts Contains the extracted shared filter and chart helper code.
packages/app/src/ServicesDashboardPage/types.ts Contains the extracted shared dashboard config types.

Reviews (2): Last reviewed commit: "test(app): fix flaky dynamic mock in Ser..." | Re-trigger Greptile

Comment thread packages/app/src/ServicesDashboardPage/__tests__/ServicesDashboardPage.test.tsx Outdated
The mocked next/dynamic wrapper assigned the resolved component to a closure
variable inside loader().then(), which never scheduled a React re-render — the
tree could stay empty and the synchronous getByTestId assertion could flake.
Hold the resolved component in state so its async arrival triggers a re-render,
and await the shell via waitFor. Test-only change; no src behavior touched.
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 235 passed • 3 skipped • 1515s

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

Tests ran across 4 shards in parallel.

View full report →

@github-actions

Copy link
Copy Markdown
Contributor

Deep Review

Scope: packages/app/src/ServicesDashboardPage.tsx (1,759-line monolith) deleted and split into a ServicesDashboardPage/ directory (barrel index.ts, types.ts, helpers.ts, page shell, HttpTab + EndpointLatencyChart, DatabaseTab, ErrorsTab, one smoke test). Base: 046220a6. Mode: report-only.

No critical issues found. The "pure file-move refactor" claim holds up under scrutiny. Correctness verified the move byte-for-byte block-by-block; the public export surface, getLayout static-property preservation, and every rewired @/ / ./ import were independently confirmed. Orchestrator cross-checks corroborate: the hook-usage histogram (useSource ×6, useServiceDashboardExpressions ×5, useState/useMemo/useEffect/useCallback ×3 each, …), action-handler count (14), and JSX markers (32) are all identical between the monolith and the split. No behavior, logic, JSX, effect-dependency array, or default-parameter value diverged.

🔵 P3 nitpicks (3)
  • packages/app/src/ServicesDashboardPage/index.ts:2 — the barrel re-exports EndpointLatencyChart (line 2) before the page default (line 3), and that order is now load-bearing: the page shell pulls in ServiceDashboardEndpointSidePanel, which imports EndpointLatencyChart back from the barrel, forming a cycle that only resolves because HttpTab is evaluated first. The cycle pre-existed in the monolith and works today, but reordering the exports could yield an undefined binding at module-eval time.
    • Fix: import EndpointLatencyChart directly from @/ServicesDashboardPage/HttpTab in ServiceDashboardEndpointSidePanel.tsx to break the cycle and remove the ordering dependency.
    • correctness, maintainability, kieran-typescript
  • packages/app/src/ServicesDashboardPage/HttpTab.tsx:1 — three extracted files (HttpTab.tsx 699, DatabaseTab.tsx 517, ServicesDashboardPage.tsx 446) still exceed the project's documented 300-line file-size guideline even after the split; content was relocated verbatim from an already-oversized file.
    • Fix: optionally extract EndpointLatencyChart from HttpTab.tsx into its own file in a follow-up to bring the largest file closer to the guideline.
    • project-standards
  • packages/app/src/ServicesDashboardPage/index.ts:1 — feature tests now live in two directories (the pre-existing src/__tests__/ServicesDashboardPage.test.ts for buildInFilterCondition and the new co-located __tests__/ServicesDashboardPage.test.tsx), and the barrel exports buildInFilterCondition largely to keep the old test's import path working.
    • Fix: consider consolidating the helper test into the co-located __tests__ dir importing from ./helpers, so the barrel export isn't public API purely for a test.
    • maintainability

Reviewers (8): correctness, testing, maintainability, project-standards, kieran-typescript, api-contract, agent-native, learnings-researcher. (adversarial was dispatched but did not return before synthesis; its attack surface was independently covered.)

Testing gaps:

  • The new smoke test only exercises the empty-state branch (useSource() mock returns { data: undefined }, forcing the "Please select a trace source" path); the <Tabs> render and tab-switching logic are never reached — acceptable for an intentionally-scoped refactor smoke test.
  • No behavioral test asserts the generated ClickHouse chart configs (select/filters/groupBy/orderBy) for HttpTab/DatabaseTab/ErrorsTab, so a future non-verbatim edit to those large config objects would not be caught. Pre-existing gap, not introduced by this diff.
  • tsc --noEmit and eslint could not be executed by the reviewers in this environment; the author reports both clean in CI — worth confirming there.

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

Labels

review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant