feat(unified-logs): compact toggle banner and ArrowDown selects first log (#46812)

Visual cleanup of the Logs sidebar (unified logs):

- Restyle the "Go back to old logs" and "Try Unified Logs" banners as
full-bleed bordered strips (no card), with an icon-only switch-back
button and a tooltip
- Add a "Beta" badge next to the "Logs" sidebar title
- Rename the product from "Logs & Analytics" to "Logs"

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Unified Logs banner with “Try Unified Logs” and “More
information” actions, plus a “Go back to old logs” utility layout.
  * Show a **“Beta”** badge for Unified Logs eligibility.

* **UX Updates**
* Updated the Logs experience to consistently display under the
**“Logs”** product layout.

* **Tests / Documentation**
* Updated end-to-end checks and examples to expect the **“Logs”**
heading text exactly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: kemal <hello@kemal.earth>
This commit is contained in:
authored and GitHub committed 2026-06-16 10:50:52 +00:00
1 parent 30ee68964f
commit a10bc9a88a
6 files changed
+93 -63

No files matched your search

@@ -0,0 +1,63 @@
import { CircleHelpIcon, Undo2 } from 'lucide-react'
import { Button, cn } from 'ui'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
interface UnifiedLogsBannerProps {
variant: 'promo' | 'utility'
onEnable?: () => void
onMoreInfo?: () => void
onSwitchBack?: () => void
className?: string
}
export function UnifiedLogsBanner({
variant,
onEnable,
onMoreInfo,
onSwitchBack,
className,
}: UnifiedLogsBannerProps) {
const stripClassName = cn(
'flex items-center justify-between gap-3 border-b border-border px-4 py-3',
className
)
if (variant === 'utility') {
return (
<div className={stripClassName}>
<div className="min-w-0 space-y-0.5">
<p className="truncate text-xs font-medium text-foreground">Go back to old logs</p>
<p className="truncate text-xs text-foreground-light">Use the traditional interface</p>
</div>
<ButtonTooltip
type="default"
className="shrink-0 px-1.5"
icon={<Undo2 />}
onClick={onSwitchBack}
tooltip={{ content: { side: 'bottom', text: 'Switch back' } }}
/>
</div>
)
}
return (
<div className={stripClassName}>
<p className="min-w-0 flex-1 truncate text-xs font-medium text-foreground">
Try Unified Logs
</p>
<div className="flex shrink-0 items-center gap-x-2">
<Button size="tiny" type="default" onClick={onEnable}>
Enable
</Button>
<ButtonTooltip
type="default"
className="px-1.5"
icon={<CircleHelpIcon />}
onClick={onMoreInfo}
tooltip={{ content: { side: 'bottom', text: 'More information' } }}
/>
</div>
</div>
)
}
@@ -25,7 +25,7 @@ const LogsLayout = ({ title, children }: PropsWithChildren<LogsLayoutProps>) =>
if (!logsEnabled) {
return (
<ProjectLayout product="Logs & Analytics" browserTitle={{ section: title }}>
<ProjectLayout product="Logs" browserTitle={{ section: title }}>
<UnknownInterface urlBack={`/project/${ref}`} />
</ProjectLayout>
)
@@ -33,14 +33,12 @@ const LogsLayout = ({ title, children }: PropsWithChildren<LogsLayoutProps>) =>
if (!canUseLogsExplorer) {
if (isLoading) {
return (
<ProjectLayout isLoading product="Logs & Analytics" browserTitle={{ section: title }} />
)
return <ProjectLayout isLoading product="Logs" browserTitle={{ section: title }} />
}
if (!isLoading && !canUseLogsExplorer) {
return (
<ProjectLayout product="Logs & Analytics" browserTitle={{ section: title }}>
<ProjectLayout product="Logs" browserTitle={{ section: title }}>
<NoPermission isFullPage resourceText="access your project's logs" />
</ProjectLayout>
)
@@ -49,7 +47,7 @@ const LogsLayout = ({ title, children }: PropsWithChildren<LogsLayoutProps>) =>
return (
<ProjectLayout
product="Logs & Analytics"
product="Logs"
browserTitle={{ section: title }}
productMenu={<LogsSidebarMenuV2 />}
>
@@ -1,5 +1,5 @@
import { IS_PLATFORM, useFlag, useParams } from 'common'
import { ChevronRight, CircleHelpIcon, Plus } from 'lucide-react'
import { ChevronRight, Plus } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React, { useState } from 'react'
@@ -29,7 +29,7 @@ import { useIsETLPrivateAlpha } from '@/components/interfaces/Database/Replicati
import { LOG_DRAIN_TYPES } from '@/components/interfaces/LogDrains/LogDrains.constants'
import SavedQueriesItem from '@/components/interfaces/Settings/Logs/Logs.SavedQueriesItem'
import { LogsSidebarItem } from '@/components/interfaces/Settings/Logs/SidebarV2/SidebarItem'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import { UnifiedLogsBanner } from '@/components/interfaces/UnifiedLogs/UnifiedLogsBanner'
import { useContentQuery } from '@/data/content/content-query'
import { useReplicationSourcesQuery } from '@/data/replication/sources-query'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
@@ -241,32 +241,13 @@ export function LogsSidebarMenuV2() {
/>
)}
{isUnifiedLogsEligible && (
<FeaturePreviewSidebarPanel
className="mx-4 mt-4"
title="Introducing unified logs"
description="A unified view across all services with improved filtering and real-time updates."
illustration={<Badge variant="success">New</Badge>}
actions={
<>
<Button
size="tiny"
type="default"
onClick={() => {
enableUnifiedLogs()
router.push(`/project/${ref}/logs`)
}}
>
Enable preview
</Button>
<ButtonTooltip
type="default"
className="px-1.5"
icon={<CircleHelpIcon />}
onClick={() => selectFeaturePreview('supabase-ui-preview-unified-logs')}
tooltip={{ content: { side: 'bottom', text: 'More information' } }}
/>
</>
}
<UnifiedLogsBanner
variant="promo"
onEnable={() => {
enableUnifiedLogs()
router.push(`/project/${ref}/logs`)
}}
onMoreInfo={() => selectFeaturePreview('supabase-ui-preview-unified-logs')}
/>
)}
@@ -1,10 +1,9 @@
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useParams } from 'common'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React, { useEffect, type ReactNode } from 'react'
import { Button, cn, ResizablePanel, usePanelRef } from 'ui'
import { Badge, Button, cn, ResizablePanel, usePanelRef } from 'ui'
import { FeaturePreviewBadge } from '../FeaturePreviewBadge'
import { FeaturePreviewSidebarPanel } from '../FeaturePreviewSidebarPanel'
import { DateRangeDisabled } from './DataTable.types'
import { DataTableFilterControls } from './DataTableFilters/DataTableFilterControls'
@@ -12,6 +11,7 @@ import { DataTableResetButton } from './DataTableResetButton'
import { useDataTable } from './providers/DataTableProvider'
import { useUnifiedLogsPreview } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { LOG_DRAIN_TYPES } from '@/components/interfaces/LogDrains/LogDrains.constants'
import { UnifiedLogsBanner } from '@/components/interfaces/UnifiedLogs/UnifiedLogsBanner'
interface FilterSideBarProps {
isFilterBarOpen: boolean
@@ -30,11 +30,7 @@ export function FilterSideBar({
const { ref } = useParams()
const { table } = useDataTable()
const {
disable: disableUnifiedLogs,
isEligible: isUnifiedLogsEligible,
isEnabled: isUnifiedLogsEnabled,
} = useUnifiedLogsPreview()
const { disable: disableUnifiedLogs, isEligible: isUnifiedLogsEligible } = useUnifiedLogsPreview()
const handleGoBackToOldLogs = () => {
disableUnifiedLogs()
@@ -70,29 +66,19 @@ export function FilterSideBar({
>
<div className="border-b border-border px-4 md:top-0">
<div className="flex h-[48px] items-center justify-between gap-3">
<p className="text-foreground text-lg">Logs</p>
<div className="flex items-center gap-2">
<p className="text-foreground text-lg">Logs</p>
{isUnifiedLogsEligible && <Badge variant="default">Beta</Badge>}
</div>
{table.getState().columnFilters.length ? <DataTableResetButton /> : null}
</div>
</div>
{isUnifiedLogsEligible && (
<UnifiedLogsBanner variant="utility" onSwitchBack={handleGoBackToOldLogs} />
)}
<div className="flex-1 p-2 sm:overflow-y-scroll">
{isUnifiedLogsEligible && (
<FeaturePreviewSidebarPanel
className="mx-2 mt-2 mb-3"
title="Go back to old logs"
description="Use the traditional interface"
illustration={
isUnifiedLogsEnabled ? (
<FeaturePreviewBadge featureKey={LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS} />
) : undefined
}
actions={
<Button type="default" size="tiny" onClick={handleGoBackToOldLogs}>
Switch back
</Button>
}
/>
)}
<DataTableFilterControls dateRangeDisabled={dateRangeDisabled} />
{afterFilters}
<FeaturePreviewSidebarPanel
+1 -1
View File
@@ -142,7 +142,7 @@ pnpm run e2e -- --ui
Example:
```ts
await expect(page.getByRole('heading', { name: 'Logs & Analytics' }), {
await expect(page.getByRole('heading', { name: 'Logs', exact: true }), {
message: 'Logs heading should be visible',
}).toBeVisible()
```
+4 -2
View File
@@ -1,6 +1,8 @@
import { expect } from '@playwright/test'
import { test } from '../utils/test.js'
import { toUrl } from '../utils/to-url.js'
const LOGS_PAGES = [
{ label: 'API Gateway', route: 'edge-logs' },
{ label: 'Postgres', route: 'postgres-logs' },
@@ -48,8 +50,8 @@ test.describe('Logs', () => {
*/
await page.goto(toUrl(`/project/${ref}/logs/${logPage.route}`))
await expect(page.getByRole('heading', { name: 'Logs & Analytics' }), {
message: 'Logs & Analytics heading should be visible',
await expect(page.getByRole('heading', { name: 'Logs', exact: true }), {
message: 'Logs heading should be visible',
}).toBeVisible()
/**