diff --git a/frontend/src/metabase/lib/analytics.js b/frontend/src/metabase/lib/analytics.js index 4c6028db7ba69d79bec89ea12f914e41245007d7..95d349604dbc93dec485b040c1ffc9614153251a 100644 --- a/frontend/src/metabase/lib/analytics.js +++ b/frontend/src/metabase/lib/analytics.js @@ -1,4 +1,5 @@ /*global ga*/ +/* @flow */ import MetabaseSettings from "metabase/lib/settings"; @@ -8,13 +9,14 @@ import { DEBUG } from "metabase/lib/debug"; // Simple module for in-app analytics. Currently sends data to GA but could be extended to anything else. const MetabaseAnalytics = { // track a pageview (a.k.a. route change) - trackPageView: function(url) { + trackPageView: function(url: string) { if (url) { // scrub query builder urls to remove serialized json queries from path url = (url.lastIndexOf('/q/', 0) === 0) ? '/q/' : url; const { tag } = MetabaseSettings.get('version'); + // $FlowFixMe ga('set', 'dimension1', tag); ga('set', 'page', url); ga('send', 'pageview', url); @@ -22,11 +24,12 @@ const MetabaseAnalytics = { }, // track an event - trackEvent: function(category, action, label, value) { + trackEvent: function(category: string, action?: string, label?: string, value?: number) { const { tag } = MetabaseSettings.get('version'); // category & action are required, rest are optional if (category && action) { + // $FlowFixMe ga('set', 'dimension1', tag); ga('send', 'event', category, action, label, value); } @@ -40,6 +43,7 @@ export default MetabaseAnalytics; export function registerAnalyticsClickListener() { + // $FlowFixMe document.body.addEventListener("click", function(e) { var node = e.target; diff --git a/frontend/src/metabase/meta/types/Visualization.js b/frontend/src/metabase/meta/types/Visualization.js index b191504a37be136bf07ac649b06956610ca3a220..e172b2037c269d320e2fa536e560514346cb0744 100644 --- a/frontend/src/metabase/meta/types/Visualization.js +++ b/frontend/src/metabase/meta/types/Visualization.js @@ -40,7 +40,10 @@ export type ClickAction = { title: any, // React Element icon?: string, popover?: (props: ClickActionPopoverProps) => any, // React Element - card?: () => ?Card + card?: () => ?Card, + + section?: string, + name?: string, } export type ClickActionProps = { diff --git a/frontend/src/metabase/qb/components/actions/CommonMetricsAction.jsx b/frontend/src/metabase/qb/components/actions/CommonMetricsAction.jsx index 1a6db1e3dab125ebe0d79c277996e2c18cc405d7..63450c1325586739f3e1eb02bbb69407f2a9591f 100644 --- a/frontend/src/metabase/qb/components/actions/CommonMetricsAction.jsx +++ b/frontend/src/metabase/qb/components/actions/CommonMetricsAction.jsx @@ -11,6 +11,7 @@ import { summarize } from "metabase/qb/lib/actions"; export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => { return tableMetadata.metrics.slice(0, 5).map(metric => ({ + name: "common-metric", title: <span>View <strong>{metric.name}</strong></span>, card: () => summarize(card, ["METRIC", metric.id], tableMetadata) })); diff --git a/frontend/src/metabase/qb/components/actions/CountByTimeAction.jsx b/frontend/src/metabase/qb/components/actions/CountByTimeAction.jsx index d5fc67592ccf0b090f32f781c3d38f44e20b5f84..fb74354a33ce3d3d11ff1d26b8237bbadd457f8b 100644 --- a/frontend/src/metabase/qb/components/actions/CountByTimeAction.jsx +++ b/frontend/src/metabase/qb/components/actions/CountByTimeAction.jsx @@ -18,8 +18,9 @@ export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => { return [ { - title: <span>Count of rows by time</span>, + name: "count-by-time", section: "sum", + title: <span>Count of rows by time</span>, icon: "line", card: () => breakout( diff --git a/frontend/src/metabase/qb/components/actions/PivotByAction.jsx b/frontend/src/metabase/qb/components/actions/PivotByAction.jsx index 39db4beb6a4c788c0f78004fb8314ac5ab6f95c7..a26f75a88c74bcb73115d693338025dbe63f935f 100644 --- a/frontend/src/metabase/qb/components/actions/PivotByAction.jsx +++ b/frontend/src/metabase/qb/components/actions/PivotByAction.jsx @@ -67,6 +67,7 @@ export default (name: string, icon: string, fieldFilter: FieldFilter) => return [ { + name: "pivot-by-" + name.toLowerCase(), section: "breakout", title: clicked ? name diff --git a/frontend/src/metabase/qb/components/actions/PlotSegmentField.jsx b/frontend/src/metabase/qb/components/actions/PlotSegmentField.jsx index ce060112ff419c2af51a103f89f5727f20ffd701..da28dcd54396d86db87d0a9080041bdf31eb1c7a 100644 --- a/frontend/src/metabase/qb/components/actions/PlotSegmentField.jsx +++ b/frontend/src/metabase/qb/components/actions/PlotSegmentField.jsx @@ -13,6 +13,7 @@ export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => { } return [ { + name: "plot", title: "Plot a field in this segment", icon: "bar", card: () => plotSegmentField(card) diff --git a/frontend/src/metabase/qb/components/actions/SummarizeBySegmentMetricAction.jsx b/frontend/src/metabase/qb/components/actions/SummarizeBySegmentMetricAction.jsx index e9c77535fe77dffdae6111e94ec022c2f2a6b11d..851400885e1aa61b750b3a204911d6c1c8afa231 100644 --- a/frontend/src/metabase/qb/components/actions/SummarizeBySegmentMetricAction.jsx +++ b/frontend/src/metabase/qb/components/actions/SummarizeBySegmentMetricAction.jsx @@ -22,6 +22,7 @@ export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => { return [ { + name: "summarize", title: "Summarize this segment", icon: "sum", // eslint-disable-next-line react/display-name diff --git a/frontend/src/metabase/qb/components/actions/UnderlyingDataAction.jsx b/frontend/src/metabase/qb/components/actions/UnderlyingDataAction.jsx index 572e7441a75569b78c23ba191947e9c900695164..871d440cbc4883d28cb1130548188f89b8efcb8d 100644 --- a/frontend/src/metabase/qb/components/actions/UnderlyingDataAction.jsx +++ b/frontend/src/metabase/qb/components/actions/UnderlyingDataAction.jsx @@ -11,6 +11,7 @@ export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => { if (card.display !== "table" && card.display !== "scalar") { return [ { + name: "underlying-data", title: "View this as a table", icon: "table", card: () => toUnderlyingData(card) diff --git a/frontend/src/metabase/qb/components/actions/UnderlyingRecordsAction.jsx b/frontend/src/metabase/qb/components/actions/UnderlyingRecordsAction.jsx index 269ae5162e3d86442a1efc9d0370556dd2986270..95fb1c00aac1833b993fb5f9c37a08dbc2f69694 100644 --- a/frontend/src/metabase/qb/components/actions/UnderlyingRecordsAction.jsx +++ b/frontend/src/metabase/qb/components/actions/UnderlyingRecordsAction.jsx @@ -16,6 +16,7 @@ export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => { if (query && !Query.isBareRows(query)) { return [ { + name: "underlying-records", title: ( <span> View the underlying diff --git a/frontend/src/metabase/qb/components/drill/CountByColumnDrill.js b/frontend/src/metabase/qb/components/drill/CountByColumnDrill.js index 791983ad0f057dc51db5bf719104c096fc5cd1d4..f755db6470fbc9d17232b1956f1d40b54dc51233 100644 --- a/frontend/src/metabase/qb/components/drill/CountByColumnDrill.js +++ b/frontend/src/metabase/qb/components/drill/CountByColumnDrill.js @@ -34,8 +34,9 @@ export default ( return [ { - title: <span>Distribution</span>, + name: "count-by-column", section: "distribution", + title: <span>Distribution</span>, card: () => pivot( summarize(card, ["count"], tableMetadata), diff --git a/frontend/src/metabase/qb/components/drill/ObjectDetailDrill.jsx b/frontend/src/metabase/qb/components/drill/ObjectDetailDrill.jsx index dee0baaa58707dd28172aa569522368da98528a8..2759d9c4ba410b5a0dd43c810a6e241ed49a4bc2 100644 --- a/frontend/src/metabase/qb/components/drill/ObjectDetailDrill.jsx +++ b/frontend/src/metabase/qb/components/drill/ObjectDetailDrill.jsx @@ -39,6 +39,7 @@ export default ( return [ { + name: "object-detail", section: "details", title: "View details", default: true, diff --git a/frontend/src/metabase/qb/components/drill/QuickFilterDrill.jsx b/frontend/src/metabase/qb/components/drill/QuickFilterDrill.jsx index b2b092623836a2053fcb319ebffd1c7bad9efda9..299a986e40779f0219059abb53fe8a24522b0447 100644 --- a/frontend/src/metabase/qb/components/drill/QuickFilterDrill.jsx +++ b/frontend/src/metabase/qb/components/drill/QuickFilterDrill.jsx @@ -47,6 +47,7 @@ export default ( } else if (isFK(column.special_type)) { return [ { + name: "view-fks", section: "filter", title: ( <span> @@ -65,6 +66,7 @@ export default ( let operators = getFiltersForColumn(column) || []; return operators.map(({ name, operator }) => ({ + name: operator, section: "filter", title: <span className="h2">{name}</span>, card: () => filter(card, operator, column, value) diff --git a/frontend/src/metabase/qb/components/drill/SortAction.jsx b/frontend/src/metabase/qb/components/drill/SortAction.jsx index 19c6277be18717ced1001725311824eada77aa43..9af247862e702c945949bfd503907f195fcda570 100644 --- a/frontend/src/metabase/qb/components/drill/SortAction.jsx +++ b/frontend/src/metabase/qb/components/drill/SortAction.jsx @@ -38,8 +38,9 @@ export default ( sortDirection === "desc" ) { actions.push({ - title: "Ascending", + name: "sort-ascending", section: "sort", + title: "Ascending", card: () => assocIn( card, @@ -54,8 +55,9 @@ export default ( sortDirection === "asc" ) { actions.push({ - title: "Descending", + name: "sort-descending", section: "sort", + title: "Descending", card: () => assocIn( card, diff --git a/frontend/src/metabase/qb/components/drill/SummarizeColumnByTimeDrill.js b/frontend/src/metabase/qb/components/drill/SummarizeColumnByTimeDrill.js index 915024ca04be43706ac0d82f764a2bdd0d026d14..2cd4003007a2a03470ad081440ec4bd08cba6933 100644 --- a/frontend/src/metabase/qb/components/drill/SummarizeColumnByTimeDrill.js +++ b/frontend/src/metabase/qb/components/drill/SummarizeColumnByTimeDrill.js @@ -36,8 +36,9 @@ export default ( const { column } = clicked; return ["sum", "count"].map(aggregation => ({ - title: <span>{capitalize(aggregation)} by time</span>, + name: "summarize-by-time", section: "sum", + title: <span>{capitalize(aggregation)} by time</span>, card: () => pivot( summarize( diff --git a/frontend/src/metabase/qb/components/drill/SummarizeColumnDrill.js b/frontend/src/metabase/qb/components/drill/SummarizeColumnDrill.js index baba836ed738d22ac8f7d1c53c1415f425544357..352b6bb47448d6de96533e6beb1744c9e8d6a2c7 100644 --- a/frontend/src/metabase/qb/components/drill/SummarizeColumnDrill.js +++ b/frontend/src/metabase/qb/components/drill/SummarizeColumnDrill.js @@ -50,7 +50,11 @@ export default ( const { column } = clicked; // $FlowFixMe - return Object.entries(AGGREGATIONS).map(([aggregation, action]) => ({ + return Object.entries(AGGREGATIONS).map(([aggregation, action]: [string, { + section: string, + title: string + }]) => ({ + name: action.title.toLowerCase(), ...action, card: () => summarize( diff --git a/frontend/src/metabase/qb/components/drill/TimeseriesPivotDrill.jsx b/frontend/src/metabase/qb/components/drill/TimeseriesPivotDrill.jsx index bee8949f2d05fb354e83e60e0dacc08f301564e6..9f614a9dcf7131d62cf738afcaee6a3c7d8181f3 100644 --- a/frontend/src/metabase/qb/components/drill/TimeseriesPivotDrill.jsx +++ b/frontend/src/metabase/qb/components/drill/TimeseriesPivotDrill.jsx @@ -18,6 +18,7 @@ export default ( return [ { + name: "timeseries-zoom", section: "zoom", title: "Zoom in", card: () => diff --git a/frontend/src/metabase/qb/components/drill/UnderlyingRecordsDrill.jsx b/frontend/src/metabase/qb/components/drill/UnderlyingRecordsDrill.jsx index afb05d51b5ad2aba818c6021febb089a564860d4..a160a080d05660be9a0a3d9c3922f23003d7d560 100644 --- a/frontend/src/metabase/qb/components/drill/UnderlyingRecordsDrill.jsx +++ b/frontend/src/metabase/qb/components/drill/UnderlyingRecordsDrill.jsx @@ -22,6 +22,7 @@ export default ( return [ { + name: "underlying-records", section: "records", title: "View " + inflect("these", count, "this", "these") + diff --git a/frontend/src/metabase/query_builder/components/ActionsWidget.jsx b/frontend/src/metabase/query_builder/components/ActionsWidget.jsx index b7559ebb500d603982cf260df4e7508b428d8a17..981c66149e17f7b953854e00fd9d54ff83196ce8 100644 --- a/frontend/src/metabase/query_builder/components/ActionsWidget.jsx +++ b/frontend/src/metabase/query_builder/components/ActionsWidget.jsx @@ -7,6 +7,8 @@ import OnClickOutsideWrapper from "metabase/components/OnClickOutsideWrapper"; import { getModeActions } from "metabase/qb/lib/modes"; +import MetabaseAnalytics from "metabase/lib/analytics"; + import cx from "classnames"; import _ from "underscore"; @@ -60,6 +62,9 @@ export default class ActionsWidget extends Component<*, Props, *> { }; toggle = () => { + if (!this.state.isOpen) { + MetabaseAnalytics.trackEvent("Actions", "Opened Action Menu"); + } this.setState({ isOpen: !this.state.isOpen, selectedActionIndex: null @@ -88,6 +93,7 @@ export default class ActionsWidget extends Component<*, Props, *> { } else if (action && action.card) { const nextCard = action.card(); if (nextCard) { + MetabaseAnalytics.trackEvent("Actions", "Executed Action", `${action.section||""}:${action.name||""}`); this.handleOnChangeCardAndRun(nextCard); } this.close(); @@ -132,7 +138,10 @@ export default class ActionsWidget extends Component<*, Props, *> { /> </div> {isOpen && - <OnClickOutsideWrapper handleDismissal={this.close}> + <OnClickOutsideWrapper handleDismissal={() => { + MetabaseAnalytics.trackEvent("Actions", "Dismissed Action Menu"); + this.close(); + }}> <div className="absolute bg-white rounded bordered shadowed py1" style={{ @@ -163,6 +172,9 @@ export default class ActionsWidget extends Component<*, Props, *> { <PopoverComponent onChangeCardAndRun={(card) => { if (card) { + if (selectedAction) { + MetabaseAnalytics.trackEvent("Actions", "Executed Action", `${selectedAction.section||""}:${selectedAction.name||""}`); + } this.handleOnChangeCardAndRun(card) } }} diff --git a/frontend/src/metabase/visualizations/components/ChartClickActions.jsx b/frontend/src/metabase/visualizations/components/ChartClickActions.jsx index b07f877635a99133d71a52b188ea0cd4b7c6f9a1..13182daf6a5ece303c9310479606c5a9592959e6 100644 --- a/frontend/src/metabase/visualizations/components/ChartClickActions.jsx +++ b/frontend/src/metabase/visualizations/components/ChartClickActions.jsx @@ -6,6 +6,8 @@ import cx from 'classnames' import Icon from "metabase/components/Icon"; import Popover from "metabase/components/Popover"; +import MetabaseAnalytics from "metabase/lib/analytics"; + import type { ClickObject, ClickAction } from "metabase/meta/types/Visualization"; import type { Card } from "metabase/meta/types/Card"; @@ -77,7 +79,9 @@ export default class ChartClickActions extends Component<*, Props, State> { if (action.popover) { this.setState({ popoverAction: action }); } else if (action.card) { - onChangeCardAndRun(action.card()); + const card = action.card(); + MetabaseAnalytics.trackEvent("Actions", "Executed Click Action", `${action.section||""}:${action.name||""}`); + onChangeCardAndRun(card); this.close(); } } @@ -95,8 +99,16 @@ export default class ChartClickActions extends Component<*, Props, State> { const PopoverContent = popoverAction.popover; popover = ( <PopoverContent - onChangeCardAndRun={onChangeCardAndRun} - onClose={this.close} + onChangeCardAndRun={(card) => { + if (popoverAction) { + MetabaseAnalytics.trackEvent("Action", "Executed Click Action", `${popoverAction.section||""}:${popoverAction.name||""}`); + } + onChangeCardAndRun(card); + }} + onClose={() => { + MetabaseAnalytics.trackEvent("Action", "Dismissed Click Action Menu"); + this.close(); + }} /> ); } @@ -111,7 +123,10 @@ export default class ChartClickActions extends Component<*, Props, State> { <Popover target={clicked.element} targetEvent={clicked.event} - onClose={this.close} + onClose={() => { + MetabaseAnalytics.trackEvent("Action", "Dismissed Click Action Menu"); + this.close(); + }} verticalAttachments={["top", "bottom"]} horizontalAttachments={["left", "center", "right"]} sizeToFit diff --git a/frontend/src/metabase/visualizations/components/Visualization.jsx b/frontend/src/metabase/visualizations/components/Visualization.jsx index 40a5679003490333bd469aa3efc80888b08e5179..0891714986a27b79dcf66354ef79b9fba2d99cbd 100644 --- a/frontend/src/metabase/visualizations/components/Visualization.jsx +++ b/frontend/src/metabase/visualizations/components/Visualization.jsx @@ -11,6 +11,7 @@ import Icon from "metabase/components/Icon.jsx"; import Tooltip from "metabase/components/Tooltip.jsx"; import { duration, formatNumber } from "metabase/lib/formatting"; +import MetabaseAnalytics from "metabase/lib/analytics"; import { getVisualizationTransformed } from "metabase/visualizations"; import { getSettings } from "metabase/visualizations/lib/settings"; @@ -208,17 +209,18 @@ export default class Visualization extends Component<*, Props, State> { } handleVisualizationClick = (clicked: ClickObject) => { + if (clicked) { + MetabaseAnalytics.trackEvent( + "Actions", + "Clicked", + `${clicked.column ? "column" : ""} ${clicked.value ? "value" : ""} ${clicked.dimensions ? "dimensions=" + clicked.dimensions.length : ""}` + ); + } + // needs to be delayed so we don't clear it when switching from one drill through to another setTimeout(() => { - // const { onChangeCardAndRun } = this.props; - // let clickActions = this.getClickActions(clicked); - // if there's a single drill action (without a popover) execute it immediately - // if (clickActions.length === 1 && clickActions[0].default && clickActions[0].card) { - // onChangeCardAndRun(clickActions[0].card()); - // } else { - this.setState({ clicked }); - // } - }, 100) + this.setState({ clicked }); + }, 100); } handleOnChangeCardAndRun = (card: UnsavedCard) => {