From fb78202139fe3d62ead5388d32128c7ca5e496a3 Mon Sep 17 00:00:00 2001 From: Brian Cao Date: Fri, 25 Aug 2023 10:45:59 -0700 Subject: [PATCH 1/5] Remove mandatory validation. --- src/lib/middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/middleware.ts b/src/lib/middleware.ts index 0efb9762..edf3e929 100644 --- a/src/lib/middleware.ts +++ b/src/lib/middleware.ts @@ -87,7 +87,7 @@ export const useValidate = createMiddleware(async (req: any, res, next) => { try { const { yup } = req as NextApiRequestQueryBody; - yup[req.method].validateSync({ ...req.query, ...req.body }); + yup[req.method] && yup[req.method].validateSync({ ...req.query, ...req.body }); } catch (e: any) { return badRequest(res, e.message); } From 1a47f594c2934ba28f6888d088f0b205f9904bf7 Mon Sep 17 00:00:00 2001 From: Brian Cao Date: Fri, 25 Aug 2023 11:03:58 -0700 Subject: [PATCH 2/5] Add dashboard filter. --- src/components/messages.js | 1 + .../pages/reports/FieldFilterForm.js | 36 +++++++---- .../pages/reports/FilterSelectForm.js | 3 +- .../pages/websites/WebsiteMetricsBar.js | 61 +++++++++++++++++-- src/pages/api/websites/[id]/index.ts | 1 + 5 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/components/messages.js b/src/components/messages.js index ff619945..f52ed5c5 100644 --- a/src/components/messages.js +++ b/src/components/messages.js @@ -140,6 +140,7 @@ export const labels = defineMessages({ description: { id: 'label.description', defaultMessage: 'Description' }, untitled: { id: 'label.untitled', defaultMessage: 'Untitled' }, type: { id: 'label.type', defaultMessage: 'Type' }, + filter: { id: 'label.filter', defaultMessage: 'Filter' }, filters: { id: 'label.filters', defaultMessage: 'Filters' }, breakdown: { id: 'label.breakdown', defaultMessage: 'Breakdown' }, true: { id: 'label.true', defaultMessage: 'True' }, diff --git a/src/components/pages/reports/FieldFilterForm.js b/src/components/pages/reports/FieldFilterForm.js index 96e96697..01efed3f 100644 --- a/src/components/pages/reports/FieldFilterForm.js +++ b/src/components/pages/reports/FieldFilterForm.js @@ -3,7 +3,14 @@ import { Form, FormRow, Item, Flexbox, Dropdown, Button } from 'react-basics'; import { useMessages, useFilters, useFormat } from 'components/hooks'; import styles from './FieldFilterForm.module.css'; -export default function FieldFilterForm({ name, label, type, values, onSelect }) { +export default function FieldFilterForm({ + name, + label, + type, + values, + onSelect, + includeOnlyEquals, +}) { const { formatMessage, labels } = useMessages(); const [filter, setFilter] = useState('eq'); const [value, setValue] = useState(); @@ -27,17 +34,19 @@ export default function FieldFilterForm({ name, label, type, values, onSelect })
- - {({ value, label }) => { - return {label}; - }} - + {!includeOnlyEquals && ( + + {({ value, label }) => { + return {label}; + }} + + )} {value => { return {formatValue(value, name)}; diff --git a/src/components/pages/reports/FilterSelectForm.js b/src/components/pages/reports/FilterSelectForm.js index 274a00ea..8e02930e 100644 --- a/src/components/pages/reports/FilterSelectForm.js +++ b/src/components/pages/reports/FilterSelectForm.js @@ -18,7 +18,7 @@ function useValues(websiteId, type) { return { data, error, isLoading }; } -export default function FilterSelectForm({ websiteId, items, onSelect }) { +export default function FilterSelectForm({ websiteId, items, onSelect, includeOnlyEquals }) { const [field, setField] = useState(); const { data, isLoading } = useValues(websiteId, field?.name); @@ -37,6 +37,7 @@ export default function FilterSelectForm({ websiteId, items, onSelect }) { type={field?.type} values={data} onSelect={onSelect} + includeOnlyEquals={includeOnlyEquals} /> ); } diff --git a/src/components/pages/websites/WebsiteMetricsBar.js b/src/components/pages/websites/WebsiteMetricsBar.js index c625e239..5297dfcd 100644 --- a/src/components/pages/websites/WebsiteMetricsBar.js +++ b/src/components/pages/websites/WebsiteMetricsBar.js @@ -1,20 +1,25 @@ import classNames from 'classnames'; -import { Row, Column } from 'react-basics'; -import { formatShortTime } from 'lib/format'; -import MetricCard from 'components/metrics/MetricCard'; +import { useApi, useDateRange, useMessages, usePageQuery, useSticky } from 'components/hooks'; import RefreshButton from 'components/input/RefreshButton'; import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import MetricCard from 'components/metrics/MetricCard'; import MetricsBar from 'components/metrics/MetricsBar'; -import { useApi, useDateRange, usePageQuery, useMessages, useSticky } from 'components/hooks'; +import FilterSelectForm from 'components/pages/reports/FilterSelectForm'; +import PopupForm from 'components/pages/reports/PopupForm'; +import { formatShortTime } from 'lib/format'; +import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row, TooltipPopup } from 'react-basics'; import styles from './WebsiteMetricsBar.module.css'; export function WebsiteMetricsBar({ websiteId, sticky }) { const { formatMessage, labels } = useMessages(); + const { get, useQuery } = useApi(); const [dateRange] = useDateRange(websiteId); const { startDate, endDate, modified } = dateRange; const { ref, isSticky } = useSticky({ enabled: sticky }); const { + resolveUrl, + router, query: { url, referrer, title, os, browser, device, country, region, city }, } = usePageQuery(); @@ -39,6 +44,17 @@ export function WebsiteMetricsBar({ websiteId, sticky }) { }), ); + const fieldOptions = [ + { name: 'url', type: 'string', label: formatMessage(labels.url) }, + { name: 'referrer', type: 'string', label: formatMessage(labels.referrer) }, + { name: 'browser', type: 'string', label: formatMessage(labels.browser) }, + { name: 'os', type: 'string', label: formatMessage(labels.os) }, + { name: 'device', type: 'string', label: formatMessage(labels.device) }, + { name: 'country', type: 'string', label: formatMessage(labels.country) }, + { name: 'region', type: 'string', label: formatMessage(labels.region) }, + { name: 'city', type: 'string', label: formatMessage(labels.city) }, + ]; + const { pageviews, uniques, bounces, totaltime } = data || {}; const num = Math.min(data && uniques.value, data && bounces.value); const diffs = data && { @@ -48,6 +64,42 @@ export function WebsiteMetricsBar({ websiteId, sticky }) { totaltime: totaltime.value - totaltime.change, }; + const handleAddFilter = ({ name, value }) => { + router.push(resolveUrl({ [name]: value })); + }; + + const WebsiteFilterButton = () => { + return ( + + + + + + {close => { + return ( + + { + handleAddFilter(value); + close(); + }} + includeOnlyEquals={true} + /> + + ); + }} + + + ); + }; + return (
+
diff --git a/src/pages/api/websites/[id]/index.ts b/src/pages/api/websites/[id]/index.ts index 597568de..0e5aacce 100644 --- a/src/pages/api/websites/[id]/index.ts +++ b/src/pages/api/websites/[id]/index.ts @@ -23,6 +23,7 @@ const schema = { id: yup.string().uuid().required(), }), }; + export default async ( req: NextApiRequestQueryBody, res: NextApiResponse, From fe56ed35d5cd736776d8e6e2644fd09ae3afffc3 Mon Sep 17 00:00:00 2001 From: Brian Cao Date: Fri, 25 Aug 2023 11:21:16 -0700 Subject: [PATCH 3/5] Hide filter. --- src/components/pages/websites/WebsiteChartList.js | 2 +- src/components/pages/websites/WebsiteMetricsBar.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/pages/websites/WebsiteChartList.js b/src/components/pages/websites/WebsiteChartList.js index f6f3f765..56cbe157 100644 --- a/src/components/pages/websites/WebsiteChartList.js +++ b/src/components/pages/websites/WebsiteChartList.js @@ -40,7 +40,7 @@ export default function WebsiteChartList({ websites, showCharts, limit }) { - + {showCharts && } ) : null; diff --git a/src/components/pages/websites/WebsiteMetricsBar.js b/src/components/pages/websites/WebsiteMetricsBar.js index 5297dfcd..35605804 100644 --- a/src/components/pages/websites/WebsiteMetricsBar.js +++ b/src/components/pages/websites/WebsiteMetricsBar.js @@ -10,7 +10,7 @@ import { formatShortTime } from 'lib/format'; import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row, TooltipPopup } from 'react-basics'; import styles from './WebsiteMetricsBar.module.css'; -export function WebsiteMetricsBar({ websiteId, sticky }) { +export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) { const { formatMessage, labels } = useMessages(); const { get, useQuery } = useApi(); @@ -162,7 +162,7 @@ export function WebsiteMetricsBar({ websiteId, sticky }) {
- + {showFilter && }
From c67deb68e6e44904b838c1ccc4d17a400e18898f Mon Sep 17 00:00:00 2001 From: Brian Cao Date: Fri, 25 Aug 2023 11:33:30 -0700 Subject: [PATCH 4/5] Remove enddate check. --- src/pages/api/realtime/[id].ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/api/realtime/[id].ts b/src/pages/api/realtime/[id].ts index ab7bb406..5b1e1e05 100644 --- a/src/pages/api/realtime/[id].ts +++ b/src/pages/api/realtime/[id].ts @@ -11,12 +11,10 @@ export interface RealtimeRequestQuery { startAt: number; } -const currentDate = new Date().getTime(); - const schema = { GET: yup.object().shape({ id: yup.string().uuid().required(), - startAt: yup.number().integer().max(currentDate).required(), + startAt: yup.number().integer().required(), }), }; From 30c32dca7d9a35ac48f31bd127a20631a82d0da7 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Fri, 25 Aug 2023 11:34:59 -0700 Subject: [PATCH 5/5] remove required yup filters on insights report --- src/pages/api/reports/insights.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/pages/api/reports/insights.ts b/src/pages/api/reports/insights.ts index d10eba3f..4d17c922 100644 --- a/src/pages/api/reports/insights.ts +++ b/src/pages/api/reports/insights.ts @@ -38,18 +38,14 @@ const schema = { ) .min(1) .required(), - filters: yup - .array() - .of( - yup.object().shape({ - name: yup.string().required(), - type: yup.string().required(), - filter: yup.string().required(), - value: yup.string().required(), - }), - ) - .min(1) - .required(), + filters: yup.array().of( + yup.object().shape({ + name: yup.string().required(), + type: yup.string().required(), + filter: yup.string().required(), + value: yup.string().required(), + }), + ), groups: yup.array().of( yup.object().shape({ name: yup.string().required(),