Skip to content

Commit 41a5c39

Browse files
Feat/logs preview (supabase#37230)
* Add unified logs feature preview and toggle controls Introduces a unified logs feature preview, allowing users to enable or disable a new logs interface with enhanced filtering and real-time updates. Adds context hooks, UI components (UnifiedLogsPreview, LogsSwitchBox), and conditional logic to display the new interface or revert to the old one. Updates routing and feature preview modal to support the unified logs preview, and extends local storage keys for feature flag management. * Refactor log UI components and remove LogsSwitchBox Replaced custom SVG icons with Lucide React icons in LogsSidebarMenuV2 and FilterSideBar for consistency and maintainability. Removed the unused LogsSwitchBox component and cleaned up related imports in FeaturePreviewModal. * Refactor sidebar panels to use FeaturePreviewSidebarPanel Introduces a reusable FeaturePreviewSidebarPanel component and replaces InnerSideBarEmptyPanel usages in LogsSidebarMenuV2 and FilterSideBar. Removes unified logs feature flag logic from Sidebar and NavigationBar.utils, simplifying route generation and UI logic. * Refine sidebar panel styles and remove unused icon Removed the unused Sparkles icon import from LogsSidebarMenuV2. Updated FeaturePreviewSidebarPanel to use 'bg-muted/10' for background, simplified class names for text alignment, and cleaned up redundant text-left classes for improved styling consistency. * Update FeaturePreviewModal.tsx * Update FeaturePreview.constants.tsx * Clean up * Final clean up --------- Co-authored-by: Joshen Lim <[email protected]>
1 parent 2835e88 commit 41a5c39

File tree

14 files changed

+234
-76
lines changed

14 files changed

+234
-76
lines changed

apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { LOCAL_STORAGE_KEYS } from 'common'
22

33
export const FEATURE_PREVIEWS = [
4+
{
5+
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS,
6+
name: 'New Logs Interface',
7+
discussionsUrl: 'https://github.com/orgs/supabase/discussions/37234',
8+
isNew: true,
9+
isPlatformOnly: true,
10+
},
411
{
512
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0,
613
name: 'Branching 2.0',

apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ export const useIsInlineEditorEnabled = () => {
9090
return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR]
9191
}
9292

93+
export const useUnifiedLogsPreview = () => {
94+
const { flags, onUpdateFlag } = useFeaturePreviewContext()
95+
const isEnabled = flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]
96+
const enable = () => onUpdateFlag(LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS, true)
97+
const disable = () => onUpdateFlag(LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS, false)
98+
return { isEnabled, enable, disable }
99+
}
100+
93101
export const useIsRealtimeSettingsEnabled = () => {
94102
const { flags } = useFeaturePreviewContext()
95103
return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_REALTIME_SETTINGS]
@@ -111,6 +119,7 @@ export const useFeaturePreviewModal = () => {
111119
const isRealtimeSettingsEnabled = useIsRealtimeSettingsFFEnabled()
112120
const gitlessBranchingEnabled = useFlag('gitlessBranching')
113121
const advisorRulesEnabled = useFlag('advisorRules')
122+
const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs')
114123

115124
const selectedFeatureKeyFromQuery = featurePreviewModal?.trim() ?? null
116125
const showFeaturePreviewModal = selectedFeatureKeyFromQuery !== null
@@ -125,19 +134,28 @@ export const useFeaturePreviewModal = () => {
125134
return gitlessBranchingEnabled
126135
case 'supabase-ui-advisor-rules':
127136
return advisorRulesEnabled
137+
case 'supabase-ui-preview-unified-logs':
138+
return isUnifiedLogsPreviewAvailable
128139
default:
129140
return true
130141
}
131142
},
132-
[isRealtimeSettingsEnabled, gitlessBranchingEnabled, advisorRulesEnabled]
143+
[
144+
isRealtimeSettingsEnabled,
145+
gitlessBranchingEnabled,
146+
advisorRulesEnabled,
147+
isUnifiedLogsPreviewAvailable,
148+
]
133149
)
134150

135-
const selectedFeatureKey = !selectedFeatureKeyFromQuery
136-
? FEATURE_PREVIEWS.filter((feature) => isFeaturePreviewReleasedToPublic(feature))[0].key
137-
: selectedFeatureKeyFromQuery
151+
const selectedFeatureKey = (
152+
!selectedFeatureKeyFromQuery
153+
? FEATURE_PREVIEWS.filter((feature) => isFeaturePreviewReleasedToPublic(feature))[0].key
154+
: selectedFeatureKeyFromQuery
155+
) as (typeof FEATURE_PREVIEWS)[number]['key']
138156

139157
const selectFeaturePreview = useCallback(
140-
(featureKey: string) => {
158+
(featureKey: (typeof FEATURE_PREVIEWS)[number]['key']) => {
141159
setFeaturePreviewModal(featureKey)
142160
},
143161
[setFeaturePreviewModal]

apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { FEATURE_PREVIEWS } from './FeaturePreview.constants'
1515
import { useFeaturePreviewContext, useFeaturePreviewModal } from './FeaturePreviewContext'
1616
import { InlineEditorPreview } from './InlineEditorPreview'
1717
import { RealtimeSettingsPreview } from './RealtimeSettingsPreview'
18+
import { UnifiedLogsPreview } from './UnifiedLogsPreview'
1819

1920
const FEATURE_PREVIEW_KEY_TO_CONTENT: {
2021
[key: string]: ReactNode
@@ -25,6 +26,7 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: {
2526
[LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR]: <InlineEditorPreview />,
2627
[LOCAL_STORAGE_KEYS.UI_PREVIEW_API_SIDE_PANEL]: <APISidePanelPreview />,
2728
[LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS]: <CLSPreview />,
29+
[LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]: <UnifiedLogsPreview />,
2830
}
2931

3032
const FeaturePreviewModal = () => {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useParams } from 'common'
2+
import { InlineLink } from 'components/ui/InlineLink'
3+
import { BASE_PATH } from 'lib/constants'
4+
import Image from 'next/image'
5+
6+
export const UnifiedLogsPreview = () => {
7+
const { ref } = useParams()
8+
9+
return (
10+
<div className="space-y-2">
11+
<Image
12+
alt="new-logs-preview"
13+
src={`${BASE_PATH}/img/previews/new-logs-preview.png`}
14+
width={1296}
15+
height={900}
16+
className="rounded border mb-4"
17+
/>
18+
<p className="text-foreground-light text-sm mb-4">
19+
Experience our enhanced logs interface with improved filtering, real-time updates, and a
20+
unified view across all your services. Built for better performance and easier debugging.
21+
</p>
22+
<div className="space-y-2 !mt-4">
23+
<p className="text-sm">Enabling this preview will:</p>
24+
<ul className="list-disc pl-6 text-sm text-foreground-light space-y-1">
25+
<li>
26+
Replace the current logs interface on the{' '}
27+
<InlineLink href={`/project/${ref}/logs`}>logs page</InlineLink> with a unified view
28+
</li>
29+
<li>Provide enhanced filtering capabilities and real-time log streaming</li>
30+
<li>Improve performance with optimized data loading and virtualization</li>
31+
<li>Offer a more modern interface with better search and navigation</li>
32+
</ul>
33+
</div>
34+
</div>
35+
)
36+
}

apps/studio/components/interfaces/Sidebar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ const ProjectLinks = () => {
224224
const { securityLints, errorLints } = useLints()
225225

226226
const showWarehouse = useFlag('warehouse')
227-
const showUnifiedLogs = useFlag('unifiedLogs')
228227

229228
const activeRoute = router.pathname.split('/')[3]
230229

@@ -247,7 +246,7 @@ const ProjectLinks = () => {
247246
storage: storageEnabled,
248247
realtime: realtimeEnabled,
249248
})
250-
const otherRoutes = generateOtherRoutes(ref, project, { unifiedLogs: showUnifiedLogs })
249+
const otherRoutes = generateOtherRoutes(ref, project)
251250
const settingsRoutes = generateSettingsRoutes(ref, project)
252251

253252
return (

apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
2-
import { ChevronRight, FilePlus, Plus } from 'lucide-react'
2+
import { ChevronRight, CircleHelpIcon, FilePlus, Plus } from 'lucide-react'
33
import Link from 'next/link'
44
import { useRouter } from 'next/router'
55
import { useState } from 'react'
66

77
import { IS_PLATFORM, useParams } from 'common'
8+
import {
9+
useFeaturePreviewModal,
10+
useUnifiedLogsPreview,
11+
} from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
812
import { CreateWarehouseCollectionModal } from 'components/interfaces/DataWarehouse/CreateWarehouseCollection'
913
import { WarehouseMenuItem } from 'components/interfaces/DataWarehouse/WarehouseMenuItem'
1014
import SavedQueriesItem from 'components/interfaces/Settings/Logs/Logs.SavedQueriesItem'
1115
import { LogsSidebarItem } from 'components/interfaces/Settings/Logs/SidebarV2/SidebarItem'
16+
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
1217
import { useWarehouseCollectionsQuery } from 'data/analytics/warehouse-collections-query'
1318
import { useWarehouseTenantQuery } from 'data/analytics/warehouse-tenant-query'
1419
import { useContentQuery } from 'data/content/content-query'
@@ -17,6 +22,7 @@ import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan'
1722
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
1823
import { useFlag } from 'hooks/ui/useFlag'
1924
import {
25+
Badge,
2026
Button,
2127
Collapsible_Shadcn_,
2228
CollapsibleContent_Shadcn_,
@@ -37,6 +43,7 @@ import {
3743
InnerSideMenuItem,
3844
} from 'ui-patterns/InnerSideMenu'
3945
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
46+
import { FeaturePreviewSidebarPanel } from '../../ui/FeaturePreviewSidebarPanel'
4047

4148
const SupaIcon = ({ className }: { className?: string }) => {
4249
return (
@@ -82,7 +89,11 @@ export function SidebarCollapsible({
8289
export function LogsSidebarMenuV2() {
8390
const router = useRouter()
8491
const { ref } = useParams() as { ref: string }
92+
8593
const warehouseEnabled = useFlag('warehouse')
94+
const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs')
95+
const { selectFeaturePreview } = useFeaturePreviewModal()
96+
const { enable: enableUnifiedLogs } = useUnifiedLogsPreview()
8697

8798
const [searchText, setSearchText] = useState('')
8899
const [createCollectionOpen, setCreateCollectionOpen] = useState(false)
@@ -213,6 +224,36 @@ export function LogsSidebarMenuV2() {
213224

214225
return (
215226
<div className="pb-12 relative">
227+
{isUnifiedLogsPreviewAvailable && (
228+
<FeaturePreviewSidebarPanel
229+
className="mx-4 mt-4"
230+
title="New Logs Interface"
231+
description="Unified view across all services with improved filtering and real-time updates"
232+
illustration={<Badge variant="brand">Feature Preview</Badge>}
233+
actions={
234+
<>
235+
<Button
236+
size="tiny"
237+
type="default"
238+
onClick={() => {
239+
enableUnifiedLogs()
240+
router.push(`/project/${ref}/logs`)
241+
}}
242+
>
243+
Enable preview
244+
</Button>
245+
<ButtonTooltip
246+
type="default"
247+
className="px-1.5"
248+
icon={<CircleHelpIcon />}
249+
onClick={() => selectFeaturePreview('supabase-ui-preview-unified-logs')}
250+
tooltip={{ content: { side: 'bottom', text: 'More information' } }}
251+
/>
252+
</>
253+
}
254+
/>
255+
)}
256+
216257
<div className="flex gap-2 p-4 items-center sticky top-0 bg-background-200 z-[1]">
217258
<InnerSideBarFilters className="w-full p-0 gap-0">
218259
<InnerSideBarFilterSearchInput

apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Blocks, FileText, Lightbulb, List, Logs, Settings } from 'lucide-react'
1+
import { Blocks, FileText, Lightbulb, List, Settings } from 'lucide-react'
22

33
import { ICON_SIZE, ICON_STROKE_WIDTH } from 'components/interfaces/Sidebar'
44
import { generateAuthMenu } from 'components/layouts/AuthLayout/AuthLayout.utils'
@@ -116,16 +116,10 @@ export const generateProductRoutes = (
116116
]
117117
}
118118

119-
export const generateOtherRoutes = (
120-
ref?: string,
121-
project?: Project,
122-
features?: { unifiedLogs?: boolean }
123-
): Route[] => {
119+
export const generateOtherRoutes = (ref?: string, project?: Project, features?: {}): Route[] => {
124120
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
125121
const buildingUrl = `/project/${ref}`
126122

127-
const showUnifiedLogs = features?.unifiedLogs ?? false
128-
129123
return [
130124
{
131125
key: 'advisors',
@@ -149,16 +143,6 @@ export const generateOtherRoutes = (
149143
icon: <List size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
150144
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/logs`),
151145
},
152-
...(showUnifiedLogs
153-
? [
154-
{
155-
key: 'unified-logs',
156-
label: 'Unified Logs',
157-
icon: <Logs size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
158-
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/unified-logs`),
159-
},
160-
]
161-
: []),
162146
{
163147
key: 'api',
164148
label: 'API Docs',

apps/studio/components/layouts/UnifiedLogsLayout/UnifiedLogsLayout.tsx

Lines changed: 0 additions & 9 deletions
This file was deleted.

apps/studio/components/ui/DataTable/FilterSideBar.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { cn, ResizablePanel } from 'ui'
1+
import { useRouter } from 'next/router'
2+
3+
import { useParams } from 'common'
4+
import { useUnifiedLogsPreview } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
5+
import { useFlag } from 'hooks/ui/useFlag'
6+
import { Button, cn, ResizablePanel } from 'ui'
7+
import { FeaturePreviewSidebarPanel } from '../FeaturePreviewSidebarPanel'
28
import { DateRangeDisabled } from './DataTable.types'
39
import { DataTableFilterControls } from './DataTableFilters/DataTableFilterControls'
410
import { DataTableResetButton } from './DataTableResetButton'
@@ -9,8 +15,18 @@ interface FilterSideBarProps {
915
}
1016

1117
export function FilterSideBar({ dateRangeDisabled }: FilterSideBarProps) {
18+
const router = useRouter()
19+
const { ref } = useParams()
1220
const { table } = useDataTable()
1321

22+
const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs')
23+
const { disable: disableUnifiedLogs } = useUnifiedLogsPreview()
24+
25+
const handleGoBackToOldLogs = () => {
26+
disableUnifiedLogs()
27+
router.push(`/project/${ref}/logs/explorer`)
28+
}
29+
1430
return (
1531
<ResizablePanel
1632
order={1}
@@ -24,13 +40,26 @@ export function FilterSideBar({ dateRangeDisabled }: FilterSideBarProps) {
2440
'hidden sm:flex'
2541
)}
2642
>
27-
<div className="border-b border-border px-2 md:top-0">
43+
<div className="border-b border-border px-4 md:top-0">
2844
<div className="flex h-[48px] items-center justify-between gap-3">
29-
<p className="px-2 text-foreground text-lg">Logs</p>
30-
<div>{table.getState().columnFilters.length ? <DataTableResetButton /> : null}</div>
45+
<p className="text-foreground text-lg">Logs</p>
46+
{table.getState().columnFilters.length ? <DataTableResetButton /> : null}
3147
</div>
3248
</div>
49+
3350
<div className="flex-1 p-2 sm:overflow-y-scroll">
51+
{isUnifiedLogsPreviewAvailable && (
52+
<FeaturePreviewSidebarPanel
53+
className="mx-2 mt-2 mb-3"
54+
title="Go back to old logs"
55+
description="Use the traditional interface"
56+
actions={
57+
<Button type="default" size="tiny" onClick={handleGoBackToOldLogs}>
58+
Switch back
59+
</Button>
60+
}
61+
/>
62+
)}
3463
<DataTableFilterControls dateRangeDisabled={dateRangeDisabled} />
3564
</div>
3665
</ResizablePanel>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { ReactNode } from 'react'
2+
import { cn } from 'ui'
3+
4+
interface FeaturePreviewSidebarPanelProps {
5+
title: string
6+
description: string
7+
illustration?: ReactNode
8+
actions?: ReactNode
9+
className?: string
10+
}
11+
12+
export function FeaturePreviewSidebarPanel({
13+
title,
14+
description,
15+
illustration,
16+
actions,
17+
className,
18+
}: FeaturePreviewSidebarPanelProps) {
19+
return (
20+
<div
21+
className={cn(
22+
'rounded-lg border p-4 space-y-3',
23+
'bg-muted/10 border-border/50',
24+
// Force left alignment and override any centering
25+
'text-left [&_*]:text-left [&_div]:items-start',
26+
className
27+
)}
28+
>
29+
{illustration && <div className="flex justify-start items-start">{illustration}</div>}
30+
31+
<div className="space-y-1">
32+
<h3 className="font-medium text-sm text-foreground">{title}</h3>
33+
<p className="text-xs text-foreground-light">{description}</p>
34+
</div>
35+
36+
{actions && <div className="flex justify-start items-start gap-x-2">{actions}</div>}
37+
</div>
38+
)
39+
}

0 commit comments

Comments
 (0)