diff --git a/apps/frontend/src/components/analytics-dashboard/analytics-chart/analytics-chart-plot/index.vue b/apps/frontend/src/components/analytics-dashboard/analytics-chart/analytics-chart-plot/index.vue index f12b7159af..f9639f742f 100644 --- a/apps/frontend/src/components/analytics-dashboard/analytics-chart/analytics-chart-plot/index.vue +++ b/apps/frontend/src/components/analytics-dashboard/analytics-chart/analytics-chart-plot/index.vue @@ -168,6 +168,14 @@ const { onRangeSelected: (start, end, groupBy) => emit('range-select', start, end, groupBy), }) +function getTooltipTotalMetricValue(value: number): number { + if (props.activeStat === 'revenue' && Math.abs(value) < 1) { + return Math.round(value * 100) / 100 + } + + return value +} + const hoverTotalValue = computed(() => { if (hoverState.sliceIndex === null) return 0 const sliceIndex = hoverState.sliceIndex @@ -176,7 +184,7 @@ const hoverTotalValue = computed(() => { return props.currentLegendEntries.reduce((sum, legendEntry) => { if (legendEntry.hidden) return sum const dataset = props.chartDatasetById.get(legendEntry.id) - return sum + (dataset?.data[sliceIndex] ?? 0) + return sum + getTooltipTotalMetricValue(dataset?.data[sliceIndex] ?? 0) }, 0) }) diff --git a/apps/frontend/src/components/analytics-dashboard/analytics-messages.ts b/apps/frontend/src/components/analytics-dashboard/analytics-messages.ts index e16df0e504..2fd92fc532 100644 --- a/apps/frontend/src/components/analytics-dashboard/analytics-messages.ts +++ b/apps/frontend/src/components/analytics-dashboard/analytics-messages.ts @@ -39,6 +39,14 @@ export const analyticsMessages = defineMessages({ id: 'analytics.project.all', defaultMessage: 'All projects', }, + yourProjects: { + id: 'analytics.project.your', + defaultMessage: 'Your projects', + }, + userProjects: { + id: 'analytics.project.user', + defaultMessage: "{username}'s projects", + }, selectProjects: { id: 'analytics.project.select', defaultMessage: 'Select projects', diff --git a/apps/frontend/src/components/analytics-dashboard/analytics-route-query.ts b/apps/frontend/src/components/analytics-dashboard/analytics-route-query.ts index c3df9e8e0b..8fdff69009 100644 --- a/apps/frontend/src/components/analytics-dashboard/analytics-route-query.ts +++ b/apps/frontend/src/components/analytics-dashboard/analytics-route-query.ts @@ -146,6 +146,7 @@ const QUERY_KEY_TABLE_SORT = 'a_table_sort' const QUERY_KEY_TABLE_SORT_DIRECTION = 'a_table_sort_direction' const QUERY_KEY_LEGACY_GRAPH_TOP_BREAKDOWN_FILTER = 'a_top_breakdown' const QUERY_KEY_LEGACY_GRAPH_LEGEND_EXPANSION = 'a_legend_expanded' +const PROJECT_SELECTION_ALL_QUERY_VALUE = 'all' const URL_FILTER_CATEGORIES: Exclude[] = [ 'project_status', @@ -405,9 +406,10 @@ export function buildDefaultAnalyticsGraphState( export function buildDefaultAnalyticsQueryBuilderState( availableProjectIds: string[], + defaultProjectIds: string[] = availableProjectIds, ): AnalyticsQueryBuilderState { return { - selectedProjectIds: [...availableProjectIds], + selectedProjectIds: [...defaultProjectIds], selectedTimeframeMode: DEFAULT_TIMEFRAME_MODE, selectedTimeframe: DEFAULT_TIMEFRAME_PRESET, selectedLastTimeframeAmount: DEFAULT_LAST_TIMEFRAME_AMOUNT, @@ -415,7 +417,7 @@ export function buildDefaultAnalyticsQueryBuilderState( selectedCustomTimeframeStartDate: getDefaultCustomStartDate(), selectedCustomTimeframeEndDate: getDefaultCustomEndDate(), selectedGroupBy: DEFAULT_GROUP_BY_PRESET, - selectedBreakdowns: getDefaultAnalyticsBreakdownPresets(availableProjectIds), + selectedBreakdowns: getDefaultAnalyticsBreakdownPresets(defaultProjectIds), selectedFilters: buildEmptySelectedFilters(), } } @@ -475,12 +477,16 @@ export function getAnalyticsBreakdownPresetForProjectSelection( export function isAnalyticsQueryBuilderStateDefault( state: AnalyticsQueryBuilderState, availableProjectIds: string[], + defaultProjectIds: string[] = availableProjectIds, ): boolean { - const defaultState = buildDefaultAnalyticsQueryBuilderState(availableProjectIds) + const defaultState = buildDefaultAnalyticsQueryBuilderState( + availableProjectIds, + defaultProjectIds, + ) const areDefaultProjectsSelected = - availableProjectIds.length === 0 + defaultProjectIds.length === 0 ? state.selectedProjectIds.length === 0 - : areAllProjectsSelected(state.selectedProjectIds, availableProjectIds) + : areAllProjectsSelected(state.selectedProjectIds, defaultProjectIds) return ( areDefaultProjectsSelected && @@ -666,13 +672,19 @@ export function readAnalyticsTableSortState( export function readAnalyticsQueryBuilderState( query: LocationQuery, availableProjectIds: string[], + defaultProjectIds: string[] = availableProjectIds, ): AnalyticsQueryBuilderState { - const defaultState = buildDefaultAnalyticsQueryBuilderState(availableProjectIds) + const defaultState = buildDefaultAnalyticsQueryBuilderState( + availableProjectIds, + defaultProjectIds, + ) const selectedProjectIdsFromQuery = parseListQueryValue(query[QUERY_KEY_PROJECT_IDS]) - const selectedProjectIds = - selectedProjectIdsFromQuery.length > 0 - ? selectedProjectIdsFromQuery - : defaultState.selectedProjectIds + let selectedProjectIds = defaultState.selectedProjectIds + if (selectedProjectIdsFromQuery.includes(PROJECT_SELECTION_ALL_QUERY_VALUE)) { + selectedProjectIds = [...availableProjectIds] + } else if (selectedProjectIdsFromQuery.length > 0) { + selectedProjectIds = selectedProjectIdsFromQuery + } const selectedFilters = buildEmptySelectedFilters() for (const category of URL_FILTER_CATEGORIES) { @@ -779,14 +791,17 @@ export function buildAnalyticsQueryBuilderRouteQuery( state: AnalyticsQueryBuilderState, availableProjectIds: string[], graphState?: AnalyticsGraphState, + defaultProjectIds: string[] = availableProjectIds, ): MutableRouteQuery { const nextRouteQuery = { ...currentRouteQuery, } as MutableRouteQuery - const projectIdsQueryValue = areAllProjectsSelected(state.selectedProjectIds, availableProjectIds) + const projectIdsQueryValue = areAllProjectsSelected(state.selectedProjectIds, defaultProjectIds) ? undefined - : serializeListQueryValue(state.selectedProjectIds) + : areAllProjectsSelected(state.selectedProjectIds, availableProjectIds) + ? PROJECT_SELECTION_ALL_QUERY_VALUE + : serializeListQueryValue(state.selectedProjectIds) const isCustomTimeframeMode = state.selectedTimeframeMode === 'custom_range' || state.selectedTimeframeMode === 'custom_datetime_range' diff --git a/apps/frontend/src/components/analytics-dashboard/query-builder/index.vue b/apps/frontend/src/components/analytics-dashboard/query-builder/index.vue index 7588611ee6..994891a130 100644 --- a/apps/frontend/src/components/analytics-dashboard/query-builder/index.vue +++ b/apps/frontend/src/components/analytics-dashboard/query-builder/index.vue @@ -69,6 +69,29 @@