From cc500ae4835373067745a22588169e6620c0297e Mon Sep 17 00:00:00 2001 From: Elizabet Oliveira Date: Fri, 15 May 2026 12:55:31 +0100 Subject: [PATCH] Migrate Service Map to PageLayout. Uses shared sticky header with source, sampling, and time controls without a duplicate title for top-level navigation. Co-authored-by: Cursor --- .changeset/page-migrate-service-map.md | 5 + packages/app/src/DBServiceMapPage.tsx | 202 +++++++++++++------------ 2 files changed, 109 insertions(+), 98 deletions(-) create mode 100644 .changeset/page-migrate-service-map.md diff --git a/.changeset/page-migrate-service-map.md b/.changeset/page-migrate-service-map.md new file mode 100644 index 0000000000..21019c4b57 --- /dev/null +++ b/.changeset/page-migrate-service-map.md @@ -0,0 +1,5 @@ +--- +'@hyperdx/app': patch +--- + +chore: migrate Service Map to shared `PageLayout` with a sticky toolbar (source, sampling, time range) and no duplicate page title. diff --git a/packages/app/src/DBServiceMapPage.tsx b/packages/app/src/DBServiceMapPage.tsx index b9d209410f..211a376652 100644 --- a/packages/app/src/DBServiceMapPage.tsx +++ b/packages/app/src/DBServiceMapPage.tsx @@ -4,10 +4,11 @@ import Head from 'next/head'; import { parseAsInteger, useQueryState } from 'nuqs'; import { useForm, useWatch } from 'react-hook-form'; import { SourceKind, TTraceSource } from '@hyperdx/common-utils/dist/types'; -import { Box, Button, Group, Modal, Slider, Text } from '@mantine/core'; +import { Button, Group, Modal, Slider, Text } from '@mantine/core'; import { IconConnection } from '@tabler/icons-react'; import EmptyState from '@/components/EmptyState'; +import { PageLayout } from '@/components/PageLayout'; import { IS_LOCAL_MODE } from '@/config'; import { withAppNav } from '@/layout'; @@ -114,113 +115,118 @@ function DBServiceMapPage() { [brandName], ); + const sourceSelect = source ? ( + + } + /> + ) : null; + + const headerActions = ( + + + Sampling {samplingLabel} + +
+ factor.value === samplingFactor, + )} + onChange={v => setSamplingFactor(SAMPLING_FACTORS[v].value)} + showLabelOnHover={false} + /> +
+ +
+ ); + if (!isLoading && !hasTraceSources) { return ( - + <> {head} - - Service Map - - {IS_LOCAL_MODE && ( - setIsCreateSourceModalOpen(false)} - title="Configure New Trace Source" - > - setIsCreateSourceModalOpen(false)} - /> - - )} - } - title="No trace sources configured" - description="The Service Map visualizes relationships between your services using trace data. Configure a trace source to get started." - maw={600} - > - {IS_LOCAL_MODE ? ( - - ) : ( - - )} - - + + {IS_LOCAL_MODE && ( + setIsCreateSourceModalOpen(false)} + title="Configure New Trace Source" + > + setIsCreateSourceModalOpen(false)} + /> + + )} + } + title="No trace sources configured" + description="The Service Map visualizes relationships between your services using trace data. Configure a trace source to get started." + maw={600} + > + {IS_LOCAL_MODE ? ( + + ) : ( + + )} + + + } + /> + ); } return source ? ( - + <> {head} - - - Service Map - - } - /> - - - - Sampling {samplingLabel} - -
- factor.value === samplingFactor, - )} - onChange={v => setSamplingFactor(SAMPLING_FACTORS[v].value)} - showLabelOnHover={false} - /> -
- -
-
- -
+ ) : null; }