From 2cab8bb15d4ff23a7f94355d41748c48f242435b Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Mon, 15 Feb 2021 13:02:12 +0100 Subject: [PATCH 1/6] feature(lib): add event type filter --- lib/filters.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/filters.js b/lib/filters.js index d4853618..8adeb13e 100644 --- a/lib/filters.js +++ b/lib/filters.js @@ -113,6 +113,17 @@ export const refFilter = (data, { domain, domainOnly, raw }) => { return Object.keys(map).map(key => ({ x: key, y: map[key], w: links[key] })); }; +export const eventTypeFilter = (data, types) => { + if (!types || types.length === 0) { + return data; + } + + return data.filter(({ x }) => { + const [event] = x.split('\t'); + return types.some(type => type === event); + }); +}; + export const browserFilter = data => data.map(({ x, y }) => ({ x: BROWSERS[x] || x, y })).filter(({ x }) => x); From 8b6308d68650818e4651bcf56dc950119264e5e2 Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Mon, 15 Feb 2021 16:28:09 +0100 Subject: [PATCH 2/6] feature(lib): add event type query --- lib/queries.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/queries.js b/lib/queries.js index df23bde8..f5a677a4 100644 --- a/lib/queries.js +++ b/lib/queries.js @@ -501,6 +501,41 @@ export function getEventMetrics( ); } +export function getEventTypes( + website_id, + start_at, + end_at, + timezone = 'utc', + unit = 'day', + filters = {}, +) { + const params = [website_id, start_at, end_at]; + const { url } = filters; + + let urlFilter = ''; + + if (url) { + urlFilter = `and url=$${params.length + 1}`; + params.push(decodeURIComponent(url)); + } + + return rawQuery( + ` + select + event_type x, + ${getDateQuery('created_at', unit, timezone)} t, + count(*) y + from event + where website_id=$1 + and created_at between $2 and $3 + ${urlFilter} + group by 1, 2 + order by 2 + `, + params, + ); +} + export async function getRealtimeData(websites, time) { const [pageviews, sessions, events] = await Promise.all([ getPageviews(websites, time), From 67551fb1294669e635ec96be08d047d1a70fe5fa Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Mon, 15 Feb 2021 16:28:46 +0100 Subject: [PATCH 3/6] feature(api): add event types endpoint --- pages/api/website/[id]/event-types.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 pages/api/website/[id]/event-types.js diff --git a/pages/api/website/[id]/event-types.js b/pages/api/website/[id]/event-types.js new file mode 100644 index 00000000..9b461cb8 --- /dev/null +++ b/pages/api/website/[id]/event-types.js @@ -0,0 +1,25 @@ +import { getEventTypes } from 'lib/queries'; +import { ok, methodNotAllowed, unauthorized } from 'lib/response'; +import { allowQuery } from 'lib/auth'; + +export default async (req, res) => { + if (req.method === 'GET') { + if (!(await allowQuery(req))) { + return unauthorized(res); + } + + const { id, start_at, end_at, url } = req.query; + + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); + + const eventTypes = await getEventTypes(websiteId, startDate, endDate, undefined, undefined, { + url, + }); + + return ok(res, eventTypes); + } + + return methodNotAllowed(res); +}; From 49b45acf4bfb7e658025aaf983b207ba6be03537 Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Mon, 15 Feb 2021 16:31:53 +0100 Subject: [PATCH 4/6] feature(dashboard): filter events by their type --- components/metrics/EventsTable.js | 65 ++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/components/metrics/EventsTable.js b/components/metrics/EventsTable.js index 437942fb..2d301f8d 100644 --- a/components/metrics/EventsTable.js +++ b/components/metrics/EventsTable.js @@ -1,18 +1,65 @@ -import React from 'react'; +import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; import MetricsTable from './MetricsTable'; import Tag from 'components/common/Tag'; +import DropDown from 'components/common/DropDown'; +import { eventTypeFilter } from 'lib/filters'; +import useDateRange from 'hooks/useDateRange'; +import useFetch from 'hooks/useFetch'; +import usePageQuery from 'hooks/usePageQuery'; +import useShareToken from 'hooks/useShareToken'; +import { TOKEN_HEADER } from 'lib/constants'; + +const EVENT_FILTER_DEFAULT = { + value: 'EVENT_FILTER_DEFAULT', + label: 'All Events', +}; export default function EventsTable({ websiteId, ...props }) { + const [eventType, setEventType] = useState(EVENT_FILTER_DEFAULT.value); + + const { + query: { url }, + } = usePageQuery(); + + const shareToken = useShareToken(); + const [dateRange] = useDateRange(websiteId); + + const { startDate, endDate, modified } = dateRange; + const { data, loading, error } = useFetch( + `/api/website/${websiteId}/event-types`, + { + params: { + start_at: +startDate, + end_at: +endDate, + url, + }, + headers: { [TOKEN_HEADER]: shareToken?.token }, + }, + [modified], + ); + + const eventTypes = data ? [...new Set(data.map(({ x }) => x))] : []; + const dropDownOptions = [EVENT_FILTER_DEFAULT, ...eventTypes.map(t => ({ value: t, label: t }))]; + return ( - } - type="event" - metric={} - websiteId={websiteId} - renderLabel={({ x }) =>