From 93fe87e57fd1c8cb60a7f8fade03291f3b2ae63b Mon Sep 17 00:00:00 2001 From: Truman Gao <106889354+tdgao@users.noreply.github.com> Date: Fri, 29 May 2026 16:06:04 -0600 Subject: [PATCH] feat: handle errors in analytics (#6245) * fix: uri too long error by temporarily batching in frontend * feat: fix empty state with error --- .../use-analytics-chart-datasets.ts | 2 +- .../analytics/analytics-data-utils.ts | 2 +- .../src/providers/analytics/analytics.ts | 40 ++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/analytics-dashboard/analytics-chart/use-analytics-chart-datasets.ts b/apps/frontend/src/components/analytics-dashboard/analytics-chart/use-analytics-chart-datasets.ts index 38d908626..be1cb6091 100644 --- a/apps/frontend/src/components/analytics-dashboard/analytics-chart/use-analytics-chart-datasets.ts +++ b/apps/frontend/src/components/analytics-dashboard/analytics-chart/use-analytics-chart-datasets.ts @@ -96,7 +96,7 @@ export function useAnalyticsChartDatasets( } return hasAvailableProjects.value - ? formatMessage(analyticsMessages.selectAtLeastOneProject) + ? formatMessage(analyticsMessages.noDataAvailable) : formatMessage(analyticsMessages.noProjectsAvailableForAnalytics) }) const legendPalette = computed(() => diff --git a/apps/frontend/src/providers/analytics/analytics-data-utils.ts b/apps/frontend/src/providers/analytics/analytics-data-utils.ts index f78d89499..260bbf776 100644 --- a/apps/frontend/src/providers/analytics/analytics-data-utils.ts +++ b/apps/frontend/src/providers/analytics/analytics-data-utils.ts @@ -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( diff --git a/apps/frontend/src/providers/analytics/analytics.ts b/apps/frontend/src/providers/analytics/analytics.ts index dcb3f63a9..3db0e3f30 100644 --- a/apps/frontend/src/providers/analytics/analytics.ts +++ b/apps/frontend/src/providers/analytics/analytics.ts @@ -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 projectGroups: ComputedRef @@ -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) &&