Files
supabase/apps/studio/components/layouts/ObservabilityLayout/ObservabilityLayout.tsx
T
Danny WhiteandJoshen Lim a753a599ea feat(studio): page titles for logs and observability (#43535)
Stacked PR 3/5 for page title improvements. Includes Logs and
Observability layout title adoption, unified logs title cleanup, and
small route title fixes.

_Base:
[dnywh/feat/page-titles](https://github.com/supabase/supabase/pull/43538)_

---

## What kind of change does this PR introduce?

- Resolves FE-1960
- Resolves FE-1983
- Resolves DEPR-207

## What is the current behavior?

Page titles between surfaces are inconsistent and vague. Sometimes they
say the product name:

```
My Project | My Org | Supabase
```

...even when on a specific surface like Database > Tables.

Other times they show the entity name but skip over the project or org
name :

```
Edge Functions | Supabase
```

## What is the new behavior?

Logs and Observability adopts the layout title format introduced in
https://github.com/supabase/supabase/pull/43538:

```
users | Table Editor | My Project | My Org | Supabase
hello-world | Logs | Edge Functions | My Project | My Org | Supabase
Backups | Database | My Project | My Org | Supabase
Authentication | My Project | My Org | Supabase
```

That format is:

entity, section, surface, project, org, brand.

Also includes unified logs title cleanup, and small route title fixes.

## Additional context

Related stacked PRs also based on the the original
[dnywh/feat/page-titles](https://github.com/supabase/supabase/pull/43538)
branch:

- https://github.com/supabase/supabase/pull/43534
- https://github.com/supabase/supabase/pull/43536
- https://github.com/supabase/supabase/pull/43537

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-03-12 14:08:15 +11:00

113 lines
3.4 KiB
TypeScript

import { IS_PLATFORM, LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useIndexAdvisorStatus } from 'components/interfaces/QueryPerformance/hooks/useIsIndexAdvisorStatus'
import { BannerIndexAdvisor } from 'components/ui/BannerStack/Banners/BannerIndexAdvisor'
import { BannerMetricsAPI } from 'components/ui/BannerStack/Banners/BannerMetricsAPI'
import { useBannerStack } from 'components/ui/BannerStack/BannerStackProvider'
import { UnknownInterface } from 'components/ui/UnknownInterface'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { withAuth } from 'hooks/misc/withAuth'
import { usePathname } from 'next/navigation'
import { PropsWithChildren, useEffect, useRef } from 'react'
import { ProjectLayout } from '../ProjectLayout'
import ObservabilityMenu from './ObservabilityMenu'
interface ObservabilityLayoutProps {
title: string
}
const ObservabilityLayoutContent = ({
title,
children,
}: PropsWithChildren<ObservabilityLayoutProps>) => {
const { ref } = useParams()
const pathname = usePathname()
const { addBanner, dismissBanner } = useBannerStack()
const { isIndexAdvisorAvailable, isIndexAdvisorEnabled } = useIndexAdvisorStatus()
const [isMetricsBannerDismissed] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.OBSERVABILITY_BANNER_DISMISSED(ref ?? ''),
false
)
const [isIndexAdvisorBannerDismissed] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.INDEX_ADVISOR_NOTICE_DISMISSED(ref ?? ''),
false
)
useEffect(() => {
if (!isMetricsBannerDismissed && IS_PLATFORM) {
addBanner({
id: 'metrics-api-banner',
isDismissed: false,
content: <BannerMetricsAPI />,
priority: 1,
})
} else {
dismissBanner('metrics-api-banner')
}
}, [isMetricsBannerDismissed, addBanner, dismissBanner])
const prevPathnameRef = useRef(pathname)
useEffect(() => {
const isQueryPerformancePage = pathname?.includes('/query-performance')
if (
isQueryPerformancePage &&
isIndexAdvisorAvailable &&
!isIndexAdvisorEnabled &&
!isIndexAdvisorBannerDismissed
) {
addBanner({
id: 'index-advisor-banner',
isDismissed: false,
content: <BannerIndexAdvisor />,
priority: 3,
})
} else if (isIndexAdvisorBannerDismissed || !isQueryPerformancePage || isIndexAdvisorEnabled) {
dismissBanner('index-advisor-banner')
}
prevPathnameRef.current = pathname
}, [
pathname,
isIndexAdvisorAvailable,
isIndexAdvisorEnabled,
isIndexAdvisorBannerDismissed,
addBanner,
dismissBanner,
])
const { reportsAll } = useIsFeatureEnabled(['reports:all'])
if (reportsAll) {
return (
<ProjectLayout
product="Observability"
browserTitle={{ section: title }}
productMenu={<ObservabilityMenu />}
isBlocking={false}
>
{children}
</ProjectLayout>
)
} else {
return <UnknownInterface urlBack={`/project/${ref}`} />
}
}
const ObservabilityLayout = (props: PropsWithChildren<ObservabilityLayoutProps>) => {
const { ref } = useParams()
const { reportsAll } = useIsFeatureEnabled(['reports:all'])
if (reportsAll) {
return <ObservabilityLayoutContent {...props} />
} else {
return <UnknownInterface urlBack={`/project/${ref}`} />
}
}
export default withAuth(ObservabilityLayout)