feat(studio): rename reports to observability (#40317)

* feat: rename reports to observability on nav sidebar

* fix: observability route

* feat: observability navigation and redirects

* fix: first time load redirects

* feat: change auth overview reports link

* feat: clean up of other links to observability sections

* fix: query perf redirects

* chore: find exhaustive deps and disable for time being

* fix: charis suggested fixes

* fix: forgotten suggested fix

* chore: delete old reports views files

* fix: paths in reports tests

* fix: url sync useEffect should trigger only once

* feat: reorganise observability sidebar

* fix: data api not being selected

* fix: wrong layout for realitime

* fix: custom reports reloading

---------

Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-11-18 17:54:05 +00:00
1 parent c83d7255a4
commit 4a9def58c1
26 files changed
+650 -49

No files matched your search

@@ -12,7 +12,7 @@ import {
TooltipTrigger,
} from 'ui'
import Link from 'next/link'
import { ChevronRight, ExternalLink, HelpCircle } from 'lucide-react'
import { ChevronRight, ExternalLink, HelpCircle, Telescope } from 'lucide-react'
import { Reports } from 'icons'
import {
ScaffoldSection,
@@ -231,11 +231,11 @@ export const OverviewMetrics = ({ metrics, isLoading, error }: OverviewMetricsPr
<div className="flex items-center justify-between mb-4">
<ScaffoldSectionTitle>Usage</ScaffoldSectionTitle>
<Link
href={`/project/${ref}/reports/auth?its=${startDate}&ite=${endDate}&isHelper=true&helperText=Last+24+hours`}
href={`/project/${ref}/observability/auth?its=${startDate}&ite=${endDate}&isHelper=true&helperText=Last+24+hours`}
className="text-sm text-link inline-flex items-center gap-x-1.5"
>
<Reports size={14} />
<span>View all reports</span>
<Telescope size={14} />
<span>Go to observability</span>
<ChevronRight size={14} />
</Link>
</div>
@@ -114,7 +114,7 @@ const DatabaseSizeUsage = ({
</span>
<Button asChild type="default" size={'tiny'}>
<Link
href={`/project/${project.ref}/reports/database#database-size-report`}
href={`/project/${project.ref}/observability/database#database-size-report`}
>
Manage Database Size
</Link>
@@ -39,7 +39,7 @@ export const CreateReportModal = ({ visible, onCancel, afterSubmit }: CreateRepo
onSuccess: (_, vars) => {
toast.success('Successfully created new report')
const newReportId = vars.payload.id
router.push(`/project/${ref}/reports/${newReportId}${preservedQueryParams}`)
router.push(`/project/${ref}/observability/${newReportId}${preservedQueryParams}`)
afterSubmit()
},
onError: (error) => {
@@ -374,7 +374,7 @@ export const ReplicaNode = ({ data }: NodeProps<ReplicaNodeData>) => {
className="gap-x-2"
disabled={status !== REPLICA_STATUS.ACTIVE_HEALTHY}
>
<Link href={`/project/${ref}/reports/database?db=${id}&chart=replication-lag`}>
<Link href={`/project/${ref}/observability/database?db=${id}&chart=replication-lag`}>
View replication lag
</Link>
</DropdownMenuItem>
@@ -315,7 +315,7 @@ const MapView = ({
disabled={database.status !== REPLICA_STATUS.ACTIVE_HEALTHY}
>
<Link
href={`/project/${ref}/reports/database?db=${database.identifier}&chart=replication-lag`}
href={`/project/${ref}/observability/database?db=${database.identifier}&chart=replication-lag`}
>
View replication lag
</Link>
@@ -28,7 +28,7 @@ export const generateAdvisorsMenu = (
{
name: 'Query Performance',
key: 'query-performance',
url: `/project/${ref}/reports/query-performance`,
url: `/project/${ref}/observability/query-performance`,
items: [],
rightIcon: <ArrowUpRight size={14} strokeWidth={1.5} className="h-4 w-4" />,
},
@@ -176,7 +176,7 @@ export const generateDatabaseMenu = (
{
name: 'Query Performance',
key: 'query-performance',
url: `/project/${ref}/reports/query-performance`,
url: `/project/${ref}/observability/query-performance`,
rightIcon: <ArrowUpRight strokeWidth={1} className="h-4 w-4" />,
items: [],
},
@@ -0,0 +1,92 @@
import { useMemo } from 'react'
import { useParams } from 'common'
import { COMMAND_MENU_SECTIONS } from 'components/interfaces/App/CommandMenu/CommandMenu.utils'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import type { CommandOptions, ICommand } from 'ui-patterns/CommandMenu'
import { orderSectionFirst, useQuery, useRegisterCommands } from 'ui-patterns/CommandMenu'
const QUERY_PERFORMANCE_COMMAND_ID = 'nav-reports-query-performance'
export function useReportsGotoCommands(options?: CommandOptions) {
let { ref } = useParams()
ref ||= '_'
const { reportsAll } = useIsFeatureEnabled(['reports:all'])
const commandQuery = useQuery()?.toLowerCase() ?? ''
const prioritizeQueryPerformance = commandQuery.includes('query')
const orderQueryPerformanceCommand = useMemo(() => {
if (!prioritizeQueryPerformance) return undefined
return (existingCommands: ICommand[], incomingCommands: ICommand[]) => {
const filteredExisting = existingCommands.filter(
(command) => command.id !== QUERY_PERFORMANCE_COMMAND_ID
)
return [...incomingCommands, ...filteredExisting]
}
}, [prioritizeQueryPerformance])
const orderNavigateSection = useMemo<CommandOptions['orderSection'] | undefined>(() => {
return prioritizeQueryPerformance ? orderSectionFirst : options?.orderSection
}, [options?.orderSection, prioritizeQueryPerformance])
useRegisterCommands(
COMMAND_MENU_SECTIONS.NAVIGATE,
reportsAll
? [
{
id: 'nav-reports',
name: 'Reports',
route: `/project/${ref}/observability`,
defaultHidden: true,
},
{
id: 'nav-reports-api',
name: 'API Reports',
route: `/project/${ref}/observability/api-overview`,
defaultHidden: true,
},
{
id: 'nav-reports-storage',
name: 'Storage Reports',
route: `/project/${ref}/observability/storage`,
defaultHidden: true,
},
{
id: 'nav-reports-database',
name: 'Database Reports',
route: `/project/${ref}/observability/database`,
defaultHidden: true,
},
]
: [],
{
...options,
orderSection: orderNavigateSection,
deps: [ref, orderNavigateSection, ...(options?.deps ?? [])],
}
)
useRegisterCommands(
COMMAND_MENU_SECTIONS.NAVIGATE,
reportsAll
? [
{
id: QUERY_PERFORMANCE_COMMAND_ID,
name: 'Query Performance Reports',
route: `/project/${ref}/observability/query-performance`,
defaultHidden: true,
},
]
: [],
{
...options,
orderCommands: orderQueryPerformanceCommand ?? options?.orderCommands,
orderSection: orderNavigateSection,
deps: [ref, orderQueryPerformanceCommand, orderNavigateSection, ...(options?.deps ?? [])],
}
)
}
@@ -0,0 +1,34 @@
import { PropsWithChildren } from 'react'
import { useParams } from 'common'
import { UnknownInterface } from 'components/ui/UnknownInterface'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { withAuth } from 'hooks/misc/withAuth'
import { ProjectLayout } from '../ProjectLayout'
import ObservabilityMenu from './ObservabilityMenu'
interface ObservabilityLayoutProps {
title?: string
}
const ObservabilityLayout = ({ title, children }: PropsWithChildren<ObservabilityLayoutProps>) => {
const { ref } = useParams()
const { reportsAll } = useIsFeatureEnabled(['reports:all'])
if (reportsAll) {
return (
<ProjectLayout
title={title}
product="Observability"
productMenu={<ObservabilityMenu />}
isBlocking={false}
>
{children}
</ProjectLayout>
)
} else {
return <UnknownInterface urlBack={`/project/${ref}`} />
}
}
export default withAuth(ObservabilityLayout)
@@ -0,0 +1,348 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Plus } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useMemo, useState } from 'react'
import { toast } from 'sonner'
import { useFlag, useParams } from 'common'
import { CreateReportModal } from 'components/interfaces/Reports/CreateReportModal'
import { UpdateCustomReportModal } from 'components/interfaces/Reports/UpdateModal'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useContentDeleteMutation } from 'data/content/content-delete-mutation'
import { Content, useContentQuery } from 'data/content/content-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useProfile } from 'lib/profile'
import { Menu, cn } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { ObservabilityMenuItem } from './ObservabilityMenuItem'
import { InnerSideBarEmptyPanel } from 'ui-patterns'
const ObservabilityMenu = () => {
const router = useRouter()
const { profile } = useProfile()
const { ref, id } = useParams()
const pageKey = (id || router.pathname.split('/')[4]) as string
const authEnabled = useFlag('authreportv2')
const edgeFnEnabled = useFlag('edgefunctionreport')
const realtimeEnabled = useFlag('realtimeReport')
const storageReportEnabled = useFlag('storagereport')
const postgrestReportEnabled = useFlag('postgrestreport')
// b/c fly doesn't support storage
const storageSupported = useIsFeatureEnabled('project_storage:all')
const storageEnabled = storageReportEnabled && storageSupported
const { can: canCreateCustomReport } = useAsyncCheckPermissions(
PermissionAction.CREATE,
'user_content',
{
resource: { type: 'report', owner_id: profile?.id },
subject: { id: profile?.id },
}
)
// Preserve date range query parameters when navigating
const preservedQueryParams = useMemo(() => {
const { its, ite, isHelper, helperText } = router.query
const params = new URLSearchParams()
if (its && typeof its === 'string') params.set('its', its)
if (ite && typeof ite === 'string') params.set('ite', ite)
if (isHelper && typeof isHelper === 'string') params.set('isHelper', isHelper)
if (helperText && typeof helperText === 'string') params.set('helperText', helperText)
const queryString = params.toString()
return queryString ? `?${queryString}` : ''
}, [router.query])
const { data: content, isLoading } = useContentQuery({
projectRef: ref,
type: 'report',
})
const { mutate: deleteReport, isLoading: isDeleting } = useContentDeleteMutation({
onSuccess: () => {
setDeleteModalOpen(false)
toast.success('Successfully deleted report')
router.push(`/project/${ref}/observability`)
},
onError: (error) => {
toast.error(`Failed to delete report: ${error.message}`)
},
})
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
const [showNewReportModal, setShowNewReportModal] = useState(false)
const [selectedReportToDelete, setSelectedReportToDelete] = useState<Content>()
const [selectedReportToUpdate, setSelectedReportToUpdate] = useState<Content>()
const onConfirmDeleteReport = () => {
if (ref === undefined) return console.error('Project ref is required')
if (selectedReportToDelete?.id === undefined) return console.error('Report ID is required')
deleteReport({ projectRef: ref, ids: [selectedReportToDelete.id] })
}
function getReportMenuItems() {
if (!content) return []
const reports = content?.content.filter((c) => c.type === 'report')
const sortedReports = reports?.sort((a, b) => {
if (a.name < b.name) {
return -1
}
if (a.name > b.name) {
return 1
}
return 0
})
const reportMenuItems = sortedReports.map((r, idx) => ({
id: r.id,
name: r.name,
description: r.description || '',
key: r.id || idx + '-report',
url: `/project/${ref}/observability/${r.id}${preservedQueryParams}`,
hasDropdownActions: true,
report: r,
}))
return reportMenuItems
}
const reportMenuItems = getReportMenuItems()
const menuItems = [
{
title: 'Performance Reports',
key: 'performance-reports',
items: [
{
name: 'API Gateway',
key: 'api-overview',
url: `/project/${ref}/observability/api-overview${preservedQueryParams}`,
},
{
name: 'Query Performance',
key: 'query-performance',
url: `/project/${ref}/observability/query-performance${preservedQueryParams}`,
},
...(postgrestReportEnabled
? [
{
name: 'Data API',
key: 'postgrest',
url: `/project/${ref}/observability/postgrest${preservedQueryParams}`,
},
]
: []),
],
},
{
title: 'Product Reports',
key: 'product-reports',
items: [
...(authEnabled
? [
{
name: 'Auth',
key: 'auth',
url: `/project/${ref}/observability/auth${preservedQueryParams}`,
},
]
: []),
{
name: 'Database',
key: 'database',
url: `/project/${ref}/observability/database${preservedQueryParams}`,
},
...(edgeFnEnabled
? [
{
name: 'Edge Functions',
key: 'edge-functions',
url: `/project/${ref}/observability/edge-functions${preservedQueryParams}`,
},
]
: []),
...(realtimeEnabled
? [
{
name: 'Realtime',
key: 'realtime',
url: `/project/${ref}/observability/realtime${preservedQueryParams}`,
},
]
: []),
...(storageEnabled
? [
{
name: 'Storage',
key: 'storage',
url: `/project/${ref}/observability/storage${preservedQueryParams}`,
},
]
: []),
],
},
]
return (
<Menu type="pills" className="mt-6">
{isLoading ? (
<div className="px-5 my-4 space-y-2">
<ShimmeringLoader />
<ShimmeringLoader className="w-3/4" />
<ShimmeringLoader className="w-1/2" />
</div>
) : (
<div className="flex flex-col gap-y-6">
<div className="mx-2">
<Menu.Group
title={
<span className="flex w-full items-center justify-between relative h-6">
<span className="uppercase font-mono">Custom Reports</span>
{reportMenuItems.length > 0 && (
<ButtonTooltip
type="default"
size="tiny"
icon={<Plus />}
disabled={!canCreateCustomReport}
className="flex items-center justify-center h-6 w-6 absolute top-0 -right-1"
onClick={() => {
setShowNewReportModal(true)
}}
tooltip={{
content: {
side: 'bottom',
text: !canCreateCustomReport
? 'You need additional permissions to create custom reports'
: undefined,
},
}}
/>
)}
</span>
}
/>
{reportMenuItems.length === 0 ? (
<div className="px-2">
<InnerSideBarEmptyPanel
title="No custom reports yet"
description="Create and save custom reports to track your project metrics"
actions={
<ButtonTooltip
type="default"
icon={<Plus />}
disabled={!canCreateCustomReport}
onClick={() => {
setShowNewReportModal(true)
}}
tooltip={{
content: {
side: 'bottom',
text: !canCreateCustomReport
? 'You need additional permissions to create custom reports'
: undefined,
},
}}
>
New custom report
</ButtonTooltip>
}
/>
</div>
) : (
<>
{reportMenuItems.map((item) => (
<ObservabilityMenuItem
key={item.id}
item={item as any}
pageKey={pageKey}
onSelectEdit={() => {
setSelectedReportToUpdate(item.report)
}}
onSelectDelete={() => {
setSelectedReportToDelete(item.report)
setDeleteModalOpen(true)
}}
/>
))}
</>
)}
</div>
{menuItems.map((item, idx) => (
<>
<div className="h-px w-full bg-border-overlay first:hidden" />
<div key={item.key + '-menu-group'}>
{item.items ? (
<div className="px-2">
<Menu.Group title={<span className="uppercase font-mono">{item.title}</span>} />
<div key={item.key} className="flex flex-col">
{item.items.map((subItem) => (
<li
key={subItem.key}
className={cn(
'pr-2 mt-1 text-foreground-light group-hover:text-foreground/80 text-sm',
'flex items-center justify-between rounded-md group relative',
subItem.key === pageKey
? 'bg-surface-300 text-foreground'
: 'hover:text-foreground'
)}
>
<Link
href={subItem.url}
className="flex-grow h-7 flex justify-between items-center pl-3"
>
<span>{subItem.name}</span>
</Link>
</li>
))}
</div>
</div>
) : null}
</div>
</>
))}
<UpdateCustomReportModal
onCancel={() => setSelectedReportToUpdate(undefined)}
selectedReport={selectedReportToUpdate}
initialValues={{
name: selectedReportToUpdate?.name || '',
description: selectedReportToUpdate?.description || '',
}}
/>
<ConfirmationModal
title="Delete custom report"
confirmLabel="Delete report"
confirmLabelLoading="Deleting report"
size="medium"
loading={isDeleting}
visible={deleteModalOpen}
onCancel={() => setDeleteModalOpen(false)}
onConfirm={onConfirmDeleteReport}
>
<div className="text-sm text-foreground-light grid gap-4">
<div className="grid gap-1">
<p>Are you sure you want to delete '{selectedReportToDelete?.name}'?</p>
</div>
</div>
</ConfirmationModal>
<CreateReportModal
visible={showNewReportModal}
onCancel={() => setShowNewReportModal(false)}
afterSubmit={() => setShowNewReportModal(false)}
/>
</div>
)}
</Menu>
)
}
export default ObservabilityMenu
@@ -0,0 +1,111 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Edit2, Trash, MoreVertical, MoreHorizontal } from 'lucide-react'
import Link from 'next/link'
import { ContentBase } from 'data/content/content-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useProfile } from 'lib/profile'
import type { Dashboards } from 'types'
import {
Button,
cn,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from 'ui'
interface ReportMenuItemProps {
item: {
id?: string
name: string
description: string
key: string
url: string
hasDropdownActions: boolean
report: ContentBase & {
type: 'report'
content: Dashboards.Content
}
}
pageKey: string
onSelectEdit: () => void
onSelectDelete: () => void
}
export const ObservabilityMenuItem = ({
item,
pageKey,
onSelectEdit,
onSelectDelete,
}: ReportMenuItemProps) => {
const { profile } = useProfile()
const { can: canUpdateCustomReport } = useAsyncCheckPermissions(
PermissionAction.UPDATE,
'user_content',
{
resource: {
type: 'report',
visibility: item.report.visibility,
owner_id: item.report.owner_id,
},
subject: { id: profile?.id },
}
)
return (
<Link
className={cn(
'pr-2 h-7 pl-3 mt-1 text-foreground-light group-hover:text-foreground/80 text-sm',
'flex items-center justify-between rounded-md group relative',
item.key === pageKey ? 'bg-surface-300 text-foreground' : 'hover:text-foreground'
)}
key={item.key + '-menukey'}
href={item.url}
>
<div>{item.name}</div>
{canUpdateCustomReport && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="text"
className="px-1 opacity-50 hover:opacity-100"
icon={<MoreVertical size={12} strokeWidth={2} />}
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
}}
/>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-32 *:gap-x-2">
<DropdownMenuItem
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
if (!item.id) return
onSelectEdit()
}}
>
<Edit2 size={12} />
<div>Rename report</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
if (!item.id) return
onSelectDelete()
}}
>
<Trash size={12} />
<div>Delete report</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
</Link>
)
}
@@ -1,4 +1,4 @@
import { Blocks, FileText, Lightbulb, List, Settings } from 'lucide-react'
import { Blocks, FileText, Lightbulb, List, Settings, Telescope } from 'lucide-react'
import { ICON_SIZE, ICON_STROKE_WIDTH } from 'components/interfaces/Sidebar'
import { generateAuthMenu } from 'components/layouts/AuthLayout/AuthLayout.utils'
@@ -152,10 +152,10 @@ export const generateOtherRoutes = (
...(IS_PLATFORM && reportsEnabled
? [
{
key: 'reports',
label: 'Reports',
icon: <Reports size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/reports`),
key: 'observability',
label: 'Observability',
icon: <Telescope size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/observability`),
},
]
: []),
@@ -16,7 +16,7 @@ import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { useChartHighlight } from './useChartHighlight'
import dayjs from 'dayjs'
import type { UpdateDateRange } from 'pages/project/[ref]/reports/database'
import type { UpdateDateRange } from 'pages/project/[ref]/observability/database'
import type { ChartData } from './Charts.types'
import { MultiAttribute } from './ComposedChart.utils'
+13 -3
View File
@@ -322,20 +322,30 @@ const nextConfig = {
source: '/project/:ref/reports/linter',
destination: '/project/:ref/database/linter',
},
{
permanent: true,
source: '/project/:ref/reports',
destination: '/project/:ref/observability',
},
{
permanent: true,
source: '/project/:ref/reports/:path*',
destination: '/project/:ref/observability/:path*',
},
{
permanent: true,
source: '/project/:ref/query-performance',
destination: '/project/:ref/reports/query-performance',
destination: '/project/:ref/observability/query-performance',
},
{
permanent: true,
source: '/project/:ref/advisors/query-performance',
destination: '/project/:ref/reports/query-performance',
destination: '/project/:ref/observability/query-performance',
},
{
permanent: true,
source: '/project/:ref/database/query-performance',
destination: '/project/:ref/reports/query-performance',
destination: '/project/:ref/observability/query-performance',
},
{
permanent: true,
@@ -1,7 +1,7 @@
import ReportPadding from 'components/interfaces/Reports/ReportPadding'
import Reports from 'components/interfaces/Reports/Reports'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import type { NextPageWithLayout } from 'types'
const PageLayout: NextPageWithLayout = () => (
@@ -14,7 +14,7 @@ const PageLayout: NextPageWithLayout = () => (
PageLayout.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout>{page}</ReportsLayout>
<ObservabilityLayout>{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -14,7 +14,7 @@ import {
import { DatePickerValue } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import { useApiReport } from 'data/reports/api-report-query'
import { useReportDateRange } from 'hooks/misc/useReportDateRange'
import type { NextPageWithLayout } from 'types'
@@ -134,7 +134,7 @@ export const ApiReport: NextPageWithLayout = () => {
ApiReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout>{page}</ReportsLayout>
<ObservabilityLayout>{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -11,7 +11,7 @@ import { ReportChartV2 } from 'components/interfaces/Reports/v2/ReportChartV2'
import { ReportSectionHeader } from 'components/interfaces/Reports/v2/ReportSectionHeader'
import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import ReportFilterBar from 'components/interfaces/Reports/ReportFilterBar'
@@ -49,7 +49,7 @@ const AuthReport: NextPageWithLayout = () => {
AuthReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout title="Auth">{page}</ReportsLayout>
<ObservabilityLayout title="Auth">{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { ArrowRight, ExternalLink, RefreshCw } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
@@ -18,7 +18,7 @@ import DiskSizeConfigurationModal from 'components/interfaces/Settings/Database/
import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import Table from 'components/to-be-cleaned/Table'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import ChartHandler from 'components/ui/Charts/ChartHandler'
@@ -56,7 +56,7 @@ const DatabaseReport: NextPageWithLayout = () => {
DatabaseReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout title="Database">{page}</ReportsLayout>
<ObservabilityLayout title="Database">{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -173,8 +173,11 @@ const DatabaseUsage = () => {
setTimeout(() => setIsRefreshing(false), 1000)
}
// [Joshen] Empty dependency array as we only want this running once
const stateSyncedFromUrlRef = useRef(false)
useEffect(() => {
if (stateSyncedFromUrlRef.current) return
stateSyncedFromUrlRef.current = true
if (db !== undefined) {
setTimeout(() => {
// [Joshen] Adding a timeout here to support navigation from settings to reports
@@ -18,7 +18,7 @@ import {
} from 'components/interfaces/Reports/v2/ReportsSelectFilter'
import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useChartHoverState } from 'components/ui/Charts/useChartHoverState'
@@ -45,7 +45,7 @@ const EdgeFunctionsReportV2: NextPageWithLayout = () => {
EdgeFunctionsReportV2.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout title="Edge Functions">{page}</ReportsLayout>
<ObservabilityLayout title="Edge Functions">{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -5,7 +5,7 @@ import { useState } from 'react'
import { useParams } from 'common'
import { CreateReportModal } from 'components/interfaces/Reports/CreateReportModal'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import { useContentQuery } from 'data/content/content-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
@@ -30,8 +30,8 @@ export const UserReportPage: NextPageWithLayout = () => {
const reports = data.content
.filter((x) => x.type === 'report')
.sort((a, b) => a.name.localeCompare(b.name))
if (reports.length >= 1) router.push(`/project/${ref}/reports/${reports[0].id}`)
if (reports.length === 0) router.push(`/project/${ref}/reports/api-overview`)
if (reports.length >= 1) router.push(`/project/${ref}/observability/${reports[0].id}`)
if (reports.length === 0) router.push(`/project/${ref}/observability/api-overview`)
},
}
)
@@ -52,7 +52,7 @@ export const UserReportPage: NextPageWithLayout = () => {
) : (
<>
<ProductEmptyState
title="Reports"
title="Observability"
ctaButtonLabel="New custom report"
onClickCta={() => {
setShowCreateReportModal(true)
@@ -81,7 +81,7 @@ export const UserReportPage: NextPageWithLayout = () => {
UserReportPage.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout>{page}</ReportsLayout>
<ObservabilityLayout>{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -1,7 +1,7 @@
import { useParams } from 'common'
import dayjs from 'dayjs'
import { ArrowRight, RefreshCw } from 'lucide-react'
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import ReportHeader from 'components/interfaces/Reports/ReportHeader'
import ReportPadding from 'components/interfaces/Reports/ReportPadding'
@@ -13,7 +13,7 @@ import {
} from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useReportDateRange } from 'hooks/misc/useReportDateRange'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
@@ -33,7 +33,7 @@ const PostgRESTReport: NextPageWithLayout = () => {
PostgRESTReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout title="PostgREST">{page}</ReportsLayout>
<ObservabilityLayout title="PostgREST">{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -70,8 +70,11 @@ const PostgrestReport = () => {
const state = useDatabaseSelectorStateSnapshot()
// [Joshen] Empty dependency array as we only want this running once
const hasStateSyncedFromUrlRef = useRef(false)
useEffect(() => {
if (hasStateSyncedFromUrlRef.current) return
hasStateSyncedFromUrlRef.current = true
if (db !== undefined) {
setTimeout(() => {
// [Joshen] Adding a timeout here to support navigation from settings to reports
@@ -86,7 +89,7 @@ const PostgrestReport = () => {
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, 200)
}
}, [])
}, [state, db, chart])
const handleDatePickerChange = (values: DatePickerValue) => {
handleDatePickerChangeFromHook(values)
@@ -14,7 +14,7 @@ import { Presets } from 'components/interfaces/Reports/Reports.types'
import { queriesFactory } from 'components/interfaces/Reports/Reports.utils'
import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
@@ -102,7 +102,7 @@ const QueryPerformanceReport: NextPageWithLayout = () => {
QueryPerformanceReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout title="Query performance">{page}</ReportsLayout>
<ObservabilityLayout title="Query performance">{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -10,7 +10,7 @@ import ReportPadding from 'components/interfaces/Reports/ReportPadding'
import { ReportChartV2 } from 'components/interfaces/Reports/v2/ReportChartV2'
import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
@@ -34,7 +34,7 @@ const RealtimeReport: NextPageWithLayout = () => {
RealtimeReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout title="Realtime">{page}</ReportsLayout>
<ObservabilityLayout title="Realtime">{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -24,7 +24,7 @@ import {
} from 'components/interfaces/Settings/Logs/Logs.DatePickers'
import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
import ObservabilityLayout from 'components/layouts/ObservabilityLayout/ObservabilityLayout'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useStorageReport } from 'data/reports/storage-report-query'
import { useReportDateRange } from 'hooks/misc/useReportDateRange'
@@ -186,7 +186,7 @@ export const StorageReport: NextPageWithLayout = () => {
StorageReport.getLayout = (page) => (
<DefaultLayout>
<ReportsLayout>{page}</ReportsLayout>
<ObservabilityLayout>{page}</ObservabilityLayout>
</DefaultLayout>
)
@@ -1,7 +1,7 @@
import { screen } from '@testing-library/react'
import { test } from 'vitest'
import { ApiReport } from 'pages/project/[ref]/reports/api-overview'
import { ApiReport } from 'pages/project/[ref]/observability/api-overview'
import { customRender as render } from 'tests/lib/custom-render'
test(`Render static elements`, async () => {
@@ -1,7 +1,7 @@
import { screen } from '@testing-library/react'
import { test } from 'vitest'
import { StorageReport } from 'pages/project/[ref]/reports/storage'
import { StorageReport } from 'pages/project/[ref]/observability/storage'
import { render } from 'tests/helpers'
// [Joshen] Mock data for ApiReport is in __mocks__/hooks/useStorageReport