mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 10:55:06 +03:00
Auth + Database Report Fix (#36768)
rollback previous files and move auth changes to new files
This commit is contained in:
1 parent
4984392edd
commit
92eabe3e3e
5 files changed
+446
-77
No files matched your search
@@ -7,7 +7,7 @@
|
||||
* This component acts as a bridge between the data-fetching logic and the
|
||||
* presentational chart component.
|
||||
*/
|
||||
import ComposedChartHandler from 'components/ui/Charts/ComposedChartHandler'
|
||||
import LogChartHandler from 'components/ui/Charts/LogChartHandler'
|
||||
import { useChartData } from 'hooks/useChartData'
|
||||
import type { UpdateDateRange } from 'pages/project/[ref]/reports/database'
|
||||
import type { MultiAttribute } from 'components/ui/Charts/ComposedChart.utils'
|
||||
@@ -43,7 +43,7 @@ const ReportChart = ({
|
||||
})
|
||||
|
||||
return (
|
||||
<ComposedChartHandler
|
||||
<LogChartHandler
|
||||
{...chart}
|
||||
attributes={
|
||||
(chartAttributes.length > 0 ? chartAttributes : chart.attributes) as MultiAttribute[]
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
/**
|
||||
* ComposedChartHandler
|
||||
*
|
||||
* A presentational component for rendering charts.
|
||||
* It is responsible only for rendering the chart UI based on the data and loading state passed to it as props.
|
||||
* All the complex data fetching logic has been moved to the useChartData hook.
|
||||
*/
|
||||
import React, { PropsWithChildren, useState, useEffect, useRef } from 'react'
|
||||
import React, { PropsWithChildren, useState, useMemo, useEffect, useRef } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { cn, WarningIcon } from 'ui'
|
||||
|
||||
@@ -17,14 +11,14 @@ import { InfraMonitoringAttribute } from 'data/analytics/infra-monitoring-query'
|
||||
import { useInfraMonitoringQueries } from 'data/analytics/infra-monitoring-queries'
|
||||
import { ProjectDailyStatsAttribute } from 'data/analytics/project-daily-stats-query'
|
||||
import { useProjectDailyStatsQueries } from 'data/analytics/project-daily-stats-queries'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import { useChartHighlight } from './useChartHighlight'
|
||||
import { getMockDataForAttribute } from 'data/reports/auth-charts'
|
||||
|
||||
import type { ChartData } from './Charts.types'
|
||||
import type { UpdateDateRange } from 'pages/project/[ref]/reports/database'
|
||||
import type { MultiAttribute } from './ComposedChart.utils'
|
||||
import { MultiAttribute } from './ComposedChart.utils'
|
||||
|
||||
interface ComposedChartHandlerProps {
|
||||
export interface ComposedChartHandlerProps {
|
||||
id?: string
|
||||
label: string
|
||||
attributes: MultiAttribute[]
|
||||
@@ -46,7 +40,6 @@ interface ComposedChartHandlerProps {
|
||||
updateDateRange: UpdateDateRange
|
||||
valuePrecision?: number
|
||||
isVisible?: boolean
|
||||
titleTooltip?: string
|
||||
docsUrl?: string
|
||||
}
|
||||
|
||||
@@ -98,6 +91,9 @@ const LazyChartWrapper = ({ children }: PropsWithChildren) => {
|
||||
const ComposedChartHandler = ({
|
||||
label,
|
||||
attributes,
|
||||
startDate,
|
||||
endDate,
|
||||
interval,
|
||||
customDateFormat,
|
||||
children = null,
|
||||
defaultChartStyle = 'bar',
|
||||
@@ -113,14 +109,131 @@ const ComposedChartHandler = ({
|
||||
showTotal,
|
||||
updateDateRange,
|
||||
valuePrecision,
|
||||
titleTooltip,
|
||||
isVisible = true,
|
||||
id,
|
||||
...otherProps
|
||||
}: PropsWithChildren<ComposedChartHandlerProps>) => {
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
|
||||
const state = useDatabaseSelectorStateSnapshot()
|
||||
const [chartStyle, setChartStyle] = useState<string>(defaultChartStyle)
|
||||
const chartHighlight = useChartHighlight()
|
||||
|
||||
if (isLoading) {
|
||||
const databaseIdentifier = state.selectedDatabaseId
|
||||
|
||||
// Use the custom hook at the top level of the component
|
||||
const attributeQueries = useAttributeQueries(
|
||||
attributes,
|
||||
ref,
|
||||
startDate,
|
||||
endDate,
|
||||
interval as AnalyticsInterval,
|
||||
databaseIdentifier,
|
||||
data,
|
||||
isVisible
|
||||
)
|
||||
|
||||
// Combine all the data into a single dataset
|
||||
const combinedData = useMemo(() => {
|
||||
if (data) return data
|
||||
|
||||
const isLoading = attributeQueries.some((query: any) => query.isLoading)
|
||||
if (isLoading) return undefined
|
||||
|
||||
const hasError = attributeQueries.some((query: any) => !query.data)
|
||||
if (hasError) return undefined
|
||||
|
||||
// Get all unique timestamps from all datasets
|
||||
const timestamps = new Set<string>()
|
||||
attributeQueries.forEach((query: any) => {
|
||||
query.data?.data?.forEach((point: any) => {
|
||||
if (point?.period_start) {
|
||||
timestamps.add(point.period_start)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const referenceLineQueries = attributeQueries.filter(
|
||||
(_, index) => attributes[index].provider === 'reference-line'
|
||||
)
|
||||
|
||||
// Combine data points for each timestamp
|
||||
const combined = Array.from(timestamps)
|
||||
.sort()
|
||||
.map((timestamp) => {
|
||||
const point: any = { timestamp }
|
||||
|
||||
// Add regular attributes
|
||||
attributes.forEach((attr, index) => {
|
||||
if (!attr) return
|
||||
|
||||
// Handle custom value attributes (like disk size)
|
||||
if (attr.customValue !== undefined) {
|
||||
point[attr.attribute] = attr.customValue
|
||||
return
|
||||
}
|
||||
|
||||
// Skip reference line attributes here, we'll add them below
|
||||
if (attr.provider === 'reference-line') return
|
||||
|
||||
const queryData = attributeQueries[index]?.data?.data
|
||||
const matchingPoint = queryData?.find((p: any) => p.period_start === timestamp)
|
||||
let value = matchingPoint?.[attr.attribute] ?? 0
|
||||
|
||||
// Apply value manipulation if provided
|
||||
if (attr.manipulateValue && typeof attr.manipulateValue === 'function') {
|
||||
// Ensure value is a number before manipulation
|
||||
const numericValue = typeof value === 'number' ? value : Number(value) || 0
|
||||
value = attr.manipulateValue(numericValue)
|
||||
}
|
||||
|
||||
point[attr.attribute] = value
|
||||
})
|
||||
|
||||
// Add reference line values for each timestamp
|
||||
referenceLineQueries.forEach((query: any) => {
|
||||
const attr = query.data.attribute
|
||||
const value = query.data.total
|
||||
point[attr] = value
|
||||
})
|
||||
|
||||
return point as DataPoint
|
||||
})
|
||||
|
||||
return combined as DataPoint[]
|
||||
}, [data, attributeQueries, attributes])
|
||||
|
||||
const loading = isLoading || attributeQueries.some((query: any) => query.isLoading)
|
||||
|
||||
// Calculate highlighted value based on the first attribute's data
|
||||
const _highlightedValue = useMemo(() => {
|
||||
if (highlightedValue !== undefined) return highlightedValue
|
||||
|
||||
const firstAttr = attributes[0]
|
||||
const firstQuery = attributeQueries[0]
|
||||
const firstData = firstQuery?.data
|
||||
|
||||
if (!firstData) return undefined
|
||||
|
||||
const shouldHighlightMaxValue =
|
||||
firstAttr.provider === 'daily-stats' &&
|
||||
!firstAttr.attribute.includes('ingress') &&
|
||||
!firstAttr.attribute.includes('egress') &&
|
||||
'maximum' in firstData
|
||||
|
||||
const shouldHighlightTotalGroupedValue = 'totalGrouped' in firstData
|
||||
|
||||
return shouldHighlightMaxValue
|
||||
? firstData.maximum
|
||||
: firstAttr.provider === 'daily-stats'
|
||||
? firstData.total
|
||||
: shouldHighlightTotalGroupedValue
|
||||
? firstData.totalGrouped?.[firstAttr.attribute as keyof typeof firstData.totalGrouped]
|
||||
: (firstData.data[firstData.data.length - 1] as any)?.[firstAttr.attribute]
|
||||
}, [highlightedValue, attributes, attributeQueries])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Panel
|
||||
className={cn(
|
||||
@@ -137,7 +250,7 @@ const ComposedChartHandler = ({
|
||||
)
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (!combinedData) {
|
||||
return (
|
||||
<div className="flex h-52 w-full flex-col items-center justify-center gap-y-2">
|
||||
<WarningIcon />
|
||||
@@ -159,11 +272,11 @@ const ComposedChartHandler = ({
|
||||
<div className="absolute right-6 z-50 flex justify-between scroll-mt-16">{children}</div>
|
||||
<ComposedChart
|
||||
attributes={attributes}
|
||||
data={data as any}
|
||||
data={combinedData as DataPoint[]}
|
||||
format={format}
|
||||
xAxisKey="period_start"
|
||||
yAxisKey={attributes[0].attribute}
|
||||
highlightedValue={highlightedValue}
|
||||
highlightedValue={_highlightedValue}
|
||||
title={label}
|
||||
customDateFormat={customDateFormat}
|
||||
chartHighlight={chartHighlight}
|
||||
@@ -176,7 +289,6 @@ const ComposedChartHandler = ({
|
||||
updateDateRange={updateDateRange}
|
||||
valuePrecision={valuePrecision}
|
||||
hideChartType={hideChartType}
|
||||
titleTooltip={titleTooltip}
|
||||
{...otherProps}
|
||||
/>
|
||||
</Panel.Content>
|
||||
@@ -184,7 +296,7 @@ const ComposedChartHandler = ({
|
||||
)
|
||||
}
|
||||
|
||||
export const useAttributeQueries = (
|
||||
const useAttributeQueries = (
|
||||
attributes: MultiAttribute[],
|
||||
ref: string | string[] | undefined,
|
||||
startDate: string,
|
||||
@@ -194,15 +306,16 @@ export const useAttributeQueries = (
|
||||
data: ChartData | undefined,
|
||||
isVisible: boolean
|
||||
) => {
|
||||
const projectRef = typeof ref === 'string' ? ref : Array.isArray(ref) ? ref[0] : ''
|
||||
|
||||
const infraAttributes = attributes.filter((attr) => attr.provider === 'infra-monitoring')
|
||||
const dailyStatsAttributes = attributes.filter((attr) => attr.provider === 'daily-stats')
|
||||
const mockAttributes = attributes.filter((attr) => attr.provider === 'mock')
|
||||
const referenceLineAttributes = attributes.filter((attr) => attr.provider === 'reference-line')
|
||||
const infraAttributes = attributes
|
||||
.filter((attr) => attr?.provider === 'infra-monitoring')
|
||||
.map((attr) => attr.attribute as InfraMonitoringAttribute)
|
||||
const dailyStatsAttributes = attributes
|
||||
.filter((attr) => attr?.provider === 'daily-stats')
|
||||
.map((attr) => attr.attribute as ProjectDailyStatsAttribute)
|
||||
const referenceLines = attributes.filter((attr) => attr?.provider === 'reference-line')
|
||||
|
||||
const infraQueries = useInfraMonitoringQueries(
|
||||
infraAttributes.map((attr) => attr.attribute as InfraMonitoringAttribute),
|
||||
infraAttributes,
|
||||
ref,
|
||||
startDate,
|
||||
endDate,
|
||||
@@ -212,7 +325,7 @@ export const useAttributeQueries = (
|
||||
isVisible
|
||||
)
|
||||
const dailyStatsQueries = useProjectDailyStatsQueries(
|
||||
dailyStatsAttributes.map((attr) => attr.attribute as ProjectDailyStatsAttribute),
|
||||
dailyStatsAttributes,
|
||||
ref,
|
||||
startDate,
|
||||
endDate,
|
||||
@@ -222,48 +335,23 @@ export const useAttributeQueries = (
|
||||
isVisible
|
||||
)
|
||||
|
||||
let infraIdx = 0
|
||||
let dailyStatsIdx = 0
|
||||
return attributes
|
||||
.filter((attr) => attr.provider !== 'logs')
|
||||
.map((attr) => {
|
||||
if (attr.provider === 'infra-monitoring') {
|
||||
return {
|
||||
...infraQueries[infraIdx++],
|
||||
data: { ...infraQueries[infraIdx - 1]?.data, provider: 'infra-monitoring' },
|
||||
}
|
||||
} else if (attr.provider === 'daily-stats') {
|
||||
return {
|
||||
...dailyStatsQueries[dailyStatsIdx++],
|
||||
data: { ...dailyStatsQueries[dailyStatsIdx - 1]?.data, provider: 'daily-stats' },
|
||||
}
|
||||
} else if (attr.provider === 'mock') {
|
||||
const mockData = getMockDataForAttribute(attr.attribute)
|
||||
return {
|
||||
isLoading: false,
|
||||
data: { ...mockData, provider: 'mock', attribute: attr.attribute },
|
||||
}
|
||||
} else if (attr.provider === 'reference-line') {
|
||||
let value = attr.value || 0
|
||||
return {
|
||||
data: {
|
||||
data: [],
|
||||
attribute: attr.attribute,
|
||||
total: value,
|
||||
maximum: value,
|
||||
totalGrouped: { [attr.attribute]: value },
|
||||
provider: 'reference-line',
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
isLoading: false,
|
||||
data: undefined,
|
||||
}
|
||||
}
|
||||
})
|
||||
const referenceLineQueries = referenceLines.map((line) => {
|
||||
let value = line.value || 0
|
||||
|
||||
return {
|
||||
data: {
|
||||
data: [], // Will be populated in combinedData
|
||||
attribute: line.attribute,
|
||||
total: value,
|
||||
maximum: value,
|
||||
totalGrouped: { [line.attribute]: value },
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
}
|
||||
})
|
||||
|
||||
return [...infraQueries, ...dailyStatsQueries, ...referenceLineQueries]
|
||||
}
|
||||
|
||||
export default function LazyComposedChartHandler(props: ComposedChartHandlerProps) {
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
import React, { PropsWithChildren, useState, useEffect, useRef } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { cn, WarningIcon } from 'ui'
|
||||
|
||||
import Panel from 'components/ui/Panel'
|
||||
import ComposedChart from './ComposedChart'
|
||||
|
||||
import { AnalyticsInterval, DataPoint } from 'data/analytics/constants'
|
||||
import { InfraMonitoringAttribute } from 'data/analytics/infra-monitoring-query'
|
||||
import { useInfraMonitoringQueries } from 'data/analytics/infra-monitoring-queries'
|
||||
import { ProjectDailyStatsAttribute } from 'data/analytics/project-daily-stats-query'
|
||||
import { useProjectDailyStatsQueries } from 'data/analytics/project-daily-stats-queries'
|
||||
import { useChartHighlight } from './useChartHighlight'
|
||||
import { getMockDataForAttribute } from 'data/reports/auth-charts'
|
||||
|
||||
import type { ChartData } from './Charts.types'
|
||||
import type { UpdateDateRange } from 'pages/project/[ref]/reports/database'
|
||||
import type { MultiAttribute } from './ComposedChart.utils'
|
||||
|
||||
interface LogChartHandlerProps {
|
||||
id?: string
|
||||
label: string
|
||||
attributes: MultiAttribute[]
|
||||
startDate: string
|
||||
endDate: string
|
||||
interval: string
|
||||
customDateFormat?: string
|
||||
defaultChartStyle?: 'bar' | 'line' | 'stackedAreaLine'
|
||||
hideChartType?: boolean
|
||||
data?: ChartData
|
||||
isLoading?: boolean
|
||||
format?: string
|
||||
highlightedValue?: string | number
|
||||
className?: string
|
||||
showTooltip?: boolean
|
||||
showLegend?: boolean
|
||||
showTotal?: boolean
|
||||
showMaxValue?: boolean
|
||||
updateDateRange: UpdateDateRange
|
||||
valuePrecision?: number
|
||||
isVisible?: boolean
|
||||
titleTooltip?: string
|
||||
docsUrl?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper component that handles intersection observer logic for lazy loading
|
||||
*/
|
||||
const LazyChartWrapper = ({ children }: PropsWithChildren) => {
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true)
|
||||
observer.disconnect()
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '150px 0px', // Start loading before the component enters viewport
|
||||
threshold: 0,
|
||||
}
|
||||
)
|
||||
|
||||
const currentRef = ref.current
|
||||
if (currentRef) {
|
||||
observer.observe(currentRef)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (currentRef) {
|
||||
observer.unobserve(currentRef)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div ref={ref}>{React.cloneElement(children as React.ReactElement, { isVisible })}</div>
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls chart display state. Optionally fetches static chart data if data is not provided.
|
||||
*
|
||||
* If the `data` prop is provided, it will disable automatic chart data fetching and pass the data directly to the chart render.
|
||||
* - loading state can also be provided through the `isLoading` prop, to display loading placeholders. Ignored if `data` key not provided.
|
||||
* - if `isLoading=true` and `data` is `undefined`, loading error message will be shown.
|
||||
*
|
||||
* Provided data must be in the expected chart format.
|
||||
*/
|
||||
const LogChartHandler = ({
|
||||
label,
|
||||
attributes,
|
||||
customDateFormat,
|
||||
children = null,
|
||||
defaultChartStyle = 'bar',
|
||||
hideChartType = false,
|
||||
data,
|
||||
isLoading,
|
||||
format,
|
||||
highlightedValue,
|
||||
className,
|
||||
showTooltip,
|
||||
showLegend,
|
||||
showMaxValue,
|
||||
showTotal,
|
||||
updateDateRange,
|
||||
valuePrecision,
|
||||
titleTooltip,
|
||||
id,
|
||||
...otherProps
|
||||
}: PropsWithChildren<LogChartHandlerProps>) => {
|
||||
const [chartStyle, setChartStyle] = useState<string>(defaultChartStyle)
|
||||
const chartHighlight = useChartHighlight()
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Panel
|
||||
className={cn(
|
||||
'flex min-h-[320px] w-full flex-col items-center justify-center gap-y-2',
|
||||
className
|
||||
)}
|
||||
wrapWithLoading={false}
|
||||
noMargin
|
||||
noHideOverflow
|
||||
>
|
||||
<Loader2 size={18} className="animate-spin text-border-strong" />
|
||||
<p className="text-xs text-foreground-lighter">Loading data for {label}</p>
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className="flex h-52 w-full flex-col items-center justify-center gap-y-2">
|
||||
<WarningIcon />
|
||||
<p className="text-xs text-foreground-lighter">Unable to load data for {label}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Rest of the component remains similar, but pass all attributes to charts
|
||||
return (
|
||||
<Panel
|
||||
noMargin
|
||||
noHideOverflow
|
||||
className={cn('relative py-2 w-full scroll-mt-16', className)}
|
||||
wrapWithLoading={false}
|
||||
id={id ?? label.toLowerCase().replaceAll(' ', '-')}
|
||||
>
|
||||
<Panel.Content className="flex flex-col gap-4">
|
||||
<div className="absolute right-6 z-50 flex justify-between scroll-mt-16">{children}</div>
|
||||
<ComposedChart
|
||||
attributes={attributes}
|
||||
data={data as any}
|
||||
format={format}
|
||||
xAxisKey="period_start"
|
||||
yAxisKey={attributes[0].attribute}
|
||||
highlightedValue={highlightedValue}
|
||||
title={label}
|
||||
customDateFormat={customDateFormat}
|
||||
chartHighlight={chartHighlight}
|
||||
chartStyle={chartStyle}
|
||||
showTooltip={showTooltip}
|
||||
showLegend={showLegend}
|
||||
showTotal={showTotal}
|
||||
showMaxValue={showMaxValue}
|
||||
onChartStyleChange={setChartStyle}
|
||||
updateDateRange={updateDateRange}
|
||||
valuePrecision={valuePrecision}
|
||||
hideChartType={hideChartType}
|
||||
titleTooltip={titleTooltip}
|
||||
{...otherProps}
|
||||
/>
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
|
||||
export const useAttributeQueries = (
|
||||
attributes: MultiAttribute[],
|
||||
ref: string | string[] | undefined,
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
interval: AnalyticsInterval,
|
||||
databaseIdentifier: string | undefined,
|
||||
data: ChartData | undefined,
|
||||
isVisible: boolean
|
||||
) => {
|
||||
const projectRef = typeof ref === 'string' ? ref : Array.isArray(ref) ? ref[0] : ''
|
||||
|
||||
const infraAttributes = attributes.filter((attr) => attr.provider === 'infra-monitoring')
|
||||
const dailyStatsAttributes = attributes.filter((attr) => attr.provider === 'daily-stats')
|
||||
const mockAttributes = attributes.filter((attr) => attr.provider === 'mock')
|
||||
const referenceLineAttributes = attributes.filter((attr) => attr.provider === 'reference-line')
|
||||
|
||||
const infraQueries = useInfraMonitoringQueries(
|
||||
infraAttributes.map((attr) => attr.attribute as InfraMonitoringAttribute),
|
||||
ref,
|
||||
startDate,
|
||||
endDate,
|
||||
interval,
|
||||
databaseIdentifier,
|
||||
data,
|
||||
isVisible
|
||||
)
|
||||
const dailyStatsQueries = useProjectDailyStatsQueries(
|
||||
dailyStatsAttributes.map((attr) => attr.attribute as ProjectDailyStatsAttribute),
|
||||
ref,
|
||||
startDate,
|
||||
endDate,
|
||||
interval,
|
||||
databaseIdentifier,
|
||||
data,
|
||||
isVisible
|
||||
)
|
||||
|
||||
let infraIdx = 0
|
||||
let dailyStatsIdx = 0
|
||||
return attributes
|
||||
.filter((attr) => attr.provider !== 'logs')
|
||||
.map((attr) => {
|
||||
if (attr.provider === 'infra-monitoring') {
|
||||
return {
|
||||
...infraQueries[infraIdx++],
|
||||
data: { ...infraQueries[infraIdx - 1]?.data, provider: 'infra-monitoring' },
|
||||
}
|
||||
} else if (attr.provider === 'daily-stats') {
|
||||
return {
|
||||
...dailyStatsQueries[dailyStatsIdx++],
|
||||
data: { ...dailyStatsQueries[dailyStatsIdx - 1]?.data, provider: 'daily-stats' },
|
||||
}
|
||||
} else if (attr.provider === 'mock') {
|
||||
const mockData = getMockDataForAttribute(attr.attribute)
|
||||
return {
|
||||
isLoading: false,
|
||||
data: { ...mockData, provider: 'mock', attribute: attr.attribute },
|
||||
}
|
||||
} else if (attr.provider === 'reference-line') {
|
||||
let value = attr.value || 0
|
||||
return {
|
||||
data: {
|
||||
data: [],
|
||||
attribute: attr.attribute,
|
||||
total: value,
|
||||
maximum: value,
|
||||
totalGrouped: { [attr.attribute]: value },
|
||||
provider: 'reference-line',
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
isLoading: false,
|
||||
data: undefined,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default function LazyLogChartHandler(props: LogChartHandlerProps) {
|
||||
return (
|
||||
<LazyChartWrapper>
|
||||
<LogChartHandler {...props} />
|
||||
</LazyChartWrapper>
|
||||
)
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import type { AnalyticsInterval, DataPoint } from 'data/analytics/constants'
|
||||
import { useAuthLogsReport } from 'data/reports/auth-report-query'
|
||||
import type { ChartData } from 'components/ui/Charts/Charts.types'
|
||||
import type { MultiAttribute } from 'components/ui/Charts/ComposedChart.utils'
|
||||
import { useAttributeQueries } from 'components/ui/Charts/ComposedChartHandler'
|
||||
import { useAttributeQueries } from 'components/ui/Charts/LogChartHandler'
|
||||
|
||||
export const useChartData = ({
|
||||
attributes,
|
||||
|
||||
@@ -21,6 +21,7 @@ import ChartHandler from 'components/ui/Charts/ChartHandler'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import ShimmerLine from 'components/ui/ShimmerLine'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import ComposedChartHandler from 'components/ui/Charts/ComposedChartHandler'
|
||||
import { DateRangePicker } from 'components/ui/DateRangePicker'
|
||||
import GrafanaPromoBanner from 'components/ui/GrafanaPromoBanner'
|
||||
|
||||
@@ -36,8 +37,8 @@ import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { TIME_PERIODS_INFRA } from 'lib/constants/metrics'
|
||||
import { formatBytes } from 'lib/helpers'
|
||||
|
||||
import ReportChart from 'components/interfaces/Reports/ReportChart'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import type { MultiAttribute } from 'components/ui/Charts/ComposedChart.utils'
|
||||
|
||||
const DatabaseReport: NextPageWithLayout = () => {
|
||||
return (
|
||||
@@ -207,10 +208,15 @@ const DatabaseUsage = () => {
|
||||
return (
|
||||
<>
|
||||
<ReportHeader showDatabaseSelector title="Database" />
|
||||
<div className="w-full flex flex-col gap-1">
|
||||
<div className="h-2 w-full">
|
||||
<ShimmerLine active={report.isLoading} />
|
||||
</div>
|
||||
</div>
|
||||
<GrafanaPromoBanner />
|
||||
<section className="relative pt-16 -mt-2">
|
||||
<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 space-x-3 pointer-events-auto dark:bg-background-200 bg-background">
|
||||
<div className="sticky top-0 bg-200 py-4 mb-4 flex items-center space-x-3 pointer-events-auto">
|
||||
<ButtonTooltip
|
||||
type="default"
|
||||
disabled={isRefreshing}
|
||||
@@ -253,13 +259,20 @@ const DatabaseUsage = () => {
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{dateRange &&
|
||||
REPORT_ATTRIBUTES_V2.filter((chart) => !chart.hide).map((chart) => (
|
||||
<ReportChart
|
||||
<ComposedChartHandler
|
||||
key={chart.id}
|
||||
chart={chart}
|
||||
{...chart}
|
||||
attributes={chart.attributes as MultiAttribute[]}
|
||||
interval={dateRange.interval}
|
||||
startDate={dateRange?.period_start?.date}
|
||||
endDate={dateRange?.period_end?.date}
|
||||
interval={dateRange.interval}
|
||||
updateDateRange={updateDateRange}
|
||||
defaultChartStyle={chart.defaultChartStyle as 'line' | 'bar' | 'stackedAreaLine'}
|
||||
showMaxValue={
|
||||
chart.id === 'client-connections' || chart.id === 'pgbouncer-connections'
|
||||
? true
|
||||
: chart.showMaxValue
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user