diff --git a/apps/frontend/src/components/ui/charts/ChartDisplay.vue b/apps/frontend/src/components/ui/charts/ChartDisplay.vue index 39564d4cb..5dbb2dbdf 100644 --- a/apps/frontend/src/components/ui/charts/ChartDisplay.vue +++ b/apps/frontend/src/components/ui/charts/ChartDisplay.vue @@ -8,7 +8,7 @@ {{ analytics.error.value }} -
+

Loading analytics...

@@ -315,6 +315,7 @@ import { Card, DropdownSelect, useCompactNumber, + useDebugLogger, useFormatMoney, useFormatNumber, } from '@modrinth/ui' @@ -332,6 +333,7 @@ import { intToRgba, } from '~/utils/analytics.js' +const debug = useDebugLogger('ChartDisplay') const formatNumber = useFormatNumber() const { formatCompactNumber } = useCompactNumber() const formatMoney = useFormatMoney() @@ -339,6 +341,8 @@ const formatMoney = useFormatMoney() const router = useNativeRouter() const theme = useTheme() +debug('setup start', { server: import.meta.server, client: import.meta.client }) + const props = withDefaults( defineProps<{ projects?: any[] @@ -359,6 +363,11 @@ const props = withDefaults( const projects = computed(() => props.projects || []) +debug('projects from props', { + count: projects.value.length, + ids: projects.value.map((p: any) => p.id), +}) + // const selectedChart = ref('downloads') const selectedChart = computed({ get: () => { @@ -438,46 +447,47 @@ const isUsingProjectColors = computed({ }, }) -const startDate = ref(dayjs().startOf('day')) -const endDate = ref(dayjs().endOf('day')) -const timeResolution = ref(30) -const isInitialized = ref(false) +const defaultRange = props.ranges.find( + (r) => r.getLabel([dayjs(), dayjs()]) === 'Previous 30 days', +)! +const initialDates = defaultRange.getDates(dayjs()) + +const internalRange: Ref = ref(defaultRange) +const startDate = ref(initialDates.startDate) +const endDate = ref(initialDates.endDate) +const timeResolution = ref(defaultRange.timeResolution) + +debug('default range initialized', { + range: defaultRange.getLabel([dayjs(), dayjs()]), + startDate: startDate.value.toISOString(), + endDate: endDate.value.toISOString(), + timeResolution: timeResolution.value, +}) onBeforeMount(() => { - // Load cached data and range from localStorage - cache. + debug('onBeforeMount') if (import.meta.client) { const rangeLabel = localStorage.getItem('analyticsSelectedRange') + debug('localStorage range', { rangeLabel }) if (rangeLabel) { - const range = props.ranges.find((r) => r.getLabel([dayjs(), dayjs()]) === rangeLabel)! + const range = props.ranges.find((r) => r.getLabel([dayjs(), dayjs()]) === rangeLabel) - if (range !== undefined) { + if (range) { internalRange.value = range - const ranges = range.getDates(dayjs()) + const dates = range.getDates(dayjs()) timeResolution.value = range.timeResolution - startDate.value = ranges.startDate - endDate.value = ranges.endDate + startDate.value = dates.startDate + endDate.value = dates.endDate + debug('range overridden from localStorage', { + startDate: dates.startDate.toISOString(), + endDate: dates.endDate.toISOString(), + timeResolution: range.timeResolution, + }) } } } }) -onMounted(() => { - if (internalRange.value === null) { - internalRange.value = props.ranges.find( - (r) => r.getLabel([dayjs(), dayjs()]) === 'Previous 30 days', - )! - } - - const ranges = selectedRange.value.getDates(dayjs()) - startDate.value = ranges.startDate - endDate.value = ranges.endDate - timeResolution.value = selectedRange.value.timeResolution - - isInitialized.value = true -}) - -const internalRange: Ref = ref(null as unknown as RangeObject) - const selectedRange = computed({ get: () => { return internalRange.value @@ -499,6 +509,7 @@ const selectedRange = computed({ }, }) +debug('calling useFetchAllAnalytics') const analytics = useFetchAllAnalytics( resetCharts, projects, @@ -507,9 +518,15 @@ const analytics = useFetchAllAnalytics( startDate, endDate, timeResolution, - isInitialized, ) +debug('awaiting analytics.fetch()') +await analytics.fetch() +debug('analytics.fetch() resolved', { + loading: analytics.loading.value, + error: analytics.error.value, +}) + const formattedCategorySubtitle = computed(() => { return ( selectedRange.value?.getLabel([dayjs(startDate.value), dayjs(endDate.value)]) ?? 'Loading...' diff --git a/apps/frontend/src/pages/dashboard/analytics.vue b/apps/frontend/src/pages/dashboard/analytics.vue index 0301240f1..25d837643 100644 --- a/apps/frontend/src/pages/dashboard/analytics.vue +++ b/apps/frontend/src/pages/dashboard/analytics.vue @@ -1,15 +1,23 @@ diff --git a/apps/frontend/src/pages/legal/cmp-info.vue b/apps/frontend/src/pages/legal/cmp-info.vue index f65d3007d..fff4dc340 100644 --- a/apps/frontend/src/pages/legal/cmp-info.vue +++ b/apps/frontend/src/pages/legal/cmp-info.vue @@ -195,7 +195,7 @@ const { data: transparencyInformation } = useQuery({ queryFn: () => client.labrinth.payouts_v3.getPlatformRevenue(), }) -const platformRevenue = computed(() => (transparencyInformation.value as any)?.all_time) +const platformRevenue = computed(() => Number((transparencyInformation.value as any)?.all_time)) const platformRevenueData = computed( () => (transparencyInformation.value as any)?.data?.slice(0, 5) ?? [], ) diff --git a/apps/frontend/src/utils/analytics.js b/apps/frontend/src/utils/analytics.js index aa1a435a3..c516de128 100644 --- a/apps/frontend/src/utils/analytics.js +++ b/apps/frontend/src/utils/analytics.js @@ -1,4 +1,4 @@ -import { injectI18n } from '@modrinth/ui' +import { injectI18n, useDebugLogger } from '@modrinth/ui' import dayjs from 'dayjs' import { computed, ref, watch } from 'vue' @@ -314,8 +314,15 @@ export const useFetchAllAnalytics = ( startDate = ref(dayjs().subtract(30, 'days')), endDate = ref(dayjs()), timeResolution = ref(1440), - isInitialized = ref(false), ) => { + const debug = useDebugLogger('useFetchAllAnalytics') + debug('init', { + projectCount: projects.value?.length, + personalRevenue, + startDate: startDate.value?.toISOString(), + endDate: endDate.value?.toISOString(), + }) + const downloadData = ref(null) const viewData = ref(null) const revenueData = ref(null) @@ -340,7 +347,22 @@ export const useFetchAllAnalytics = ( revenue: processRevAnalytics(revenueData.value, projects.value, theme.active), })) + const buildQuery = () => { + const q = { + start_date: startDate.value.toISOString(), + end_date: endDate.value.toISOString(), + resolution_minutes: timeResolution.value, + } + + if (projects.value?.length) { + q.project_ids = JSON.stringify(projects.value.map((p) => p.id)) + } + + return q + } + const fetchData = async (query) => { + debug('fetchData called', { query }) const normalQuery = new URLSearchParams(query) const revenueQuery = new URLSearchParams(query) @@ -355,6 +377,7 @@ export const useFetchAllAnalytics = ( loading.value = true error.value = null + debug('fetching all 5 endpoints...') const responses = await Promise.all([ useFetchAnalytics(`analytics/downloads?${qs}`), useFetchAnalytics(`analytics/views?${qs}`), @@ -362,16 +385,21 @@ export const useFetchAllAnalytics = ( useFetchAnalytics(`analytics/countries/downloads?${qs}`), useFetchAnalytics(`analytics/countries/views?${qs}`), ]) + debug('all 5 endpoints resolved', { + downloads: Object.keys(responses[0] || {}).length, + views: Object.keys(responses[1] || {}).length, + revenue: Object.keys(responses[2] || {}).length, + }) - // collect project ids from projects.value into a set const projectIds = new Set() if (projects.value) { projects.value.forEach((p) => projectIds.add(p.id)) } else { - // if projects.value is not set, we assume that we want all project ids Object.keys(responses[0] || {}).forEach((id) => projectIds.add(id)) } + debug('filtering to projectIds', { count: projectIds.size }) + const filterProjectIds = (data) => { const filtered = {} Object.entries(data).forEach(([id, values]) => { @@ -389,43 +417,27 @@ export const useFetchAllAnalytics = ( downloadsByCountry.value = responses[3] || {} viewsByCountry.value = responses[4] || {} } catch (e) { + debug('fetchData error', e) error.value = e } finally { loading.value = false + debug('fetchData done, loading=false') + } + } + + const fetch = async () => { + debug('fetch() called', { projectCount: projects.value?.length }) + await fetchData(buildQuery()) + if (onDataRefresh) { + onDataRefresh() } } watch( - [ - () => startDate.value, - () => endDate.value, - () => timeResolution.value, - () => projects.value, - () => isInitialized.value, - ], - async () => { - if (!isInitialized.value) { - return - } - - const q = { - start_date: startDate.value.toISOString(), - end_date: endDate.value.toISOString(), - resolution_minutes: timeResolution.value, - } - - if (projects.value?.length) { - q.project_ids = JSON.stringify(projects.value.map((p) => p.id)) - } - - await fetchData(q) - - if (onDataRefresh) { - onDataRefresh() - } - }, - { - immediate: true, + [() => startDate.value, () => endDate.value, () => timeResolution.value, () => projects.value], + (newVals, oldVals) => { + debug('watch triggered', { new: newVals, old: oldVals }) + fetch() }, ) @@ -474,6 +486,6 @@ export const useFetchAllAnalytics = ( totalData, loading, error, - isInitialized, + fetch, } }