feat: handle errors in analytics (#6245)

* fix: uri too long error by temporarily batching in frontend

* feat: fix empty state with error
This commit is contained in:
Truman Gao
2026-05-29 16:06:04 -06:00
committed by GitHub
parent 02363c27a2
commit 93fe87e57f
3 changed files with 41 additions and 3 deletions
@@ -96,7 +96,7 @@ export function useAnalyticsChartDatasets(
}
return hasAvailableProjects.value
? formatMessage(analyticsMessages.selectAtLeastOneProject)
? formatMessage(analyticsMessages.noDataAvailable)
: formatMessage(analyticsMessages.noProjectsAvailableForAnalytics)
})
const legendPalette = computed(() =>
@@ -21,7 +21,7 @@ const ANALYTICS_START_TIME = new Date(ANALYTICS_START_TIMESTAMP).getTime()
export const REVENUE_MIN_TIMEFRAME_MS = 1 * 24 * 60 * 60 * 1000 // need at least 1 day in timeframe range to show revenue
const ANALYTICS_DAY_MS = 24 * 60 * 60 * 1000
const ANALYTICS_MAX_TIME_SLICES = 256 // controls granularity allowed in "group by" for timeframe ranges
const ANALYTICS_PROJECT_IDS_FETCH_BATCH_SIZE = 2000
const ANALYTICS_PROJECT_IDS_FETCH_BATCH_SIZE = 40
const ANALYTICS_PROJECT_IDS_FETCH_BATCH_DELAY_MS = 300
function isProjectAnalyticsPoint(
@@ -1,5 +1,10 @@
import type { Labrinth } from '@modrinth/api-client'
import { createContext, injectModrinthClient, type ProjectPageContext } from '@modrinth/ui'
import {
createContext,
injectModrinthClient,
injectNotificationManager,
type ProjectPageContext,
} from '@modrinth/ui'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import type { ComputedRef, Ref } from 'vue'
@@ -127,6 +132,26 @@ const ANALYTICS_PREFETCH_GC_TIME_MS = 15 * 1000
const ANALYTICS_FILTER_OPTIONS_GC_TIME_MS = 60 * 1000
const ANALYTICS_MOBILE_LAYOUT_QUERY = '(pointer: coarse), (max-width: 800px)'
function getAnalyticsFetchErrorMessage(error: unknown): string {
if (error && typeof error === 'object') {
const dataDescription = (error as { data?: { description?: unknown } }).data?.description
if (typeof dataDescription === 'string' && dataDescription.length > 0) {
return dataDescription
}
const message = (error as { message?: unknown }).message
if (typeof message === 'string' && message.length > 0) {
return message
}
}
if (typeof error === 'string' && error.length > 0) {
return error
}
return 'Please try refreshing the page or changing your query.'
}
export interface AnalyticsDashboardContextValue {
hasProjectContext: ComputedRef<boolean>
projectGroups: ComputedRef<AnalyticsDashboardProjectGroup[]>
@@ -221,6 +246,7 @@ export function createAnalyticsDashboardContext(
options: CreateAnalyticsDashboardContextOptions,
): AnalyticsDashboardContextValue {
const client = injectModrinthClient()
const { addNotification } = injectNotificationManager()
const queryClient = useQueryClient()
const route = useRoute()
const initialQueryState = readAnalyticsQueryBuilderState(route.query, [])
@@ -851,6 +877,7 @@ export function createAnalyticsDashboardContext(
data: currentAnalyticsData,
isPending: currentTimeSlicePending,
isFetching: currentFetching,
error: currentAnalyticsError,
refetch: refetchCurrentTimeSlices,
} = useQuery({
queryKey: computed(() =>
@@ -876,6 +903,17 @@ export function createAnalyticsDashboardContext(
enabled: computed(() => isAnalyticsFetchRequestReady(analyticsTimeSlicesFetchRequest.value)),
gcTime: ANALYTICS_TIME_SLICES_GC_TIME_MS,
})
watch(currentAnalyticsError, (error) => {
if (!error) {
return
}
addNotification({
title: 'Analytics failed to load',
text: getAnalyticsFetchErrorMessage(error),
type: 'error',
})
})
const isCurrentTimeSliceLoading = computed(
() =>
isAnalyticsFetchRequestReady(analyticsTimeSlicesFetchRequest.value) &&