Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Labrinth } from '@modrinth/api-client'

import {
type AnalyticsBreakdownGroup,
type AnalyticsBreakdownPreset,
type AnalyticsDashboardProject,
type AnalyticsDashboardStat,
Expand All @@ -17,6 +18,7 @@ import {
analyticsStatCardMessages,
formatAnalyticsDependentProjectFallbackLabel,
formatAnalyticsDownloadReasonLabel,
formatAnalyticsDownloadSourceLabel,
formatAnalyticsLoaderLabel,
formatAnalyticsMonetizationLabel,
} from '../analytics-messages'
Expand All @@ -30,6 +32,10 @@ import {
isUnknownAnalyticsBreakdownValue,
UNKNOWN_BREAKDOWN_VALUE,
} from '../breakdown'
import {
applyAnalyticsBreakdownGroup,
getAnalyticsBreakdownGroupSeriesName,
} from '../breakdown-groups'
import { PREVIOUS_PERIOD_DATASET_ID_PREFIX } from './analytics-chart-constants'

export type ChartDataset = {
Expand Down Expand Up @@ -168,6 +174,9 @@ export function formatBreakdownLabel(
if (selectedBreakdown === 'download_reason') {
return formatAnalyticsDownloadReasonLabel(normalizedLowercaseValue, formatMessage)
}
if (selectedBreakdown === 'user_agent') {
return formatAnalyticsDownloadSourceLabel(normalizedValue, formatMessage)
}
if (selectedBreakdown === 'dependent_project_download') {
if (isNoDependentAnalyticsBreakdownValue(breakdownValue)) {
return formatMessage(analyticsMessages.noDependent)
Expand Down Expand Up @@ -412,6 +421,7 @@ export function buildChartDatasets(
getVersionProjectName: ((versionId: string) => string | undefined) | undefined,
formatMessage: FormatMessage,
sliceCount: number = timeSlices.length,
breakdownGroup?: AnalyticsBreakdownGroup | null,
): ChartDataset[] {
const selectedProjectIds = new Set(selectedProjects.map((project) => project.id))
if (selectedProjectIds.size === 0) {
Expand All @@ -428,6 +438,14 @@ export function buildChartDatasets(
return collapseRepeatedUnknownBreakdownLabels(
normalizedBreakdowns.map((breakdown, index) => {
const breakdownValue = breakdownValues[index] ?? ''
if (breakdown === breakdownGroup?.breakdown) {
const seriesName = getAnalyticsBreakdownGroupSeriesName(
breakdownValue,
breakdownGroup,
formatMessage(analyticsMessages.other),
)
if (seriesName) return seriesName
}
if (breakdown === 'project' || breakdown === 'dependent_project_download') {
if (
breakdown === 'dependent_project_download' &&
Expand Down Expand Up @@ -464,7 +482,7 @@ export function buildChartDatasets(

if (
normalizedBreakdowns.length > 0 &&
!(normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'project')
!(normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'project' && !breakdownGroup)
) {
const hasVersionBreakdown = normalizedBreakdowns.includes('version_id')
const hasDependentProjectBreakdown = normalizedBreakdowns.includes('dependent_project_download')
Expand All @@ -488,10 +506,10 @@ export function buildChartDatasets(
continue
}

const breakdownValues = getAnalyticsBreakdownValues(
point,
const breakdownValues = applyAnalyticsBreakdownGroup(
getAnalyticsBreakdownValues(point, normalizedBreakdowns, formatMessage),
normalizedBreakdowns,
formatMessage,
breakdownGroup,
)
if (breakdownValues.some((breakdownValue) => breakdownValue === ALL_BREAKDOWN_VALUE)) {
continue
Expand Down Expand Up @@ -543,11 +561,14 @@ export function buildChartDatasets(
'dependent_project_download',
)
const versionName =
hasVersionBreakdown && versionBreakdownIndex !== -1
hasVersionBreakdown &&
versionBreakdownIndex !== -1 &&
breakdownGroup?.breakdown !== 'version_id'
? getVersionDisplayName?.(breakdownValues[versionBreakdownIndex] ?? '')
: undefined
const dependentProjectId =
dependentProjectBreakdownIndex !== -1
dependentProjectBreakdownIndex !== -1 &&
breakdownGroup?.breakdown !== 'dependent_project_download'
? breakdownValues[dependentProjectBreakdownIndex]
: undefined
const dependentProjectName = dependentProjectId
Expand All @@ -556,7 +577,9 @@ export function buildChartDatasets(
: (projectNamesById.get(dependentProjectId) ?? dependentProjectId)
: undefined
const versionProjectName =
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
normalizedBreakdowns.length === 1 &&
normalizedBreakdowns[0] === 'version_id' &&
breakdownGroup?.breakdown !== 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined
const dependencyProjectNames = [...(dependentOnProjectIdsByBreakdown.get(breakdownKey) ?? [])]
Expand All @@ -575,7 +598,7 @@ export function buildChartDatasets(
)
: undefined
const color =
normalizedBreakdowns.length === 1
normalizedBreakdowns.length === 1 && !breakdownGroup
? getBreakdownColor(
breakdownValues[0] ?? '',
normalizedBreakdowns[0],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { computed, type ComputedRef, ref, watch } from 'vue'
import { useTheme } from '~/composables/nuxt-accessors'
import { isDarkTheme } from '~/plugins/theme/index.ts'
import type {
AnalyticsBreakdownGroup,
AnalyticsBreakdownPreset,
AnalyticsDashboardContextValue,
AnalyticsDashboardProject,
Expand Down Expand Up @@ -51,6 +52,7 @@ export function useAnalyticsChartDatasets(
| 'displayedSelectedGroupBy'
| 'displayedSelectedBreakdowns'
| 'displayedSelectedFilters'
| 'resolvedActiveBreakdownGroup'
| 'hiddenGraphDatasetIds'
| 'hasExplicitGraphDatasetSelection'
| 'isGraphDatasetSelectionActive'
Expand Down Expand Up @@ -175,6 +177,7 @@ export function useAnalyticsChartDatasets(
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
formatMessage,
sliceCount.value,
context.resolvedActiveBreakdownGroup.value,
),
)
const previousChartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() =>
Expand All @@ -191,6 +194,7 @@ export function useAnalyticsChartDatasets(
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
formatMessage,
sliceCount.value,
context.resolvedActiveBreakdownGroup.value,
),
)
const allChartDatasets = computed(() => chartDatasetsByStat.value[context.activeStat.value])
Expand Down Expand Up @@ -384,6 +388,7 @@ function buildDatasetsByStat(
getVersionProjectName: ((versionId: string) => string | undefined) | undefined,
formatMessage: FormatMessage,
sliceCount: number,
breakdownGroup: AnalyticsBreakdownGroup | null,
) {
const datasetsByStat = {} as Record<AnalyticsDashboardStat, ChartDataset[]>
for (const stat of ANALYTICS_DASHBOARD_STATS) {
Expand All @@ -401,6 +406,7 @@ function buildDatasetsByStat(
getVersionProjectName,
formatMessage,
sliceCount,
breakdownGroup,
)
}
return datasetsByStat
Expand Down
104 changes: 104 additions & 0 deletions apps/frontend/src/components/analytics-dashboard/analytics-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ export const analyticsMessages = defineMessages({
id: 'analytics.action.refresh',
defaultMessage: 'Refresh',
},
saveButton: {
id: 'analytics.action.save',
defaultMessage: 'Save',
},
cancelButton: {
id: 'analytics.action.cancel',
defaultMessage: 'Cancel',
},
fetchingResults: {
id: 'analytics.loading.fetching-results',
defaultMessage: 'Fetching results...',
Expand Down Expand Up @@ -129,6 +137,102 @@ export const analyticsMessages = defineMessages({
id: 'analytics.query.label.breakdown',
defaultMessage: 'Breakdown:',
},
breakdownGroupsButton: {
id: 'analytics.breakdown-groups.button',
defaultMessage: 'Group series',
},
breakdownGroupsNoGrouping: {
id: 'analytics.breakdown-groups.none',
defaultMessage: 'No grouping',
},
breakdownGroupsCreate: {
id: 'analytics.breakdown-groups.create',
defaultMessage: 'Create group',
},
breakdownGroupsEdit: {
id: 'analytics.breakdown-groups.edit',
defaultMessage: 'Edit group',
},
breakdownGroupsDelete: {
id: 'analytics.breakdown-groups.delete',
defaultMessage: 'Delete group',
},
breakdownGroupsDeleteTitle: {
id: 'analytics.breakdown-groups.delete-title',
defaultMessage: 'Delete {name}?',
},
breakdownGroupsDeleteDescription: {
id: 'analytics.breakdown-groups.delete-description',
defaultMessage: 'This saved breakdown group will be permanently deleted from this browser.',
},
breakdownGroupsModalCreateTitle: {
id: 'analytics.breakdown-groups.modal.create-title',
defaultMessage: 'Create breakdown group',
},
breakdownGroupsModalEditTitle: {
id: 'analytics.breakdown-groups.modal.edit-title',
defaultMessage: 'Edit breakdown group',
},
breakdownGroupsGroupName: {
id: 'analytics.breakdown-groups.group-name',
defaultMessage: 'Group name',
},
breakdownGroupsGroupNamePlaceholder: {
id: 'analytics.breakdown-groups.group-name-placeholder',
defaultMessage: 'For example, Platform families',
},
breakdownGroupsSeriesName: {
id: 'analytics.breakdown-groups.series-name',
defaultMessage: 'Series name',
},
breakdownGroupsSeriesValues: {
id: 'analytics.breakdown-groups.series-values',
defaultMessage: 'Breakdown values',
},
breakdownGroupsSelectValues: {
id: 'analytics.breakdown-groups.select-values',
defaultMessage: 'Select values',
},
breakdownGroupsAddSeries: {
id: 'analytics.breakdown-groups.add-series',
defaultMessage: 'Add series',
},
breakdownGroupsRemoveSeries: {
id: 'analytics.breakdown-groups.remove-series',
defaultMessage: 'Remove series',
},
breakdownGroupsOtherDescription: {
id: 'analytics.breakdown-groups.other-description',
defaultMessage: '{count, plural, one {# unassigned value} other {# unassigned values}}',
},
breakdownGroupsUnavailableValue: {
id: 'analytics.breakdown-groups.unavailable-value',
defaultMessage: '{value} (not in current results)',
},
breakdownGroupsNameRequired: {
id: 'analytics.breakdown-groups.validation.name-required',
defaultMessage: 'Enter a group name.',
},
breakdownGroupsNameDuplicate: {
id: 'analytics.breakdown-groups.validation.name-duplicate',
defaultMessage: 'A group with this name already exists for this breakdown.',
},
breakdownGroupsSeriesRequired: {
id: 'analytics.breakdown-groups.validation.series-required',
defaultMessage: 'Add at least one custom series.',
},
breakdownGroupsSeriesNameRequired: {
id: 'analytics.breakdown-groups.validation.series-name-required',
defaultMessage: 'Every custom series needs a name.',
},
breakdownGroupsSeriesNameDuplicate: {
id: 'analytics.breakdown-groups.validation.series-name-duplicate',
defaultMessage: 'Custom series names must be unique.',
},
breakdownGroupsSeriesValuesRequired: {
id: 'analytics.breakdown-groups.validation.series-values-required',
defaultMessage: 'Assign at least one breakdown value to every custom series.',
},
addFilterButton: {
id: 'analytics.query.filter.add',
defaultMessage: 'Add filter',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { TableColumn } from '@modrinth/ui'

import type {
AnalyticsBreakdownGroup,
AnalyticsBreakdownPreset,
AnalyticsDashboardStat,
AnalyticsSelectedFilters,
Expand All @@ -23,6 +24,7 @@ type BuildAnalyticsTableColumnsOptions = {
selectedBreakdowns: readonly AnalyticsTableBreakdownPreset[]
selectedFilters: AnalyticsSelectedFilters
showBreakdownColumn: boolean
breakdownGroup: AnalyticsBreakdownGroup | null
formatMessage: FormatMessage
getRelevantAnalyticsDashboardStats: (
breakdowns: readonly AnalyticsBreakdownPreset[],
Expand All @@ -42,6 +44,7 @@ export function buildAnalyticsTableColumns({
selectedBreakdowns,
selectedFilters,
showBreakdownColumn,
breakdownGroup,
formatMessage,
getRelevantAnalyticsDashboardStats,
}: BuildAnalyticsTableColumnsOptions): TableColumn<AnalyticsTableColumnKey>[] {
Expand All @@ -62,7 +65,10 @@ export function buildAnalyticsTableColumns({
for (const breakdown of selectedBreakdowns) {
nextColumns.push({
key: getAnalyticsTableBreakdownColumnKey(breakdown),
label: getAnalyticsTableBreakdownColumnLabel(breakdown, formatMessage),
label:
breakdownGroup?.breakdown === breakdown
? breakdownGroup.name
: getAnalyticsTableBreakdownColumnLabel(breakdown, formatMessage),
enableSorting: true,
width: breakdown === 'project' && selectedBreakdowns.length === 1 ? '45%' : undefined,
})
Expand Down
Loading
Loading