mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(studio) Link observability pages to relevant docs (#47351)
Closes DOCS-488 <img width="1266" height="353" alt="Screenshot 2026-06-26 at 11 02 57 AM" src="https://github.com/user-attachments/assets/67b5d47b-249e-4e53-9230-2bbcb7f037b7" /> ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## Problem We have helpful documentation that delves into each observability metric, but it is not easily findable in the moment it is needed while viewing the dashboards. ## Solution Solution includes: - Add a docs link in Studio in every relevant place with the `DocsButton` component - Add aria-hidden on the `DocsButton` icon - An added `constants.ts` to see all of the docs links in one place - A contextual aria-label for the docs so that screenreader users know where they're going | Page | Docs link | |------|-----------| | Overview | `/guides/telemetry/reports` | | Query Performance / Query Insights | `/guides/platform/performance#examining-query-performance` | | API Gateway | `/guides/telemetry/reports#api-gateway` | | Database | `/guides/telemetry/reports#database` | | Data API | `/guides/telemetry/reports#postgrest` | | Auth | `/guides/telemetry/reports#auth` | | Edge Functions | `/guides/telemetry/reports#edge-functions` | | Storage | `/guides/telemetry/reports#storage` | | Realtime | `/guides/realtime/reports` | | Custom reports | `/guides/telemetry/reports#using-reports` | Query Performance and Query Insights already had the button in their custom headers. They now use the shared constants. ## Tophatting 1. Go to a project `/observability`. 2. Click into each of the panels and see a **Docs** link in the top right. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added direct documentation links across observability report pages, making it easier to open relevant help content from each view. * Added clearer, page-specific labels for observability headers and docs links. * **Bug Fixes** * Improved accessibility for icon buttons so icons are hidden from assistive technologies while button labels remain clear. * Adjusted report navigation layouts to keep controls aligned with the new docs buttons. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
f78c9db32e
commit
5fc0c86007
16 files changed
+139
-64
No files matched your search
@@ -0,0 +1,15 @@
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
|
||||
export const OBSERVABILITY_DOCS_HREFS = {
|
||||
overview: `${DOCS_URL}/guides/telemetry/reports`,
|
||||
queryPerformance: `${DOCS_URL}/guides/platform/performance#examining-query-performance`,
|
||||
queryInsights: `${DOCS_URL}/guides/platform/performance#examining-query-performance`,
|
||||
apiGateway: `${DOCS_URL}/guides/telemetry/reports#api-gateway`,
|
||||
database: `${DOCS_URL}/guides/telemetry/reports#database`,
|
||||
dataApi: `${DOCS_URL}/guides/telemetry/reports#postgrest`,
|
||||
auth: `${DOCS_URL}/guides/telemetry/reports#auth`,
|
||||
edgeFunctions: `${DOCS_URL}/guides/telemetry/reports#edge-functions`,
|
||||
storage: `${DOCS_URL}/guides/telemetry/reports#storage`,
|
||||
realtime: `${DOCS_URL}/guides/realtime/reports`,
|
||||
customReport: `${DOCS_URL}/guides/telemetry/reports#using-reports`,
|
||||
} as const
|
||||
@@ -8,12 +8,14 @@ import { Badge, Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
|
||||
import { useUnifiedLogsPreview } from '../App/FeaturePreview/FeaturePreviewContext'
|
||||
import { DatabaseInfrastructureSection } from './DatabaseInfrastructureSection'
|
||||
import { OBSERVABILITY_DOCS_HREFS } from './Observability.constants'
|
||||
import { useObservabilityOverviewData } from './ObservabilityOverview.utils'
|
||||
import { ObservabilityOverviewFooter } from './ObservabilityOverviewFooter'
|
||||
import { ServiceHealthTable } from './ServiceHealthTable'
|
||||
import { useSlowQueriesCount } from './useSlowQueriesCount'
|
||||
import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
|
||||
import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ChartIntervalDropdown } from '@/components/ui/Logs/ChartIntervalDropdown'
|
||||
import { CHART_INTERVALS } from '@/components/ui/Logs/logs.utils'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
@@ -23,6 +25,8 @@ import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganizati
|
||||
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
||||
import { useShortcut } from '@/state/shortcuts/useShortcut'
|
||||
|
||||
const REPORT_TITLE = 'Overview'
|
||||
|
||||
type ChartIntervalKey = '1hr' | '1day' | '7day'
|
||||
|
||||
export const ObservabilityOverview = () => {
|
||||
@@ -167,7 +171,7 @@ export const ObservabilityOverview = () => {
|
||||
<ReportPadding>
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
<div className="flex items-center gap-3">
|
||||
<ReportHeader title="Overview" />
|
||||
<ReportHeader title={REPORT_TITLE} />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="warning">Beta</Badge>
|
||||
@@ -178,6 +182,7 @@ export const ObservabilityOverview = () => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.overview} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
|
||||
@@ -9,7 +9,7 @@ const ReportStickyNav = ({
|
||||
return (
|
||||
<section className={cn('relative flex flex-col gap-4 pt-16 -mt-2', className)}>
|
||||
<div className="absolute inset-0 z-40 pointer-events-none flex flex-col gap-4">
|
||||
<div className="sticky top-0 py-4 mb-4 flex items-center gap-2 pointer-events-auto dark:bg-background-200 bg-background">
|
||||
<div className="sticky top-0 py-4 mb-4 flex w-full items-center gap-2 pointer-events-auto dark:bg-background-200 bg-background">
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,10 +12,12 @@ import { ChartConfig } from '../SQLEditor/UtilityPanel/ChartConfig'
|
||||
import { GridResize } from './GridResize'
|
||||
import { MetricOptions } from './MetricOptions'
|
||||
import { LAYOUT_COLUMN_COUNT } from './Reports.constants'
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import { DiscardChangesConfirmationDialog } from '@/components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog'
|
||||
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
||||
import { DatabaseSelector } from '@/components/ui/DatabaseSelector'
|
||||
import { DateRangePicker } from '@/components/ui/DateRangePicker'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import NoPermission from '@/components/ui/NoPermission'
|
||||
import { DEFAULT_CHART_CONFIG } from '@/components/ui/QueryBlock/QueryBlock'
|
||||
import { AnalyticsInterval } from '@/data/analytics/constants'
|
||||
@@ -366,12 +368,14 @@ const Reports = () => {
|
||||
return <NoPermission isFullPage resourceText="access this custom report" />
|
||||
}
|
||||
|
||||
const reportTitle = currentReport?.name || 'Reports'
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col space-y-4" style={{ maxHeight: '100%' }}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1>{currentReport?.name || 'Reports'}</h1>
|
||||
<h1>{reportTitle}</h1>
|
||||
<p className="text-foreground-light">{currentReport?.description}</p>
|
||||
</div>
|
||||
{hasEdits && (
|
||||
@@ -396,14 +400,6 @@ const Reports = () => {
|
||||
</div>
|
||||
<div className={cn('mb-4 flex items-center gap-x-3 justify-between')}>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ButtonTooltip
|
||||
variant="default"
|
||||
icon={<RefreshCw className={isRefreshing ? 'animate-spin' : ''} />}
|
||||
className="w-7"
|
||||
disabled={isRefreshing}
|
||||
tooltip={{ content: { side: 'bottom', text: 'Refresh report' } }}
|
||||
onClick={onRefreshReport}
|
||||
/>
|
||||
<div className="flex items-center gap-x-3">
|
||||
<DateRangePicker
|
||||
value="7d"
|
||||
@@ -423,6 +419,15 @@ const Reports = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-x-2">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.customReport} topic={reportTitle} />
|
||||
<ButtonTooltip
|
||||
variant="default"
|
||||
icon={<RefreshCw className={isRefreshing ? 'animate-spin' : ''} />}
|
||||
className="w-7"
|
||||
disabled={isRefreshing}
|
||||
tooltip={{ content: { side: 'bottom', text: 'Refresh report' } }}
|
||||
onClick={onRefreshReport}
|
||||
/>
|
||||
{canUpdateReport ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
||||
@@ -5,9 +5,10 @@ interface DocsButtonProps {
|
||||
href: string
|
||||
abbrev?: boolean
|
||||
className?: string
|
||||
topic?: string
|
||||
}
|
||||
|
||||
export const DocsButton = ({ href, abbrev = true, className }: DocsButtonProps) => {
|
||||
export const DocsButton = ({ href, abbrev = true, className, topic }: DocsButtonProps) => {
|
||||
return (
|
||||
<Button
|
||||
asChild
|
||||
@@ -16,7 +17,12 @@ export const DocsButton = ({ href, abbrev = true, className }: DocsButtonProps)
|
||||
icon={<BookOpen />}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<a target="_blank" rel="noopener noreferrer" href={href}>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={href}
|
||||
aria-label={topic ? `${topic} documentation (opens in new tab)` : undefined}
|
||||
>
|
||||
{abbrev ? 'Docs' : 'Documentation'}
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import {
|
||||
ErrorCountsChartRenderer,
|
||||
NetworkTrafficRenderer,
|
||||
@@ -18,11 +19,14 @@ import { DatePickerValue } from '@/components/interfaces/Settings/Logs/Logs.Date
|
||||
import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt'
|
||||
import DefaultLayout from '@/components/layouts/DefaultLayout'
|
||||
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { useApiReport } from '@/data/reports/api-report-query'
|
||||
import { useReportDateRange } from '@/hooks/misc/useReportDateRange'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const REPORT_TITLE = 'API Gateway'
|
||||
|
||||
export const ApiReport: NextPageWithLayout = () => {
|
||||
const report = useApiReport()
|
||||
|
||||
@@ -94,10 +98,10 @@ export const ApiReport: NextPageWithLayout = () => {
|
||||
|
||||
return (
|
||||
<ReportPadding>
|
||||
<ReportHeader title="API Gateway" showDatabaseSelector={false} />
|
||||
<ReportHeader title={REPORT_TITLE} showDatabaseSelector={false} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex w-full items-center gap-3">
|
||||
<ReportFilterBar
|
||||
onRemoveFilters={removeFilters}
|
||||
onDatepickerChange={handleDatepickerChange}
|
||||
@@ -109,9 +113,10 @@ export const ApiReport: NextPageWithLayout = () => {
|
||||
filters={filters}
|
||||
datepickerHelpers={datePickerHelpers}
|
||||
initialDatePickerValue={datePickerValue}
|
||||
className="w-full"
|
||||
className="flex-1 min-w-0"
|
||||
showDatabaseSelector={false}
|
||||
/>
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.apiGateway} topic={REPORT_TITLE} />
|
||||
<UpgradePrompt
|
||||
show={showUpgradePrompt}
|
||||
setShowUpgradePrompt={setShowUpgradePrompt}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { parseAsJson, useQueryState } from 'nuqs'
|
||||
import { useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import ReportFilterBar from '@/components/interfaces/Reports/ReportFilterBar'
|
||||
import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
|
||||
import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
|
||||
@@ -29,6 +30,7 @@ import DefaultLayout from '@/components/layouts/DefaultLayout'
|
||||
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
|
||||
import type { ChartHighlightAction } from '@/components/ui/Charts/ChartHighlightActions'
|
||||
import { ReportSettings } from '@/components/ui/Charts/ReportSettings'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import {
|
||||
@@ -58,6 +60,8 @@ AuthReport.getLayout = (page) => (
|
||||
export type UpdateDateRange = (from: string, to: string) => void
|
||||
export default AuthReport
|
||||
|
||||
const REPORT_TITLE = 'Auth'
|
||||
|
||||
const AuthUsage = () => {
|
||||
const { ref } = useParams()
|
||||
const chartSyncId = `auth-report`
|
||||
@@ -209,11 +213,12 @@ const AuthUsage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReportHeader title="Auth" showDatabaseSelector={false} />
|
||||
<ReportHeader title={REPORT_TITLE} showDatabaseSelector={false} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.auth} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { Alert, AlertDescription, Button } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
|
||||
import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
|
||||
import { REPORT_DATERANGE_HELPER_LABELS } from '@/components/interfaces/Reports/Reports.constants'
|
||||
@@ -25,6 +26,7 @@ import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
||||
import type { MultiAttribute } from '@/components/ui/Charts/ComposedChart.utils'
|
||||
import { LazyComposedChartHandler } from '@/components/ui/Charts/ComposedChartHandler'
|
||||
import { ReportSettings } from '@/components/ui/Charts/ReportSettings'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import { analyticsKeys } from '@/data/analytics/keys'
|
||||
@@ -65,6 +67,8 @@ DatabaseReport.getLayout = (page) => (
|
||||
export type UpdateDateRange = (from: string, to: string) => void
|
||||
export default DatabaseReport
|
||||
|
||||
const REPORT_TITLE = 'Database'
|
||||
|
||||
const DatabaseUsage = () => {
|
||||
const { db, chart, ref } = useParams()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
@@ -227,10 +231,11 @@ const DatabaseUsage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReportHeader showDatabaseSelector title="Database" />
|
||||
<ReportHeader showDatabaseSelector title={REPORT_TITLE} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<>
|
||||
<div className="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.database} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
@@ -275,7 +280,7 @@ const DatabaseUsage = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{selectedDateRange &&
|
||||
|
||||
@@ -6,6 +6,7 @@ import { parseAsJson, useQueryState } from 'nuqs'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
|
||||
import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
|
||||
import {
|
||||
@@ -28,6 +29,7 @@ import DefaultLayout from '@/components/layouts/DefaultLayout'
|
||||
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
|
||||
import { ReportSettings } from '@/components/ui/Charts/ReportSettings'
|
||||
import { useChartHoverState } from '@/components/ui/Charts/useChartHoverState'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import { useEdgeFunctionsQuery } from '@/data/edge-functions/edge-functions-query'
|
||||
@@ -54,6 +56,8 @@ EdgeFunctionsReportV2.getLayout = (page) => (
|
||||
|
||||
export default EdgeFunctionsReportV2
|
||||
|
||||
const REPORT_TITLE = 'Edge Functions'
|
||||
|
||||
const EdgeFunctionsUsage = () => {
|
||||
const { ref } = useParams()
|
||||
const { data: functions } = useEdgeFunctionsQuery({
|
||||
@@ -143,11 +147,12 @@ const EdgeFunctionsUsage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReportHeader title="Edge Functions" showDatabaseSelector={false} />
|
||||
<ReportHeader title={REPORT_TITLE} showDatabaseSelector={false} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.edgeFunctions} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ArrowRight, RefreshCw } from 'lucide-react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import ReportFilterBar from '@/components/interfaces/Reports/ReportFilterBar'
|
||||
import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
|
||||
import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt'
|
||||
import DefaultLayout from '@/components/layouts/DefaultLayout'
|
||||
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import { useRefreshHandler, useReportDateRange } from '@/hooks/misc/useReportDateRange'
|
||||
@@ -43,6 +45,8 @@ PostgRESTReport.getLayout = (page) => (
|
||||
export type UpdateDateRange = (from: string, to: string) => void
|
||||
export default PostgRESTReport
|
||||
|
||||
const REPORT_TITLE = 'Data API'
|
||||
|
||||
const PostgrestReport = () => {
|
||||
const { db, chart } = useParams()
|
||||
const {
|
||||
@@ -115,11 +119,12 @@ const PostgrestReport = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReportHeader showDatabaseSelector={false} title="Data API" />
|
||||
<ReportHeader showDatabaseSelector={false} title={REPORT_TITLE} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.dataApi} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import { QueryInsights } from '@/components/interfaces/QueryInsights/QueryInsights'
|
||||
import { REPORT_DATERANGE_HELPER_LABELS } from '@/components/interfaces/Reports/Reports.constants'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
@@ -7,7 +8,6 @@ import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/Observ
|
||||
import { DatabaseSelector } from '@/components/ui/DatabaseSelector'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { useReportDateRange } from '@/hooks/misc/useReportDateRange'
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const PRESETS = [
|
||||
@@ -16,6 +16,8 @@ const PRESETS = [
|
||||
REPORT_DATERANGE_HELPER_LABELS.LAST_24_HOURS,
|
||||
]
|
||||
|
||||
const REPORT_TITLE = 'Query Insights'
|
||||
|
||||
const QueryInsightsReport: NextPageWithLayout = () => {
|
||||
const { selectedDateRange, datePickerValue, datePickerHelpers, handleDatePickerChange } =
|
||||
useReportDateRange(REPORT_DATERANGE_HELPER_LABELS.LAST_60_MINUTES)
|
||||
@@ -30,11 +32,9 @@ const QueryInsightsReport: NextPageWithLayout = () => {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="w-full mb-0 flex lg:items-center justify-between gap-4 py-2 px-6 lg:flex-row flex-col border-b lg:h-[48px]">
|
||||
<h3 className="text-foreground text-xl prose">Query Insights</h3>
|
||||
<h3 className="text-foreground text-xl prose">{REPORT_TITLE}</h3>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<DocsButton
|
||||
href={`${DOCS_URL}/guides/platform/performance#examining-query-performance`}
|
||||
/>
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.queryInsights} topic={REPORT_TITLE} />
|
||||
<DatabaseSelector />
|
||||
<Select
|
||||
value={datePickerValue.isHelper ? datePickerValue.text : undefined}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useParams } from 'common'
|
||||
import { parseAsArrayOf, parseAsInteger, parseAsJson, parseAsString, useQueryStates } from 'nuqs'
|
||||
import { Admonition } from 'ui-patterns'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import { useIndexAdvisorStatus } from '@/components/interfaces/QueryPerformance/hooks/useIsIndexAdvisorStatus'
|
||||
import { useQueryPerformanceSort } from '@/components/interfaces/QueryPerformance/hooks/useQueryPerformanceSort'
|
||||
import { QueryPerformance } from '@/components/interfaces/QueryPerformance/QueryPerformance'
|
||||
@@ -16,9 +17,10 @@ import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/Observ
|
||||
import { DatabaseSelector } from '@/components/ui/DatabaseSelector'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const REPORT_TITLE = 'Query Performance'
|
||||
|
||||
const QueryPerformanceReport: NextPageWithLayout = () => {
|
||||
const { ref } = useParams()
|
||||
const { data: project, isLoading: isLoadingProject } = useSelectedProjectQuery()
|
||||
@@ -88,11 +90,9 @@ const QueryPerformanceReport: NextPageWithLayout = () => {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="w-full mb-0 flex lg:items-center justify-between gap-4 py-4 px-6 lg:flex-row flex-col">
|
||||
<h3 className="text-foreground text-xl prose">Query Performance</h3>
|
||||
<h3 className="text-foreground text-xl prose">{REPORT_TITLE}</h3>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<DocsButton
|
||||
href={`${DOCS_URL}/guides/platform/performance#examining-query-performance`}
|
||||
/>
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.queryPerformance} topic={REPORT_TITLE} />
|
||||
<DatabaseSelector />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ArrowRight, RefreshCw } from 'lucide-react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import ReportFilterBar from '@/components/interfaces/Reports/ReportFilterBar'
|
||||
import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
|
||||
import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
|
||||
@@ -17,6 +18,7 @@ import { LogsDatePicker } from '@/components/interfaces/Settings/Logs/Logs.DateP
|
||||
import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt'
|
||||
import DefaultLayout from '@/components/layouts/DefaultLayout'
|
||||
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import { realtimeReports } from '@/data/reports/v2/realtime.config'
|
||||
@@ -43,6 +45,8 @@ RealtimeReport.getLayout = (page) => (
|
||||
export type UpdateDateRange = (from: string, to: string) => void
|
||||
export default RealtimeReport
|
||||
|
||||
const REPORT_TITLE = 'Realtime'
|
||||
|
||||
const RealtimeUsage = () => {
|
||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||
const [showDatePicker, setShowDatePicker] = useState(false)
|
||||
@@ -140,11 +144,12 @@ const RealtimeUsage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReportHeader showDatabaseSelector={false} title="Realtime" />
|
||||
<ReportHeader showDatabaseSelector={false} title={REPORT_TITLE} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<div className="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.realtime} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
|
||||
@@ -4,6 +4,7 @@ import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { OBSERVABILITY_DOCS_HREFS } from '@/components/interfaces/Observability/Observability.constants'
|
||||
import {
|
||||
NetworkTrafficRenderer,
|
||||
ResponseSpeedChartRenderer,
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt'
|
||||
import DefaultLayout from '@/components/layouts/DefaultLayout'
|
||||
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import { useStorageReport } from '@/data/reports/storage-report-query'
|
||||
@@ -36,6 +38,8 @@ import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
||||
import { useShortcut } from '@/state/shortcuts/useShortcut'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const REPORT_TITLE = 'Storage'
|
||||
|
||||
export const StorageReport: NextPageWithLayout = () => {
|
||||
const report = useStorageReport()
|
||||
|
||||
@@ -88,11 +92,12 @@ export const StorageReport: NextPageWithLayout = () => {
|
||||
|
||||
return (
|
||||
<ReportPadding>
|
||||
<ReportHeader title="Storage" showDatabaseSelector={false} />
|
||||
<ReportHeader title={REPORT_TITLE} showDatabaseSelector={false} />
|
||||
<ReportStickyNav
|
||||
content={
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<DocsButton href={OBSERVABILITY_DOCS_HREFS.storage} topic={REPORT_TITLE} />
|
||||
<ShortcutTooltip
|
||||
shortcutId={SHORTCUT_IDS.OBSERVABILITY_REFRESH}
|
||||
label="Refresh report"
|
||||
|
||||
@@ -74,6 +74,13 @@ describe('#Button', () => {
|
||||
expect(screen.queryByRole('button')).toHaveClass('w-full')
|
||||
})
|
||||
|
||||
it('should hide decorative icons from assistive technology', () => {
|
||||
render(<Button icon={<svg data-testid="button-icon" />}>Save</Button>)
|
||||
|
||||
expect(screen.getByTestId('button-icon').parentElement).toHaveAttribute('aria-hidden', 'true')
|
||||
expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should forward ref', () => {
|
||||
const ref: React.MutableRefObject<HTMLButtonElement | null> = {
|
||||
current: null,
|
||||
|
||||
@@ -239,6 +239,12 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
// - Otherwise, default to 0 for keyboard accessibility
|
||||
const computedTabIndex = tabIndex !== undefined ? tabIndex : disabled ? -1 : 0
|
||||
|
||||
const renderIconContainer = (content: ReactNode) => (
|
||||
<div aria-hidden className={cn(IconContainerVariants({ size, variant }))}>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
@@ -260,35 +266,31 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
children,
|
||||
undefined,
|
||||
showIcon &&
|
||||
(loading ? (
|
||||
<div className={cn(IconContainerVariants({ size, variant }))}>
|
||||
<Loader2 className={cn(loadingVariants({ loading, variant }))} />
|
||||
</div>
|
||||
) : _iconLeft ? (
|
||||
<div className={cn(IconContainerVariants({ size, variant }))}>{_iconLeft}</div>
|
||||
) : null),
|
||||
(loading
|
||||
? renderIconContainer(
|
||||
<Loader2 className={cn(loadingVariants({ loading, variant }))} />
|
||||
)
|
||||
: _iconLeft
|
||||
? renderIconContainer(_iconLeft)
|
||||
: null),
|
||||
children.props.children && (
|
||||
<span className={'truncate'}>{children.props.children}</span>
|
||||
),
|
||||
iconRight && !loading && (
|
||||
<div className={cn(IconContainerVariants({ size, variant }))}>{iconRight}</div>
|
||||
)
|
||||
iconRight && !loading && renderIconContainer(iconRight)
|
||||
)
|
||||
) : null
|
||||
) : (
|
||||
<>
|
||||
{showIcon &&
|
||||
(loading ? (
|
||||
<div className={cn(IconContainerVariants({ size, variant }))}>
|
||||
<Loader2 className={cn(loadingVariants({ loading, variant }))} />
|
||||
</div>
|
||||
) : _iconLeft ? (
|
||||
<div className={cn(IconContainerVariants({ size, variant }))}>{_iconLeft}</div>
|
||||
) : null)}{' '}
|
||||
(loading
|
||||
? renderIconContainer(
|
||||
<Loader2 className={cn(loadingVariants({ loading, variant }))} />
|
||||
)
|
||||
: _iconLeft
|
||||
? renderIconContainer(_iconLeft)
|
||||
: null)}{' '}
|
||||
{children && <span className={'truncate'}>{children}</span>}{' '}
|
||||
{iconRight && !loading && (
|
||||
<div className={cn(IconContainerVariants({ size, variant }))}>{iconRight}</div>
|
||||
)}
|
||||
{iconRight && !loading && renderIconContainer(iconRight)}
|
||||
</>
|
||||
)}
|
||||
</Comp>
|
||||
|
||||
Reference in new issue
Block a user