From 9b558d444a1a1e38ba38b218ce9e0309cb6e533f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 9 Jan 2023 16:13:06 +0100 Subject: [PATCH] feat(ui) - insights - wip --- .../InsightsCard/InsightItem.tsx | 25 ++++ .../InsightsCard/InsightsCard.tsx | 31 +++++ .../InsightsCard/index.ts | 1 + .../components/WidgetChart/WidgetChart.tsx | 7 +- .../components/WidgetForm/WidgetForm.tsx | 117 ++++++++++-------- .../MetricTypeDropdown/MetricTypeDropdown.tsx | 1 + .../components/WidgetView/WidgetView.tsx | 16 ++- frontend/app/components/ui/SVG.tsx | 3 +- frontend/app/constants/card.ts | 7 ++ frontend/app/constants/filterOptions.js | 10 +- frontend/app/mstore/metricStore.ts | 89 ++++++++++--- .../app/svg/icons/arrow-counterclockwise.svg | 4 + frontend/app/types/filter/filterType.ts | 7 ++ 13 files changed, 244 insertions(+), 74 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightItem.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightsCard.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/index.ts create mode 100644 frontend/app/svg/icons/arrow-counterclockwise.svg diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightItem.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightItem.tsx new file mode 100644 index 000000000..41ac503be --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightItem.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Icon } from 'UI'; + +interface Props { + item: any; + onClick?: (e: React.MouseEvent) => void; +} +function InsightItem(props: Props) { + const { item, onClick = () => {} } = props; + return ( +
+ +
{item.ratio}
+
on
+
Update
+
increased by
+
{item.increase}
+
+ ); +} + +export default InsightItem; diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightsCard.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightsCard.tsx new file mode 100644 index 000000000..a100158e6 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/InsightsCard.tsx @@ -0,0 +1,31 @@ +import { useStore } from 'App/mstore'; +import { observer } from 'mobx-react-lite'; +import React from 'react'; +import InsightItem from './InsightItem'; + +const data = [ + { icon: 'dizzy', ratio: 'Click Rage', increase: 10, iconColor: 'red' }, + { icon: 'dizzy', ratio: 'Click Rage', increase: 10, iconColor: 'yello' }, + { icon: 'dizzy', ratio: 'Click Rage', increase: 10, iconColor: 'green' }, + { icon: 'dizzy', ratio: 'Click Rage', increase: 10, iconColor: 'gray' }, + { icon: 'dizzy', ratio: 'Click Rage', increase: 10, iconColor: 'red' }, +]; +interface Props {} +function InsightsCard(props: Props) { + const { metricStore } = useStore(); + const metric = metricStore.instance; + + const clickHanddler = (e: React.MouseEvent) => { + console.log(e); + }; + + return ( +
+ {data.map((item) => ( + + ))} +
+ ); +} + +export default observer(InsightsCard); diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/index.ts b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/index.ts new file mode 100644 index 000000000..bd85f2e4b --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard/index.ts @@ -0,0 +1 @@ +export { default } from './InsightsCard' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 3291c0006..d522c299a 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -13,12 +13,13 @@ import { getStartAndEndTimestampsByDensity } from 'Types/dashboard/helper'; import { debounce } from 'App/utils'; import useIsMounted from 'App/hooks/useIsMounted' import { FilterKey } from 'Types/filter/filterType'; -import { TIMESERIES, TABLE, CLICKMAP, FUNNEL, ERRORS, PERFORMANCE, RESOURCE_MONITORING, WEB_VITALS } from 'App/constants/card'; +import { TIMESERIES, TABLE, CLICKMAP, FUNNEL, ERRORS, PERFORMANCE, RESOURCE_MONITORING, WEB_VITALS, INSIGHTS } from 'App/constants/card'; import FunnelWidget from 'App/components/Funnels/FunnelWidget'; import SessionWidget from '../Sessions/SessionWidget'; import CustomMetricTableSessions from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions'; import CustomMetricTableErrors from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors'; import ClickMapCard from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/ClickMapCard' +import InsightsCard from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard'; interface Props { metric: any; @@ -193,6 +194,10 @@ function WidgetChart(props: Props) { ) } + if (metricType === INSIGHTS) { + return + } + return
Unknown metric type
; } return ( diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index d9eaf0e65..64ebef507 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { metricOf, issueOptions } from 'App/constants/filterOptions'; +import React, { useEffect, useState } from 'react'; +import { metricOf, issueOptions, issueCategories } from 'App/constants/filterOptions'; import { FilterKey } from 'Types/filter/filterType'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; @@ -18,9 +18,11 @@ import { RESOURCE_MONITORING, PERFORMANCE, WEB_VITALS, + INSIGHTS, } from 'App/constants/card'; -import { clickmapFilter, eventKeys } from 'App/types/filter/newFilter'; +import { eventKeys } from 'App/types/filter/newFilter'; import { renderClickmapThumbnail } from './renderMap'; +import Widget from 'App/mstore/types/widget'; interface Props { history: any; match: any; @@ -37,68 +39,45 @@ function WidgetForm(props: Props) { const { metricStore, dashboardStore } = useStore(); const isSaving = metricStore.isSaving; const metric: any = metricStore.instance; + const [initialInstance, setInitialInstance] = useState(); const timeseriesOptions = metricOf.filter((i) => i.type === 'timeseries'); const tableOptions = metricOf.filter((i) => i.type === 'table'); - const isTable = metric.metricType === 'table'; + const isTable = metric.metricType === TABLE; const isClickmap = metric.metricType === CLICKMAP; - const isFunnel = metric.metricType === 'funnel'; + const isFunnel = metric.metricType === FUNNEL; + const isInsights = metric.metricType === INSIGHTS; const canAddSeries = metric.series.length < 3; const eventsLength = metric.series[0].filter.filters.filter((i: any) => i.isEvent).length; const cannotSaveFunnel = isFunnel && (!metric.series[0] || eventsLength <= 1); + const isPredefined = [ERRORS, PERFORMANCE, RESOURCE_MONITORING, WEB_VITALS].includes( metric.metricType ); - const excludeFilterKeys = isClickmap ? eventKeys : [] + const excludeFilterKeys = isClickmap ? eventKeys : []; + + useEffect(() => { + if (!!metric && !initialInstance) { + setInitialInstance(metric.toJson()); + } + }, [metric]); const writeOption = ({ value, name }: { value: any; name: any }) => { value = Array.isArray(value) ? value : value.value; const obj: any = { [name]: value }; - if (name === 'metricValue') { - obj.metricValue = value; - - if (Array.isArray(obj.metricValue) && obj.metricValue.length > 1) { - obj.metricValue = obj.metricValue.filter((i: any) => i.value !== 'all'); - } - } - if (name === 'metricType') { switch (value) { case TIMESERIES: obj.metricOf = timeseriesOptions[0].value; - obj.viewType = 'lineChart'; break; case TABLE: obj.metricOf = tableOptions[0].value; - obj.viewType = 'table'; - break; - case FUNNEL: - obj.metricOf = 'sessionCount'; - break; - case ERRORS: - case RESOURCE_MONITORING: - case PERFORMANCE: - case WEB_VITALS: - obj.viewType = 'chart'; - break; - case CLICKMAP: - obj.viewType = 'chart'; - - if (value !== CLICKMAP) { - metric.series[0].filter.removeFilter(0); - } - - if (metric.series[0].filter.filters.length < 1) { - metric.series[0].filter.addFilter({ - ...clickmapFilter, - value: [''], - }); - } break; } } + metricStore.merge(obj); }; @@ -112,10 +91,16 @@ function WidgetForm(props: Props) { } } const savedMetric = await metricStore.save(metric); + setInitialInstance(metric.toJson()) if (wasCreating) { if (parseInt(dashboardId, 10) > 0) { - history.replace(withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId)); - dashboardStore.addWidgetToDashboard(dashboardStore.getDashboard(parseInt(dashboardId, 10))!, [savedMetric.metricId]); + history.replace( + withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId) + ); + dashboardStore.addWidgetToDashboard( + dashboardStore.getDashboard(parseInt(dashboardId, 10))!, + [savedMetric.metricId] + ); } else { history.replace(withSiteId(metricDetails(savedMetric.metricId), siteId)); } @@ -134,6 +119,11 @@ function WidgetForm(props: Props) { } }; + const undoChnages = () => { + const w = new Widget(); + metricStore.merge(w.fromJson(initialInstance), false); + }; + return (
@@ -142,7 +132,7 @@ function WidgetForm(props: Props) { - {metric.metricOf === FilterKey.ISSUE && ( + {metric.metricOf === FilterKey.ISSUE && metric.metricType === TABLE && ( <> issue type + + )} + {metric.metricType === 'table' && !(metric.metricOf === FilterKey.ERRORS || metric.metricOf === FilterKey.SESSIONS) && ( <> @@ -183,8 +187,8 @@ function WidgetForm(props: Props) { {!isPredefined && (
- {`${isTable || isFunnel || isClickmap ? 'Filter by' : 'Chart Series'}`} - {!isTable && !isFunnel && !isClickmap && ( + {`${isTable || isFunnel || isClickmap || isInsights ? 'Filter by' : 'Chart Series'}`} + {!isTable && !isFunnel && !isClickmap && !isInsights && ( +
+ + {metric.exists() && metric.hasChanged && ( + + )} +
{metric.exists() && ( diff --git a/frontend/app/components/Dashboard/components/WidgetForm/components/MetricTypeDropdown/MetricTypeDropdown.tsx b/frontend/app/components/Dashboard/components/WidgetForm/components/MetricTypeDropdown/MetricTypeDropdown.tsx index ac47765b8..f2faf0d5a 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/components/MetricTypeDropdown/MetricTypeDropdown.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/components/MetricTypeDropdown/MetricTypeDropdown.tsx @@ -43,6 +43,7 @@ function MetricTypeDropdown(props: Props) { const onChange = (type: string) => { metricStore.changeType(type); }; + return (