feat: improve analytics dashboard (#5897)

* feat: implement cancel/apply for custom timeframe range picker

* feat: implement dot for showing todays date

* feat: add max date to be today and show todays date

* feat: if ratio mode, dont show total

* feat: implement show more batching excess lines into "Other" bucket

* refactor: pnpm prepr

* feat: add pick and plop for date range start/end dates

* feat: implement reset query button

* feat: clear button to clear breakdown

* feat: more aggressively trim allowed minimum group by option

* fix: dont show project status filter when from project settings/analytics

* fix: clear selected X above number when appropriate

* feat: graph style updates and dont show year in x axis unless more than 2 year timeframe

* fix: loading state to include legend in blur

* feat: add project icon to project select

* feat: filter out draft projects from analytics

* feat: implement multiselect sections headers, project select org sections, and project options icons

* feat: implement click and drag to select date range

* feat: implement windows history for query builder

* revert: no longer switch breakdown/filter option if same category

* feat: implement showing project for project version breakdown/filter when there are multiple projects

* feat: implement modrinth sided events

* fix: border radius

* feat: implement analytics range highlight

* fix: loading state showing empty state text

* refactor: pnpm prepr

* feat: improve dropdown filter bar and multiselect performance

* fix: multiselect keyboard use

* fix: graph overflow issues

* fix: loading state text on table

* feat: implement tooltip scroll

* fix: adjust charts event tooltip

* feat: shorten time to not repeat am/pm

* feat: implement query params for graph component settings

* fix: qa

* feat: add reset timeframe button

* fix: legend colors moving between metric by determining color based on only downloads metric index

* feat: implement auto switching temporarily to group by day for renvenue metric and disable revenue metric for time range < 2 days

* fix: change to > 1 day

* fix: custom timeframe picker

* feat: implement big performance improvement for table

* feat: implement hover on legend to highlight graph

* fix: defer commit in query builder/filter and style fixes

* feat: more performance optimization to analytics dashboard state, chart, and table

* feat: add tooltip for other item

* feat: improve custom time frame range select

* feat: implement analytics events admin page

* fix: switch column order

* pnpm prepr

* feat: implement mock analytics events

* feat: improve analytics events admin page

* feat: focus title input on analytics create event modal

* fix: remove labels annoying

* feat: hook up analytics events backend

* fix: type error

* feat: reduce combobox padding

* feat: reduce padding on multiselect

* feat: add overlay scrollbar for combobox

* feat: a bunch of style fixes to combobox, multiselect and dropdown filter bar

* feat: MORE PADDING fixes

* feat: use user_agent for download source

* Revert "feat: use user_agent for download source"

This reverts commit d6dc8a99f1.

* fix: query filter project version lag and borked virtualization

* feat: rename breakdown "none" to "project"

* feat: implement right side checkmark for multiselect

* feat: keep crossed out legend items still shown in tooltip but also crossed out

* fix: focus styles

* fix: focus styles pt2

* feat: implement filter by top 8

* fix: preview is incorrect when selecting same date in range date picker

* feat (playtest): cross out legend items in tooltip and allow hide/show in tooltip

* feat (playtest): table component controls what graph shows

* feat: change download source to use user_agent

* feat: fix click to cross out in legend

* feat: add hover legend item to highlight line in tooltip

* fix: export csv to always be dropdown

* feat: implement breakdown = none

* performance: frontend memory reduction

* performance: reduce memory usage from project versions query by keeping only whats necessary

* fix: table checked items not in graph if 0

* feat: add shift click to select a range in table

* performance: add caching for metric types so switching between them is snappy

* performance: batch analytics requests by 15 project ids, with 150 ms delay between, so backend is happy

* feat: add analytics table search

* refactor: pnpm prepr

* fix: query filter options not coming in from analytics fetch

* feat: remove breakdown = none when there are multiple projects

* feat: improve table sorting

* feat: sort projects in project dropdown

* fix: getting project name for project versions

* fix: add loading state for filter and parallel fetch

* performance: use precomputed map for project version options to remove first hover lag

* feat: dropdown filter always open on one side and improve styles

* fix: custom time range picker being weird

* refactor: pnpm prepr

* fix: add back in batch with 300ms interval for projects to prevent backend rate limiting

* performance: only do queries to populate graph first before other analytics queries

* fix: QA polish issues around style and copy

* feat: dont show select all when its just one item in section

* fix: bugs with ratio mode and hiding chart lines

* fix: adjust padding in combobox and multiselect and fix not unfocusing when deselect

* fix: small styles

* fix: polish admin analytic events

* fix: keep scroll position with selection action row appearing when selecting one

* feat: add subheading in graph for showing N items from table

* feat: add unmonetized explaination tooltip

* performance: implement limit on how many lines can be shown in graph

* feat: mobile pass

* refactor: pnpm prepr

* add clear button

* feat: add time in analytics event and normalize date/time so its correct to timezones

* fix: padding

* feat: implement show prev period toggle

* feat: extract TimeFramePicker to packages/ui

* fix: adjust style

* feat: keep table selected persisted in query parameter

* fix: style on prev item value in legend

* fix: when breakdown switches, reset selected series

* fix: tooltip styles

* feat: change project selection to reset to show top 8 only if reconciled down to 0 items

* feat: implement show top 8 button in graph subheading

* fix: rename download type to download reason

* fix: formatting label for table

* feat: persist table sort by and sort direction

* fix: show top 8 button in graph not defaulting to top 8 for other metrics

* feat: implement prev period analytics fetch into the same current period fetch by shifting start date

* refactor: pnpm prepr

* fix: remove number if its just top 1

* fix: brief select items empty state when switch breakdown

* feat: implement format table playtime column

* feat: update export csv filename

* feat: change playtime column to display in hours

* refactor: pnpm prepr

* fix: still download type in filter

* feat: update analytics tooltip

* fix: wrong all projects icon

* feat: force legend order and graph colour for monetization

* refactor: pnpm prepr

* fix: multiselect and combobox sizes

* fix: chart icon add hover delay

* feat: (to playtest) implement multiple breakdowns

* fix: couple UX things for multiple breakdown

* fix: cannot unpin on page click

* fix: multiple breakdown legend and tooltip labels

* feat: add right side checkmark for dropdown filtr bar

* feat: enabling prev period will cross out prev for current ones already crossed out

* feat-mobile: remove drag to select time frame in graph

* feat-mobile: dropdown filter to replace dropdown for submenus on small screen

* feat-mobile: time frame picker to use different start and end date pickers for mobile

* fix-mobile: fix multiselect scroll on mobile

* feat: consolidate is mobile ref into context

* fix-mobile: combobox and multiselect scroll bug when mobile search bar open, fix timeframe picker mobile pick date, and dropdown filter bar click outside to close

* fix-mobile: smaller metric card font

* fix: dropdown filter bar scroll while search

* feat: implement project side events

* feat: implement better mobile view design for query builder

* feat: handle events overflow

* small: add select none

* feat: remove clear project and breakdown

* fix: event icon hover color

* feat: default hide project events if there are multiple projects, and default show if only 1 project

* feat: implement analytics performance updates, including facets, and v3 user projects

* feat: grey out dimmed lined on legend item hover

* feat-mobile: style fixes

* add close on select prop

* feat: add close on select for time frame picker mobile

* feat: date picker default read only

* refactor: pnpm prepr

* feat: default to projects breakdown instead of no breakdown with multiple projects

* fix-mobile: improve graph touch interactions

* small: 2 sig figs on playtime

* feat: deduplicate version uploads that have same version number and are uploaded on same day

* fix: analytics events grouping causing overflow

* feat: improve performance on analytics events grouping

* fix: tooltip expanding page width briefly

* fix: prevent double tap to zoom on inputs

* feat: add click to show chart event for mobile

* fix: toggle not having touch manipulation

* fix: chart tooltip scroll in mobile

* fix: remove project breakdownoption as it is default breakdown when none are selected

* fix: dropdown filter bar briefly empty when switching pages in mobile

* feat: keep tooltip open after drag in mobile

* fix: using plural instead of single for project breakdown

* fix: date picker scrolling page after picking date in mobile by suppressing focus

* fix: callback to Organization instead of org id

* feat: improve chart tooltip date range label formatter to be much more consistent

* feat: tap to toggle event tooltip

* fix: add user select none on graph and fix zoom into download threshold input

* fix: frontend still filtering after backend already filters

* feat: fix emptys state height content shift

* fix: qa issues

* fix: a number of qa issues

- Hide project events based on visible project legend/table selection
- Filter project status events by end status and add explicit copy for approved, private, and unlisted
- Style Modrinth analytics events with blue icon, marker, guide, and range borders
- Add scroll fade shadows to analytics chart and event tooltips
- Show previous-period date range in the chart tooltip
- Make project breakdown conditional on multiple selected projects and allow no breakdown when none are selected
- Add breakdown selection actions and fix “Group by day” copy

* feat: implement graph controls dropdown

* fix date picker typing into time input

* fix: styles in events table

* small: style

* feat: implement using new backend facets route

* feat: implement user get all projects

* performance: deter non-critical fetches to after analytics is in

* fix: refreshing causes multiple projects to do breakdown=none

* performance: cache project version options to fix lag on open sub menu

* refactor: remove chart event height being controlled by parent

* feat: update controls dropdown to have fainter border

* fix: loading bar not fading away

* fix: cannot click in graph

* feat: dont conditionally show multiselect selection actions

* fix: z-index and padding issues

* fix: project events incorrectly toggling on for first page load

* feat: remove show more and show less in legend, always show all

* fix: playtime y axis labels

* feat: improve y axis formatting for playtime and others

* feat: use tabs for game version select, and remove prev period when change breakdown or project selection

* refactor: pnpm prepr

* feat: change hidden legend items to not contribute to ratio percentages

* feat: event icon consume scroll for tooltip panel

* feat: remove gap inside chart tooltip

* feat: add gap for date picker 2 calendar view

* feat: improve analytics events grouping logic for modrinth events to be close to target

* pnpm prepr

* fix: cant click in gap in toggle

* fix: bugs around selected series from table not persisting with timeframe or filter changes

* refactor: kabab case

* refactor: split up large analytics chart and table component files into smaller components and ts modules

* fix: legend is stale after resetting query

* refactor: split up giant analytics provider with utils

* i18n pass

* revert: format number composable change

* fix: playtime was choosing y axis ticks in seconds instead of hours

* refactor: rename folder that with components to match main component name

* refactor: same rename for analytics table for consistency

* refactor: name main components to index.vue and keep folder name as component name

* refactor: pnpm prepr

* refactor: rename types

* refactor: move query builder types into types file and move components into components/analytics-dashboard

* refactor: colocate query builder url with analytics-dashboard component

* refactor: pnpm prepr:frontend

* fix: download threshold not width fit

* fix: no option to see release/all game versions in selected filter dropdown

* fix: game version dropdown width

---------

Signed-off-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
This commit is contained in:
Truman Gao
2026-05-29 19:39:55 +00:00
committed by GitHub
co-authored by Calum H.
parent f49951084e
commit 11b2b6e6c0
100 changed files with 23707 additions and 2981 deletions
+468
View File
@@ -8,6 +8,474 @@
"admin.billing.error.not-found": {
"message": "User not found"
},
"analytics.action.add": {
"message": "Add"
},
"analytics.action.cancel": {
"message": "Cancel"
},
"analytics.action.refresh": {
"message": "Refresh"
},
"analytics.action.reset": {
"message": "Reset"
},
"analytics.breakdown.country": {
"message": "Country"
},
"analytics.breakdown.download-reason": {
"message": "Download reason"
},
"analytics.breakdown.download-source": {
"message": "Download source"
},
"analytics.breakdown.game-version": {
"message": "Game version"
},
"analytics.breakdown.generic": {
"message": "Breakdown"
},
"analytics.breakdown.loader": {
"message": "Loader"
},
"analytics.breakdown.monetization": {
"message": "Monetization"
},
"analytics.breakdown.none.selected": {
"message": "No breakdown"
},
"analytics.breakdown.project": {
"message": "Project"
},
"analytics.breakdown.project-status": {
"message": "Project status"
},
"analytics.breakdown.project-version": {
"message": "Project version"
},
"analytics.breakdown.selected": {
"message": "Breakdown by {breakdown}"
},
"analytics.chart.action.show-all": {
"message": "Show all"
},
"analytics.chart.action.show-limited": {
"message": "Show limited"
},
"analytics.chart.action.show-top-eight": {
"message": "Show top 8"
},
"analytics.chart.axis.playtime-hours": {
"message": "{hours} h"
},
"analytics.chart.controls.active-count": {
"message": "{count} active"
},
"analytics.chart.controls.annotations": {
"message": "Annotations"
},
"analytics.chart.controls.aria": {
"message": "Analytics graph controls, {activeCount}"
},
"analytics.chart.controls.button": {
"message": "Controls"
},
"analytics.chart.controls.dialog-aria": {
"message": "Analytics graph controls"
},
"analytics.chart.controls.display": {
"message": "Display"
},
"analytics.chart.controls.modrinth-events": {
"message": "Modrinth events"
},
"analytics.chart.controls.no-modrinth-events": {
"message": "No Modrinth events in graph."
},
"analytics.chart.controls.no-project-events": {
"message": "No project events in graph."
},
"analytics.chart.controls.previous-period": {
"message": "Previous period"
},
"analytics.chart.controls.project-events": {
"message": "Project events"
},
"analytics.chart.controls.ratio": {
"message": "Ratio"
},
"analytics.chart.empty.select-table-items": {
"message": "Select items from table below to visualize your data."
},
"analytics.chart.events.count-aria": {
"message": "{count, plural, one {# analytics event} other {# analytics events}}"
},
"analytics.chart.events.project-title": {
"message": "<project>{projectName}</project>: {title}"
},
"analytics.chart.events.see-announcement": {
"message": "See announcement"
},
"analytics.chart.legend.monetization-details.aria": {
"message": "View monetized analytics details"
},
"analytics.chart.legend.monetization-details.description": {
"message": "Only views and downloads made through Modrinth count toward monetization, and downloads require users to be logged in."
},
"analytics.chart.legend.monetization-details.title": {
"message": "Monetized analytics details"
},
"analytics.chart.legend.previous-period-suffix": {
"message": "{name} (Prev.)"
},
"analytics.chart.render-limit.description": {
"message": "Showing all selected lines from table may degrade page performance."
},
"analytics.chart.render-limit.header": {
"message": "Show all {count} lines in graph?"
},
"analytics.chart.table-selection.all": {
"message": "Showing all {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table"
},
"analytics.chart.table-selection.count": {
"message": "Showing {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table"
},
"analytics.chart.table-selection.limited": {
"message": "Showing {limit} {itemType, select, project {{limit, plural, one {project} other {projects}}} country {{limit, plural, one {country} other {countries}}} monetization {{limit, plural, one {monetization value} other {monetization values}}} downloadSource {{limit, plural, one {download source} other {download sources}}} downloadReason {{limit, plural, one {download reason} other {download reasons}}} projectVersion {{limit, plural, one {project version} other {project versions}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {game version} other {game versions}}} other {{limit, plural, one {item} other {items}}}} from table"
},
"analytics.chart.table-selection.top": {
"message": "Showing top {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table"
},
"analytics.chart.tooltip.duration.days": {
"message": "{count, plural, one {# day} other {# days}}"
},
"analytics.chart.tooltip.duration.hours": {
"message": "{count, plural, one {# hour} other {# hours}}"
},
"analytics.chart.tooltip.duration.minutes": {
"message": "{count, plural, one {# minute} other {# minutes}}"
},
"analytics.chart.tooltip.hide-entry": {
"message": "Hide {name} in graph"
},
"analytics.chart.tooltip.pinned": {
"message": "Chart tooltip pinned"
},
"analytics.chart.tooltip.pinned-aria": {
"message": "Pinned"
},
"analytics.chart.tooltip.previous-period-short": {
"message": "(prev.)"
},
"analytics.chart.tooltip.show-entry": {
"message": "Show {name} in graph"
},
"analytics.chart.tooltip.total": {
"message": "Total"
},
"analytics.chart.view.area": {
"message": "Area"
},
"analytics.chart.view.bar": {
"message": "Bar"
},
"analytics.chart.view.line": {
"message": "Line"
},
"analytics.download-reason.dependency": {
"message": "Dependency"
},
"analytics.download-reason.modpack": {
"message": "Modpack"
},
"analytics.download-reason.standalone": {
"message": "Standalone"
},
"analytics.download-reason.update": {
"message": "Update"
},
"analytics.download-source.app": {
"message": "Modrinth App"
},
"analytics.download-source.website": {
"message": "Modrinth Website"
},
"analytics.downloads.suffix": {
"message": "downloads"
},
"analytics.empty.no-data": {
"message": "No data available"
},
"analytics.empty.no-data-for-analytics": {
"message": "No data available for analytics"
},
"analytics.empty.no-projects": {
"message": "No projects available"
},
"analytics.empty.no-projects-for-analytics": {
"message": "No projects available for analytics"
},
"analytics.empty.select-project": {
"message": "Select at least one project to view data"
},
"analytics.filter.game-version-type": {
"message": "Game version type"
},
"analytics.filter.game-version-type.all": {
"message": "All"
},
"analytics.filter.game-version-type.release": {
"message": "Release"
},
"analytics.filter.search.countries": {
"message": "Search countries..."
},
"analytics.filter.search.download-sources": {
"message": "Search download sources..."
},
"analytics.filter.search.project-versions": {
"message": "Search project versions..."
},
"analytics.filter.search.versions": {
"message": "Search versions..."
},
"analytics.graph.title.downloads": {
"message": "Downloads Over Time"
},
"analytics.graph.title.playtime": {
"message": "Playtime Over Time"
},
"analytics.graph.title.revenue": {
"message": "Revenue Over Time"
},
"analytics.graph.title.views": {
"message": "Views Over Time"
},
"analytics.group-by.1h": {
"message": "1h"
},
"analytics.group-by.6h": {
"message": "6h"
},
"analytics.group-by.date": {
"message": "Date"
},
"analytics.group-by.day": {
"message": "Day"
},
"analytics.group-by.month": {
"message": "Month"
},
"analytics.group-by.selected.day": {
"message": "Group by day"
},
"analytics.group-by.selected.hour": {
"message": "Group by hour"
},
"analytics.group-by.selected.month": {
"message": "Group by month"
},
"analytics.group-by.selected.six-hours": {
"message": "Group by 6 hours"
},
"analytics.group-by.selected.week": {
"message": "Group by week"
},
"analytics.group-by.selected.year": {
"message": "Group by year"
},
"analytics.group-by.week": {
"message": "Week"
},
"analytics.group-by.year": {
"message": "Year"
},
"analytics.loading.fetching-results": {
"message": "Fetching results..."
},
"analytics.options.loading": {
"message": "Loading..."
},
"analytics.project-event.project-approved": {
"message": "Project approved"
},
"analytics.project-event.project-private": {
"message": "Project set to private"
},
"analytics.project-event.project-status-changed": {
"message": "Project status changed"
},
"analytics.project-event.project-unlisted": {
"message": "Project unlisted"
},
"analytics.project-event.version-released": {
"message": "{version} released"
},
"analytics.project-event.version-uploaded": {
"message": "Version uploaded"
},
"analytics.project-status.approved": {
"message": "Approved"
},
"analytics.project-status.archived": {
"message": "Archived"
},
"analytics.project-status.draft": {
"message": "Draft"
},
"analytics.project-status.other": {
"message": "Other"
},
"analytics.project-status.private": {
"message": "Private"
},
"analytics.project-status.rejected": {
"message": "Rejected"
},
"analytics.project-status.unlisted": {
"message": "Unlisted"
},
"analytics.project-status.withheld": {
"message": "Withheld"
},
"analytics.project.all": {
"message": "All projects"
},
"analytics.project.count": {
"message": "{count, plural, one {# project} other {# projects}}"
},
"analytics.project.icon-alt": {
"message": "{name} Icon"
},
"analytics.project.select": {
"message": "Select projects"
},
"analytics.query.filter.add": {
"message": "Add filter"
},
"analytics.query.label.breakdown": {
"message": "Breakdown:"
},
"analytics.query.label.grouped-by": {
"message": "Grouped by"
},
"analytics.query.label.project": {
"message": "Project:"
},
"analytics.query.label.timeframe": {
"message": "Timeframe:"
},
"analytics.stat.downloads": {
"message": "Downloads"
},
"analytics.stat.playtime": {
"message": "Playtime"
},
"analytics.stat.playtime-hours": {
"message": "{hours} hrs"
},
"analytics.stat.previous-period-comparison": {
"message": "vs prev. period"
},
"analytics.stat.previous-period-comparison-short": {
"message": "vs prev."
},
"analytics.stat.revenue": {
"message": "Revenue"
},
"analytics.stat.revenue-value": {
"message": "${value}"
},
"analytics.stat.unavailable": {
"message": "N/A"
},
"analytics.stat.unavailable-tooltip": {
"message": "Stat unavailable for current query"
},
"analytics.stat.views": {
"message": "Views"
},
"analytics.table.csv.date-range": {
"message": "{start} to {end}"
},
"analytics.table.csv.filename": {
"message": "Modrinth Analytics {breakdown} Breakdown - {dateRange}"
},
"analytics.table.csv.header.playtime-seconds": {
"message": "Playtime (seconds)"
},
"analytics.table.csv.selected-range": {
"message": "Selected Range"
},
"analytics.table.duration.days": {
"message": "{count, plural, one {# day} other {# days}}"
},
"analytics.table.duration.hours": {
"message": "{count, plural, one {# hour} other {# hours}}"
},
"analytics.table.duration.minutes": {
"message": "{count, plural, one {# minute} other {# minutes}}"
},
"analytics.table.empty.no-matching-rows": {
"message": "No matching analytics rows"
},
"analytics.table.export-csv": {
"message": "Export CSV"
},
"analytics.table.export.cumulative": {
"message": "Cumulative"
},
"analytics.table.export.grouped": {
"message": "Grouped by {groupBy}"
},
"analytics.table.pagination.summary": {
"message": "Showing {start} to {end} of {total}"
},
"analytics.table.search.placeholder": {
"message": "Search..."
},
"analytics.threshold.countries-above": {
"message": "Countries above"
},
"analytics.threshold.country-downloads-aria": {
"message": "Country downloads threshold"
},
"analytics.threshold.game-version-downloads-aria": {
"message": "Game version downloads threshold"
},
"analytics.threshold.game-versions-above": {
"message": "Game versions above"
},
"analytics.threshold.project-downloads-aria": {
"message": "Project downloads threshold"
},
"analytics.threshold.project-version-downloads-aria": {
"message": "Project version downloads threshold"
},
"analytics.threshold.project-versions-above": {
"message": "Project versions above"
},
"analytics.threshold.projects-above": {
"message": "Projects above"
},
"analytics.title": {
"message": "Analytics"
},
"analytics.value.monetized": {
"message": "Monetized"
},
"analytics.value.none": {
"message": "None"
},
"analytics.value.other": {
"message": "Other"
},
"analytics.value.unknown": {
"message": "Unknown"
},
"analytics.value.unmonetized": {
"message": "Unmonetized"
},
"app-marketing.download.description": {
"message": "Our desktop app is available across all platforms, choose your desired version."
},