mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Reports clean ups (#37248)
* use similar ui styles for different charts * fix naming * use similar styles, fix height * rm year from tooltip timestamp * remove unnecessary code in useChartData
This commit is contained in:
1 parent
5e15856d13
commit
dfacfd5137
9 files changed
+23
-139
No files matched your search
@@ -49,7 +49,6 @@ const ReportChart = ({
|
||||
const {
|
||||
data,
|
||||
isLoading: isLoadingChart,
|
||||
chartAttributes,
|
||||
highlightedValue,
|
||||
} = useChartData({
|
||||
attributes: chart.attributes,
|
||||
@@ -72,9 +71,7 @@ const ReportChart = ({
|
||||
const { data: filledData, isError: isFillError } = useFillTimeseriesSorted(
|
||||
chartDataArray,
|
||||
'period_start',
|
||||
(chartAttributes.length > 0 ? chartAttributes : chart.attributes).map(
|
||||
(attr: any) => attr.attribute
|
||||
),
|
||||
chart.attributes.map((attr: any) => attr.attribute),
|
||||
0,
|
||||
startDate,
|
||||
endDate,
|
||||
@@ -159,9 +156,7 @@ const ReportChart = ({
|
||||
return (
|
||||
<LogChartHandler
|
||||
{...chart}
|
||||
attributes={
|
||||
(chartAttributes.length > 0 ? chartAttributes : chart.attributes) as MultiAttribute[]
|
||||
}
|
||||
attributes={chart.attributes as MultiAttribute[]}
|
||||
data={finalData}
|
||||
isLoading={isLoadingChart || isLoading}
|
||||
highlightedValue={highlightedValue as any}
|
||||
|
||||
@@ -82,6 +82,6 @@ export const DEFAULT_STACK_COLORS: ValidStackColor[] = [
|
||||
|
||||
export enum DateTimeFormats {
|
||||
FULL = 'MMM D, YYYY, hh:mma',
|
||||
FULL_SECONDS = 'MMM D, YYYY, hh:mm:ssa',
|
||||
FULL_SECONDS = 'MMM D, hh:mm:ssa',
|
||||
DATE_ONLY = 'MMM D, YYYY',
|
||||
}
|
||||
@@ -238,7 +238,7 @@ const ComposedChartHandler = ({
|
||||
return (
|
||||
<Panel
|
||||
className={cn(
|
||||
'flex min-h-[320px] w-full flex-col items-center justify-center gap-y-2',
|
||||
'flex min-h-[280px] w-full flex-col items-center justify-center gap-y-2',
|
||||
className
|
||||
)}
|
||||
wrapWithLoading={false}
|
||||
|
||||
@@ -112,23 +112,6 @@ const LogChartHandler = ({
|
||||
const [chartStyle, setChartStyle] = useState<string>(defaultChartStyle)
|
||||
const chartHighlight = useChartHighlight()
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Panel
|
||||
className={cn(
|
||||
'flex min-h-[280px] 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">
|
||||
@@ -143,10 +126,16 @@ const LogChartHandler = ({
|
||||
<Panel
|
||||
noMargin
|
||||
noHideOverflow
|
||||
className={cn('relative w-full scroll-mt-16', className)}
|
||||
className={cn('relative w-full overflow-hidden scroll-mt-16', className)}
|
||||
wrapWithLoading={false}
|
||||
id={id ?? label.toLowerCase().replaceAll(' ', '-')}
|
||||
>
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 rounded-md flex w-full flex-col items-center justify-center gap-y-2 bg-surface-100 backdrop z-20">
|
||||
<Loader2 size={18} className="animate-spin text-border-strong" />
|
||||
<p className="text-xs text-foreground-lighter">Loading data for {label}</p>
|
||||
</div>
|
||||
)}
|
||||
<Panel.Content className="flex flex-col gap-4">
|
||||
<div className="absolute right-6 z-50 flex justify-between scroll-mt-16">{children}</div>
|
||||
<ComposedChart
|
||||
@@ -186,12 +175,8 @@ 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 infraQueries = useInfraMonitoringQueries(
|
||||
infraAttributes.map((attr) => attr.attribute as InfraMonitoringAttribute),
|
||||
|
||||
@@ -307,6 +307,7 @@ export function useAuthLogsReport({
|
||||
data: rawData,
|
||||
error,
|
||||
isLoading,
|
||||
isFetching,
|
||||
} = useQuery(
|
||||
['auth-logs-report', projectRef, logsMetric, startDate, endDate, interval, sql],
|
||||
async () => {
|
||||
@@ -339,5 +340,6 @@ export function useAuthLogsReport({
|
||||
attributes: chartAttributes,
|
||||
isLoading,
|
||||
error,
|
||||
isFetching,
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,11 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
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/LogChartHandler'
|
||||
import { useEdgeFunctionReport } from 'data/reports/edgefn-query'
|
||||
import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query'
|
||||
import type { InfraMonitoringAttribute } from 'data/analytics/infra-monitoring-query'
|
||||
|
||||
export const useChartData = ({
|
||||
attributes,
|
||||
@@ -42,10 +38,8 @@ export const useChartData = ({
|
||||
}) => {
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
const state = useDatabaseSelectorStateSnapshot()
|
||||
|
||||
const logsAttributes = attributes.filter((attr) => attr.provider === 'logs')
|
||||
const nonLogsAttributes = attributes.filter((attr) => attr.provider !== 'logs')
|
||||
|
||||
const isEdgeFunctionRoute = router.asPath.includes('/reports/edge-functions')
|
||||
|
||||
@@ -76,66 +70,15 @@ export const useChartData = ({
|
||||
functionIds,
|
||||
})
|
||||
|
||||
const {
|
||||
data: infraData,
|
||||
error: infraError,
|
||||
isLoading: isInfraLoading,
|
||||
} = useInfraMonitoringQuery(
|
||||
{
|
||||
projectRef: ref as string,
|
||||
attribute: nonLogsAttributes[0]?.attribute as InfraMonitoringAttribute,
|
||||
interval: interval as AnalyticsInterval,
|
||||
startDate,
|
||||
endDate,
|
||||
databaseIdentifier: state.selectedDatabaseId,
|
||||
},
|
||||
{ enabled: enabled && nonLogsAttributes.length > 0 }
|
||||
)
|
||||
|
||||
const logsData = isEdgeFunctionRoute ? edgeFunctionData : authData
|
||||
const logsChartAttributes = isEdgeFunctionRoute
|
||||
? edgeFunctionChartAttributes
|
||||
: authChartAttributes
|
||||
const isLogsLoading = isEdgeFunctionRoute ? isEdgeFunctionLoading : isAuthLoading
|
||||
|
||||
const chartAttributes = useMemo(
|
||||
() => nonLogsAttributes.concat(logsChartAttributes || []),
|
||||
[nonLogsAttributes, logsChartAttributes]
|
||||
)
|
||||
|
||||
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,
|
||||
true
|
||||
)
|
||||
|
||||
// Combine all the data into a single dataset
|
||||
const combinedData = useMemo(() => {
|
||||
if (data) return data
|
||||
|
||||
const regularAttributeQueries = attributeQueries.filter(
|
||||
(q) => q.data?.provider !== 'logs' && q.data?.provider !== 'reference-line'
|
||||
)
|
||||
const isLoading =
|
||||
(logsAttributes.length > 0 && isLogsLoading) ||
|
||||
regularAttributeQueries.some((query: any) => query.isLoading)
|
||||
if (isLoading) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const hasError = regularAttributeQueries.some((query: any) => !query.data)
|
||||
if (hasError) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// Get all unique timestamps from all datasets
|
||||
const timestamps = new Set<string>()
|
||||
if (logsData) {
|
||||
@@ -145,17 +88,6 @@ export const useChartData = ({
|
||||
}
|
||||
})
|
||||
}
|
||||
regularAttributeQueries.forEach((query: any) => {
|
||||
query.data?.data?.forEach((point: any) => {
|
||||
if (point?.period_start) {
|
||||
timestamps.add(point.period_start)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const referenceLineQueries = attributeQueries.filter(
|
||||
(q) => q.data?.provider === 'reference-line'
|
||||
)
|
||||
|
||||
// Combine data points for each timestamp
|
||||
const combined = Array.from(timestamps)
|
||||
@@ -166,44 +98,20 @@ export const useChartData = ({
|
||||
const logPoint = logsData?.find((p: any) => p.period_start === timestamp) || {}
|
||||
Object.assign(point, logPoint)
|
||||
|
||||
chartAttributes.forEach((attr) => {
|
||||
if (!attr) return
|
||||
if (attr.provider === 'logs') return
|
||||
if (attr.provider === 'reference-line') return
|
||||
if (attr.customValue !== undefined) {
|
||||
point[attr.attribute] = attr.customValue
|
||||
return
|
||||
}
|
||||
|
||||
const query = regularAttributeQueries.find((q) => q.data?.attribute === attr.attribute)
|
||||
const matchingPoint = query?.data?.data?.find((p: any) => p.period_start === timestamp)
|
||||
point[attr.attribute] = matchingPoint?.[attr.attribute] ?? 0
|
||||
})
|
||||
|
||||
// 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, chartAttributes, isLogsLoading, logsData, logsAttributes])
|
||||
}, [data, attributes, isLogsLoading, logsData, logsAttributes])
|
||||
|
||||
const loading =
|
||||
(logsAttributes.length > 0 && isLogsLoading) ||
|
||||
attributeQueries.some((query: any) => query.isLoading)
|
||||
const loading = logsAttributes.length > 0 && isLogsLoading
|
||||
|
||||
// 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
|
||||
const firstData = logsChartAttributes.find((p: any) => p.attribute === firstAttr.attribute)
|
||||
|
||||
if (!firstData) return undefined
|
||||
|
||||
@@ -222,12 +130,11 @@ export const useChartData = ({
|
||||
: shouldHighlightTotalGroupedValue
|
||||
? firstData.totalGrouped?.[firstAttr.attribute as keyof typeof firstData.totalGrouped]
|
||||
: (firstData.data?.[firstData.data?.length - 1] as any)?.[firstAttr.attribute]
|
||||
}, [highlightedValue, attributes, attributeQueries])
|
||||
}, [highlightedValue, attributes])
|
||||
|
||||
return {
|
||||
data: combinedData,
|
||||
isLoading: loading,
|
||||
chartAttributes,
|
||||
highlightedValue: _highlightedValue,
|
||||
}
|
||||
}
|
||||
@@ -82,14 +82,7 @@ const AuthUsage = () => {
|
||||
setIsRefreshing(true)
|
||||
AUTH_REPORT_ATTRIBUTES.forEach((attr) => {
|
||||
attr.attributes.forEach((subAttr) => {
|
||||
queryClient.invalidateQueries([
|
||||
'auth-metrics',
|
||||
ref,
|
||||
subAttr.attribute,
|
||||
selectedDateRange.period_start.date,
|
||||
selectedDateRange.period_end.date,
|
||||
selectedDateRange.interval,
|
||||
])
|
||||
queryClient.invalidateQueries(['auth-logs-report', 'auth-metrics'])
|
||||
})
|
||||
})
|
||||
refetch()
|
||||
@@ -150,6 +143,7 @@ const AuthUsage = () => {
|
||||
endDate={selectedDateRange?.period_end?.date}
|
||||
updateDateRange={updateDateRange}
|
||||
orgPlanId={orgPlan?.id}
|
||||
isLoading={isRefreshing}
|
||||
/>
|
||||
))}
|
||||
<div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Loader } from 'lucide-react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
|
||||
import styleHandler from '../../lib/theme/styleHandler'
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function Loading({ children, active }: Props) {
|
||||
return (
|
||||
<div className={classNames.join(' ')}>
|
||||
<div className={contentClasses.join(' ')}>{children}</div>
|
||||
{active && <Loader size={24} className={spinnerClasses.join(' ')} />}
|
||||
{active && <Loader2 size={24} className={spinnerClasses.join(' ')} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1417,8 +1417,9 @@ export default {
|
||||
},
|
||||
spinner: `
|
||||
absolute
|
||||
text-brand animate-spin
|
||||
text-foreground-lighter animate-spin
|
||||
inset-0
|
||||
size-5
|
||||
m-auto
|
||||
`,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user