From 36258e58c9f78af775c4cfea89218b205104373f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 4 Jul 2022 15:58:10 +0200 Subject: [PATCH] fix(ui) - dashboard date range selection reload, metric not found message --- .../components/WidgetChart/WidgetChart.tsx | 12 +- .../components/WidgetView/WidgetView.tsx | 109 ++++++++++-------- .../app/components/ui/NoContent/NoContent.js | 6 +- frontend/app/mstore/dashboardStore.ts | 16 ++- frontend/app/mstore/metricStore.ts | 22 +--- frontend/app/services/MetricService.ts | 2 +- frontend/app/utils.ts | 4 +- 7 files changed, 85 insertions(+), 86 deletions(-) diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index ad98b9b84..3b189fb52 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -4,7 +4,7 @@ import CustomMetricPercentage from 'App/components/Dashboard/Widgets/CustomMetri import CustomMetricTable from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable'; import CustomMetricPieChart from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart'; import { Styles } from 'App/components/Dashboard/Widgets/common'; -import { observer } from 'mobx-react-lite'; +import { observer, useObserver } from 'mobx-react-lite'; import { Loader } from 'UI'; import { useStore } from 'App/mstore'; import WidgetPredefinedChart from '../WidgetPredefinedChart'; @@ -28,8 +28,8 @@ function WidgetChart(props: Props) { const { isWidget = false, metric, isTemplate } = props; const { dashboardStore, metricStore } = useStore(); const _metric: any = metricStore.instance; - const period = dashboardStore.period; - const drillDownPeriod = dashboardStore.drillDownPeriod; + const period = useObserver(() => dashboardStore.period); + const drillDownPeriod = useObserver(() => dashboardStore.drillDownPeriod); const drillDownFilter = dashboardStore.drillDownFilter; const colors = Styles.customMetricColors; const [loading, setLoading] = useState(true) @@ -66,10 +66,10 @@ function WidgetChart(props: Props) { } const depsString = JSON.stringify(_metric.series); - const fetchMetricChartData = (metric: any, payload: any, isWidget: any) => { + const fetchMetricChartData = (metric: any, payload: any, isWidget: any, period: any) => { if (!isMounted()) return; setLoading(true) - dashboardStore.fetchMetricChartData(metric, payload, isWidget).then((res: any) => { + dashboardStore.fetchMetricChartData(metric, payload, isWidget, period).then((res: any) => { if (isMounted()) setData(res); }).finally(() => { setLoading(false); @@ -85,7 +85,7 @@ function WidgetChart(props: Props) { prevMetricRef.current = metric; const timestmaps = drillDownPeriod.toTimestamps(); const payload = isWidget ? { ...params } : { ...metricParams, ...timestmaps, ...metric.toJson() }; - debounceRequest(metric, payload, isWidget); + debounceRequest(metric, payload, isWidget, !isWidget ? drillDownPeriod : period); }, [drillDownPeriod, period, depsString, _metric.page, metric.metricType, metric.metricOf, metric.viewType]); diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index 91d47751a..3ea5dda5d 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { useStore } from 'App/mstore'; -import cn from 'classnames' -import { Icon, Loader } from 'UI'; +import cn from 'classnames'; +import { Icon, Loader, NoContent } from 'UI'; import WidgetForm from '../WidgetForm'; import WidgetPreview from '../WidgetPreview'; import WidgetSessions from '../WidgetSessions'; @@ -11,41 +11,51 @@ import { withSiteId } from 'App/routes'; import FunnelIssues from '../Funnels/FunnelIssues/FunnelIssues'; import Breadcrumb from 'Shared/Breadcrumb'; import { FilterKey } from 'Types/filter/filterType'; -import { Prompt } from 'react-router' +import { Prompt } from 'react-router'; +import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; interface Props { history: any; - match: any - siteId: any + match: any; + siteId: any; } function WidgetView(props: Props) { - const { match: { params: { siteId, dashboardId, metricId } } } = props; + const { + match: { + params: { siteId, dashboardId, metricId }, + }, + } = props; const { metricStore, dashboardStore } = useStore(); const widget = useObserver(() => metricStore.instance); const loading = useObserver(() => metricStore.isLoading); const [expanded, setExpanded] = useState(!metricId || metricId === 'create'); - const hasChanged = useObserver(() => widget.hasChanged) - + const hasChanged = useObserver(() => widget.hasChanged); + const dashboards = useObserver(() => dashboardStore.dashboards); const dashboard = useObserver(() => dashboards.find((d: any) => d.dashboardId == dashboardId)); const dashboardName = dashboard ? dashboard.name : null; + const [metricNotFound, setMetricNotFound] = useState(false); React.useEffect(() => { if (metricId && metricId !== 'create') { - metricStore.fetch(metricId, dashboardStore.period); + metricStore.fetch(metricId, dashboardStore.period).catch((e) => { + if (e.status === 404 || e.status === 422) { + setMetricNotFound(true); + } + }); } else if (metricId === 'create') { metricStore.init(); } - }, []) + }, []); const onBackHandler = () => { props.history.goBack(); - } + }; const openEdit = () => { if (expanded) return; - setExpanded(true) - } + setExpanded(true); + }; return useObserver(() => ( @@ -58,50 +68,55 @@ function WidgetView(props: Props) { return 'You have unsaved changes. Are you sure you want to leave?'; }} /> +
-
-
+ +
Metric not found!
+
+ } + > +
+
-

- metricStore.merge({ name })} - canEdit={expanded} - /> -

-
setExpanded(!expanded)}> -
- {expanded ? 'Close' : 'Edit'} - + })} + onClick={openEdit} + > +

+ metricStore.merge({ name })} canEdit={expanded} /> +

+
setExpanded(!expanded)}> +
+ {expanded ? 'Close' : 'Edit'} + +
+ + {expanded && }
- { expanded && } -
- - - { widget.metricOf !== FilterKey.SESSIONS && widget.metricOf !== FilterKey.ERRORS && ( - <> - { (widget.metricType === 'table' || widget.metricType === 'timeseries') && } - { widget.metricType === 'funnel' && } - - )} + + {widget.metricOf !== FilterKey.SESSIONS && widget.metricOf !== FilterKey.ERRORS && ( + <> + {(widget.metricType === 'table' || widget.metricType === 'timeseries') && } + {widget.metricType === 'funnel' && } + + )} +
)); diff --git a/frontend/app/components/ui/NoContent/NoContent.js b/frontend/app/components/ui/NoContent/NoContent.js index 395015a72..a82b8b99b 100644 --- a/frontend/app/components/ui/NoContent/NoContent.js +++ b/frontend/app/components/ui/NoContent/NoContent.js @@ -3,9 +3,8 @@ import { Icon } from 'UI'; import styles from './noContent.module.css'; export default ({ - title = "No data available.", + title =
No data available.
, subtext, - animatedIcon = false, icon, iconSize = 100, size, @@ -17,8 +16,7 @@ export default ({ }) => (!show ? children :
{ - // icon &&
- animatedIcon ?
: (icon && ) + icon && } { title &&
{ title }
} { diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index c4e739fd8..54fbe1972 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -74,12 +74,14 @@ export interface IDashboardSotre { fetchTemplates(hardRefresh: boolean): Promise; deleteDashboardWidget(dashboardId: string, widgetId: string): Promise; addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise; + setDrillDownPeriod(period: any): void; updatePinned(dashboardId: string): Promise; fetchMetricChartData( metric: IWidget, data: any, - isWidget: boolean + isWidget: boolean, + period: Period ): Promise; setPeriod(period: any): void; } @@ -484,10 +486,12 @@ export default class DashboardStore implements IDashboardSotre { fetchMetricChartData( metric: IWidget, data: any, - isWidget: boolean = false + isWidget: boolean = false, + period: Period ): Promise { - const period = this.period.toTimestamps(); + period = period.toTimestamps(); const params = { ...period, ...data, key: metric.predefinedKey }; + if (metric.page && metric.limit) { params["page"] = metric.page; @@ -504,7 +508,7 @@ export default class DashboardStore implements IDashboardSotre { ) { const _data = { ...data, - chart: getChartFormatter(this.period)(data.chart), + chart: getChartFormatter(period)(data.chart), }; metric.setData(_data); resolve(_data); @@ -529,7 +533,7 @@ export default class DashboardStore implements IDashboardSotre { ); } else { if (data.hasOwnProperty("chart")) { - _data["chart"] = getChartFormatter(this.period)( + _data["chart"] = getChartFormatter(period)( data.chart ); _data["namesMap"] = data.chart @@ -545,7 +549,7 @@ export default class DashboardStore implements IDashboardSotre { return unique; }, []); } else { - _data["chart"] = getChartFormatter(this.period)( + _data["chart"] = getChartFormatter(period)( Array.isArray(data) ? data : [] ); _data["namesMap"] = Array.isArray(data) diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 9a6ddbe5d..2a1f45c4a 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -172,7 +172,8 @@ export default class MetricStore implements IMetricStore { return metricService.getMetric(id) .then((metric: any) => { return this.instance = new Widget().fromJson(metric, period) - }).finally(() => { + }) + .finally(() => { this.isLoading = false }) } @@ -199,22 +200,3 @@ export default class MetricStore implements IMetricStore { }) } } - -const sampleJsonFunnel = { - // metricId: 1, - name: "Funnel Sample", - metricType: 'funnel', - series: [ - { - name: 'Series 1', - filter: { - eventsOrder: 'then', - filters: [ - { type: 'LOCATION', operator: 'is', value: ['/sessions', '/errors', '/users'], percent: 100, completed: 60, dropped: 40, }, - { type: 'LOCATION', operator: 'is', value: ['/sessions'], percent: 80, completed: 40, dropped: 60, }, - { type: 'CLICK', operator: 'on', value: ['DASHBOARDS'], percent: 80, completed: 10, dropped: 90, } - ] - } - } - ], -} diff --git a/frontend/app/services/MetricService.ts b/frontend/app/services/MetricService.ts index 2696eec3b..991418c62 100644 --- a/frontend/app/services/MetricService.ts +++ b/frontend/app/services/MetricService.ts @@ -45,7 +45,7 @@ export default class MetricService implements IMetricService { */ getMetric(metricId: string): Promise { return this.client.get('/metrics/' + metricId) - .then((response: { json: () => any; }) => response.json()) + .then(fetchErrorCheck) .then((response: { data: any; }) => response.data || {}); } diff --git a/frontend/app/utils.ts b/frontend/app/utils.ts index 15a3ffc88..9765d69c3 100644 --- a/frontend/app/utils.ts +++ b/frontend/app/utils.ts @@ -314,9 +314,9 @@ export const exportCSVFile = (headers, items, fileTitle) => { } }; -export const fetchErrorCheck = (response: any) => { +export const fetchErrorCheck = async (response: any) => { if (!response.ok) { - throw Error(response.statusText); + return Promise.reject(response); } return response.json(); };