Skip to content
Snippets Groups Projects
Commit 14c6a669 authored by Sameer Al-Sakran's avatar Sameer Al-Sakran Committed by GitHub
Browse files

Merge pull request #5007 from metabase/action-instrumentation

Action instrumentation
parents 4181f780 5196d7fb
No related branches found
No related tags found
No related merge requests found
Showing
with 79 additions and 23 deletions
/*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;
......
......@@ -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 = {
......
......@@ -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)
}));
......
......@@ -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(
......
......@@ -67,6 +67,7 @@ export default (name: string, icon: string, fieldFilter: FieldFilter) =>
return [
{
name: "pivot-by-" + name.toLowerCase(),
section: "breakout",
title: clicked
? name
......
......@@ -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)
......
......@@ -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
......
......@@ -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)
......
......@@ -16,6 +16,7 @@ export default ({ card, tableMetadata }: ClickActionProps): ClickAction[] => {
if (query && !Query.isBareRows(query)) {
return [
{
name: "underlying-records",
title: (
<span>
View the underlying
......
......@@ -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),
......
......@@ -39,6 +39,7 @@ export default (
return [
{
name: "object-detail",
section: "details",
title: "View details",
default: true,
......
......@@ -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)
......
......@@ -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,
......
......@@ -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(
......
......@@ -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(
......
......@@ -18,6 +18,7 @@ export default (
return [
{
name: "timeseries-zoom",
section: "zoom",
title: "Zoom in",
card: () =>
......
......@@ -22,6 +22,7 @@ export default (
return [
{
name: "underlying-records",
section: "records",
title: "View " +
inflect("these", count, "this", "these") +
......
......@@ -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)
}
}}
......
......@@ -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
......
......@@ -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) => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment