From 1b3a3dfc21f5d57ae7f334544500f20c91562681 Mon Sep 17 00:00:00 2001 From: Sudheer Salavadi Date: Tue, 4 Feb 2025 03:49:49 -0500 Subject: [PATCH] Product analytics refinements (#3002) * Various UX, UI and Functional Improvements in Dashboards & Cards - Depth filter of Sankey chart data in frontend - Dashboard & Cards empty state view updates - Disabled save image feature on cards * Fixed empty views and headers * Various improvements across dashboards and cards. --- frontend/app/components/Charts/BarChart.tsx | 5 + .../app/components/Charts/ColumnChart.tsx | 5 + frontend/app/components/Charts/LineChart.tsx | 5 + frontend/app/components/Charts/PieChart.tsx | 5 + .../app/components/Charts/SankeyChart.tsx | 110 +++--- .../Dashboard/Widgets/CardSessionsByList.tsx | 6 +- .../ClickMapCard/ClickMapCard.tsx | 7 +- .../Dashboard/Widgets/ListWithIcons.tsx | 2 +- .../DashboardHeader/DashboardHeader.tsx | 23 +- .../DashboardList/DashboardList.tsx | 13 +- .../DashboardOptions/DashboardOptions.tsx | 2 +- .../DashboardWidgetGrid.tsx | 4 +- .../MetricViewHeader/MetricViewHeader.tsx | 70 ++-- .../components/MetricsList/MetricsList.tsx | 102 ++++-- .../components/WidgetForm/WidgetFormNew.tsx | 8 +- .../components/WidgetName/WidgetName.tsx | 2 +- .../WidgetPreview/WidgetPreview.tsx | 6 +- .../WidgetSessions/WidgetSessions.tsx | 5 +- .../components/WidgetView/WidgetView.tsx | 54 +-- .../WidgetWrapper/WidgetWrapperNew.tsx | 7 +- .../shared/AnimatedSVG/AnimatedSVG.tsx | 10 +- .../SelectDateRange/SelectDateRange.tsx | 1 + .../app/components/ui/PageTitle/PageTitle.tsx | 2 +- frontend/app/svg/ca-no-cards.svg | 68 +--- frontend/app/svg/ca-no-dashboards.svg | 324 ++---------------- 25 files changed, 320 insertions(+), 526 deletions(-) diff --git a/frontend/app/components/Charts/BarChart.tsx b/frontend/app/components/Charts/BarChart.tsx index 94f599bad..035731595 100644 --- a/frontend/app/components/Charts/BarChart.tsx +++ b/frontend/app/components/Charts/BarChart.tsx @@ -67,6 +67,11 @@ function ORBarChart(props: BarChartProps) { ...defaultOptions.tooltip, formatter: customTooltipFormatter(chartUuid.current), }, + toolbox: { + feature: { + saveAsImage: { show: false }, + }, + }, xAxis, yAxis, dataset: datasets, diff --git a/frontend/app/components/Charts/ColumnChart.tsx b/frontend/app/components/Charts/ColumnChart.tsx index cce550bc4..0ad96deba 100644 --- a/frontend/app/components/Charts/ColumnChart.tsx +++ b/frontend/app/components/Charts/ColumnChart.tsx @@ -59,6 +59,11 @@ function ColumnChart(props: ColumnChartProps) { .filter((s: any) => !s._hideInLegend) .map((s: any) => s.name), }, + toolbox: { + feature: { + saveAsImage: { show: false }, + }, + }, grid: { ...defaultOptions.grid, left: 40, diff --git a/frontend/app/components/Charts/LineChart.tsx b/frontend/app/components/Charts/LineChart.tsx index 75f50d346..4849a7c9b 100644 --- a/frontend/app/components/Charts/LineChart.tsx +++ b/frontend/app/components/Charts/LineChart.tsx @@ -76,6 +76,11 @@ function ORLineChart(props: Props) { ...defaultOptions.tooltip, formatter: customTooltipFormatter(chartUuid.current), }, + toolbox: { + feature: { + saveAsImage: { show: false }, + }, + }, dataset: datasets, series, }); diff --git a/frontend/app/components/Charts/PieChart.tsx b/frontend/app/components/Charts/PieChart.tsx index 9675aab97..86a5c9ade 100644 --- a/frontend/app/components/Charts/PieChart.tsx +++ b/frontend/app/components/Charts/PieChart.tsx @@ -58,6 +58,11 @@ function PieChart(props: PieChartProps) { left: 10, right: 10, }, + toolbox: { + feature: { + saveAsImage: { show: false }, + }, + }, legend: { ...defaultOptions.legend, type: 'plain', diff --git a/frontend/app/components/Charts/SankeyChart.tsx b/frontend/app/components/Charts/SankeyChart.tsx index 831e16035..aac036ad8 100644 --- a/frontend/app/components/Charts/SankeyChart.tsx +++ b/frontend/app/components/Charts/SankeyChart.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { echarts, defaultOptions } from './init'; import { SankeyChart } from 'echarts/charts'; import { sankeyTooltip, getEventPriority, getNodeName } from './sankeyUtils'; -import { boxShadow } from 'html2canvas/dist/types/css/property-descriptors/box-shadow'; +import { NoContent } from 'App/components/ui'; +import {InfoCircleOutlined} from '@ant-design/icons'; echarts.use([SankeyChart]); interface SankeyNode { @@ -35,36 +36,61 @@ const EChartsSankey: React.FC = (props) => { const { data, height = 240, onChartClick } = props; const chartRef = React.useRef(null); + if (data.nodes.length === 0 || data.links.length === 0) { + return ( + + + Set a start or end point to visualize the journey. If set, try adjusting filters. + + + } + show={true} + /> + ); + } + React.useEffect(() => { if (!chartRef.current) return; const chart = echarts.init(chartRef.current); + const maxDepth = 4; + const filteredNodes = data.nodes.filter((n) => (n.depth ?? 0) <= maxDepth); + const filteredLinks = data.links.filter((l) => { + const sourceNode = data.nodes.find((n) => n.id === l.source); + const targetNode = data.nodes.find((n) => n.id === l.target); + return (sourceNode?.depth ?? 0) <= maxDepth && (targetNode?.depth ?? 0) <= maxDepth; + }); - const nodeValues = new Array(data.nodes.length).fill(0); - const echartNodes = data.nodes.map((n) => { - let computedName = getNodeName(n.eventType || 'Minor Paths', n.name); - - if (computedName === 'Other') { - computedName = 'Minor Paths'; - } - const itemColor = - computedName === 'Minor Paths' - ? '#222F99' - : n.eventType === 'DROP' - ? '#B5B7C8' - : '#394eff'; - return { - name: computedName, - depth: n.depth, - type: n.eventType, - id: n.id, - draggable: false, - itemStyle: { color: itemColor }, - }; - }) + const nodeValues = new Array(filteredNodes.length).fill(0); + + + const echartNodes = filteredNodes + .map((n) => { + let computedName = getNodeName(n.eventType || 'Minor Paths', n.name); + if (computedName === 'Other') { + computedName = 'Minor Paths'; + } + const itemColor = + computedName === 'Minor Paths' + ? '#222F99' + : n.eventType === 'DROP' + ? '#B5B7C8' + : '#394eff'; + return { + name: computedName, + depth: n.depth, + type: n.eventType, + id: n.id, + draggable: false, + itemStyle: { color: itemColor }, + }; + }) .sort((a, b) => { if (a.depth === b.depth) { return getEventPriority(a.type || '') - getEventPriority(b.type || ''); @@ -72,9 +98,11 @@ const EChartsSankey: React.FC = (props) => { return (a.depth as number) - (b.depth as number); } }); + console.log('EChart Nodes:', echartNodes); + - const echartLinks = data.links.map((l) => ({ + const echartLinks = filteredLinks.map((l) => ({ source: echartNodes.findIndex((n) => n.id === l.source), target: echartNodes.findIndex((n) => n.id === l.target), value: l.sessionsCount, @@ -100,8 +128,8 @@ const EChartsSankey: React.FC = (props) => { }, toolbox: { feature: { - saveAsImage: { show: false } - } + saveAsImage: { show: false }, + }, }, series: [ { @@ -121,7 +149,7 @@ const EChartsSankey: React.FC = (props) => { tooltip: { formatter: sankeyTooltip(echartNodes, nodeValues), }, - nodeAlign: 'right', + nodeAlign: 'jusitfy', nodeWidth: 40, nodeGap: 8, lineStyle: { @@ -139,7 +167,6 @@ const EChartsSankey: React.FC = (props) => { chart.setOption(option); - function getUpstreamNodes(nodeIdx: number, visited = new Set()) { if (visited.has(nodeIdx)) return; visited.add(nodeIdx); @@ -151,7 +178,6 @@ const EChartsSankey: React.FC = (props) => { return visited; } - function getDownstreamNodes(nodeIdx: number, visited = new Set()) { if (visited.has(nodeIdx)) return; visited.add(nodeIdx); @@ -163,18 +189,15 @@ const EChartsSankey: React.FC = (props) => { return visited; } - function getConnectedChain(nodeIdx: number): Set { const upstream = getUpstreamNodes(nodeIdx) || new Set(); const downstream = getDownstreamNodes(nodeIdx) || new Set(); return new Set([...upstream, ...downstream]); } - const originalNodes = [...echartNodes]; const originalLinks = [...echartLinks]; - chart.on('mouseover', function (params: any) { if (params.dataType === 'node') { const hoveredIndex = params.dataIndex; @@ -182,10 +205,10 @@ const EChartsSankey: React.FC = (props) => { const updatedNodes = echartNodes.map((node, idx) => { const baseOpacity = connectedChain.has(idx) ? 1 : 0.35; - - const extraStyle = idx === hoveredIndex - ? {borderColor: '#000000', borderWidth:1, borderType: 'dotted' } - : {}; + const extraStyle = + idx === hoveredIndex + ? { borderColor: '#000000', borderWidth: 1, borderType: 'dotted' } + : {}; return { ...node, itemStyle: { @@ -196,14 +219,14 @@ const EChartsSankey: React.FC = (props) => { }; }); - const updatedLinks = echartLinks.map((link) => ({ ...link, lineStyle: { ...link.lineStyle, - opacity: (connectedChain.has(link.source) && connectedChain.has(link.target)) - ? 0.5 - : 0.1, + opacity: + connectedChain.has(link.source) && connectedChain.has(link.target) + ? 0.5 + : 0.1, }, })); @@ -236,16 +259,17 @@ const EChartsSankey: React.FC = (props) => { if (!onChartClick) return; if (params.dataType === 'node') { const nodeIndex = params.dataIndex; - const node = data.nodes[nodeIndex]; + // Use filteredNodes here. + const node = filteredNodes[nodeIndex]; onChartClick([{ node }]); } else if (params.dataType === 'edge') { const linkIndex = params.dataIndex; - const link = data.links[linkIndex]; + // Use filteredLinks here. + const link = filteredLinks[linkIndex]; onChartClick([{ link }]); } }); - const ro = new ResizeObserver(() => chart.resize()); ro.observe(chartRef.current); diff --git a/frontend/app/components/Dashboard/Widgets/CardSessionsByList.tsx b/frontend/app/components/Dashboard/Widgets/CardSessionsByList.tsx index 7d3dabc73..593fae385 100644 --- a/frontend/app/components/Dashboard/Widgets/CardSessionsByList.tsx +++ b/frontend/app/components/Dashboard/Widgets/CardSessionsByList.tsx @@ -44,11 +44,11 @@ function CardSessionsByList({ list, selected, paginated, onClickHandler = () => key={row.name} onClick={(e) => onClickHandler(e, row)} style={{ - borderBottom: index === data.length - 1 ? 'none' : '1px dotted rgba(0, 0, 0, 0.05)', + borderBottom: index === data.length - 1 ? 'none' : 'none', padding: '4px 10px', lineHeight: '1px' }} - className={cn('rounded', selected === row.name ? 'bg-active-blue' : '')} + className={cn('rounded-lg border-b-0 hover:bg-active-blue cursor-pointer', selected === row.name ? 'bg-active-blue' : '')} > title={(
- {row.displayName} + {row.displayName} {row.sessionCount}
diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/ClickMapCard/ClickMapCard.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/ClickMapCard/ClickMapCard.tsx index e857b2520..1b0e35d35 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/ClickMapCard/ClickMapCard.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/ClickMapCard/ClickMapCard.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; import ClickMapRenderer from 'App/components/Session/Player/ClickMapRenderer'; -import { NoContent, Icon } from 'App/components/ui'; +import { NoContent } from 'App/components/ui'; +import {InfoCircleOutlined} from '@ant-design/icons'; function ClickMapCard() { const [customSession, setCustomSession] = React.useState(null); @@ -63,8 +64,8 @@ function ClickMapCard() { style={{ minHeight: 220 }} title={
- - No data available for the selected period. + + Set a start point to visualize the heatmap. If set, try adjusting filters.
} show={true} diff --git a/frontend/app/components/Dashboard/Widgets/ListWithIcons.tsx b/frontend/app/components/Dashboard/Widgets/ListWithIcons.tsx index 0b69fb4cc..958329dfa 100644 --- a/frontend/app/components/Dashboard/Widgets/ListWithIcons.tsx +++ b/frontend/app/components/Dashboard/Widgets/ListWithIcons.tsx @@ -35,7 +35,7 @@ function ListWithIcons({ list = [] }: Props) { title={(
- {row.name} + {row.name} {row.value}
diff --git a/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx b/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx index f2dcefbbb..66937d564 100644 --- a/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx +++ b/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx @@ -3,13 +3,15 @@ import BackButton from 'Shared/Breadcrumb/BackButton'; import { withSiteId } from 'App/routes'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { PageTitle, confirm } from 'UI'; -import { Tooltip } from 'antd'; +import { Tooltip, Popover, Button } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; import SelectDateRange from 'Shared/SelectDateRange'; import { useStore } from 'App/mstore'; import DashboardOptions from '../DashboardOptions'; import withModal from 'App/components/Modal/withModal'; import { observer } from 'mobx-react-lite'; import DashboardEditModal from '../DashboardEditModal'; +import AddCardSection from '../AddCardSection/AddCardSection'; interface IProps { siteId: string; @@ -63,14 +65,13 @@ function DashboardHeader(props: Props) { title={ // @ts-ignore - {dashboard?.name} +
{dashboard?.name}
} - onDoubleClick={() => onEdit(true)} + onClick={() => onEdit(true)} className="mr-3 select-none border-b border-b-borderColor-transparent hover:border-dashed hover:border-gray-medium cursor-pointer" />
@@ -78,6 +79,17 @@ function DashboardHeader(props: Props) { className="flex items-center gap-2" style={{ flex: 1, justifyContent: 'end' }} > + + } + overlayInnerStyle={{ padding: 0, borderRadius: '0.75rem' }} + > + + + No matching results -
+
Try adjusting your search criteria or creating a new dashboard.
@@ -188,14 +188,13 @@ function DashboardList() { ) : (
- - Create your first dashboard. + + Create and organize your insights -
- Organize your product and technical insights as cards in dashboards - to see the bigger picture. +
+ Build dashboards to track key metrics and monitor performance in one place.
-
+
diff --git a/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx b/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx index c785109df..b0a28e16b 100644 --- a/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx +++ b/frontend/app/components/Dashboard/components/DashboardOptions/DashboardOptions.tsx @@ -49,7 +49,7 @@ function DashboardOptions(props: Props) { return ( - - - + + {showHeader && ( + + + + + + )}
-
- } - trigger="click" - > - - - - - -
+ + + + + +
+ )}
); } -export default observer(MetricViewHeader); - +export default observer(MetricViewHeader); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 0400e90a5..e88ce64a6 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -1,11 +1,14 @@ import { observer } from 'mobx-react-lite'; import React, { useEffect, useMemo, useState } from 'react'; -import { NoContent, Loader } from 'UI'; +import { NoContent, Loader, Pagination } from 'UI'; import { useStore } from 'App/mstore'; import { sliceListPerPage } from 'App/utils'; import GridView from './GridView'; import ListView from './ListView'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; +import AddCardSection from '../AddCardSection/AddCardSection'; +import { Popover, Button } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; function MetricsList({ siteId, @@ -33,30 +36,28 @@ function MetricsList({ (i) => !existingCardIds?.includes(parseInt(i.metricId)) ) : metricStore.filteredCards, - [metricStore.filteredCards] + [metricStore.filteredCards, existingCardIds, onSelectionChange] ); const loading = metricStore.isLoading; useEffect(() => { void metricStore.fetchList(); - }, []); + }, [metricStore]); useEffect(() => { - if (!onSelectionChange) { - return; - } + if (!onSelectionChange) return; onSelectionChange(selectedMetrics); - }, [selectedMetrics]); + }, [selectedMetrics, onSelectionChange]); const toggleMetricSelection = (id: any) => { if (Array.isArray(id)) { setSelectedMetrics(id); - return + return; } if (selectedMetrics.includes(id)) { - setSelectedMetrics((prev) => prev.filter((i: number) => i !== id)); + setSelectedMetrics((prev: number[]) => prev.filter((i) => i !== id)); } else { - setSelectedMetrics((prev) => [...prev, id]); + setSelectedMetrics((prev: number[]) => [...prev, id]); } }; @@ -64,32 +65,59 @@ function MetricsList({ useEffect(() => { metricStore.updateKey('sessionsPage', 1); - }, []); + }, [metricStore]); const showOwn = metricStore.filter.showMine; const toggleOwn = () => { metricStore.updateKey('showMine', !showOwn); - } + }; + + // Define dimensions for the empty state illustration + const isFiltered = + metricsSearch !== '' || (metricStore.filter.type && metricStore.filter.type !== 'all'); + + const searchImageDimensions = { width: 60, height: 'auto' }; + const defaultImageDimensions = { width: 600, height: 'auto' }; + const emptyImage = isFiltered ? ICONS.NO_RESULTS : ICONS.NO_CARDS; + const imageDimensions = isFiltered ? searchImageDimensions : defaultImageDimensions; + + return ( - -
- {metricsSearch !== '' - ? 'No matching results' - : "You haven't created any cards yet"} + show={length === 0} + title={ +
+ +
+ {isFiltered + ? 'No matching results' + : 'Unlock insights with data cards'} +
+
+ } + subtext={ + isFiltered ? ( + '' + ) : ( +
+
+ Create and customize cards to analyze trends and user behavior effectively.
+ } + trigger="click" + > + +
- } - subtext={ - metricsSearch !== '' - ? '' - : 'Utilize cards to visualize key user interactions or product performance metrics.' - } - > + ) + } + > {listView ? ( - setSelectedMetrics(checked ? cards.map((i: any) => i.metricId).slice(0, 30 - existingCardIds!.length) : []) + toggleAll={({ target: { checked } }) => + setSelectedMetrics( + checked + ? cards.map((i: any) => i.metricId).slice(0, 30 - (existingCardIds?.length || 0)) + : [] + ) } /> ) : ( @@ -115,14 +147,16 @@ function MetricsList({ toggleSelection={toggleMetricSelection} />
-
+
Showing{' '} - {Math.min(cards.length, metricStore.pageSize)} out - of {cards.length} cards + + {Math.min(cards.length, metricStore.pageSize)} + {' '} + out of {cards.length} cards
metricStore.updateKey('page', page)} limit={metricStore.pageSize} debounceRequest={100} @@ -135,4 +169,4 @@ function MetricsList({ ); } -export default observer(MetricsList); +export default observer(MetricsList); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetFormNew.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetFormNew.tsx index cce9d5457..be35a51c4 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetFormNew.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetFormNew.tsx @@ -71,13 +71,7 @@ const FilterSection = observer(({ layout, metric, excludeFilterKeys, excludeCate React.useEffect(() => { const defaultSeriesCollapseState: Record = {}; metric.series.forEach((s: any) => { - defaultSeriesCollapseState[s.seriesId] = defaultSeriesCollapseState[ - s.seriesId - ] - ? defaultSeriesCollapseState[s.seriesId] - : allOpen - ? false - : defaultClosed.current; + defaultSeriesCollapseState[s.seriesId] = isTable ? false : (allOpen ? false : defaultClosed.current); }); setSeriesCollapseState(defaultSeriesCollapseState); }, [metric.series]); diff --git a/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx b/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx index 4e17c8b6a..e6d6c7d1a 100644 --- a/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx +++ b/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx @@ -60,7 +60,7 @@ function WidgetName(props: Props) { /> ) : ( // @ts-ignore - +
setEditing(true)} className={cn( diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 8994324b0..d24a7acf8 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -29,7 +29,7 @@ function WidgetPreview(props: Props) {
-
+
-
- -
+
}
- {hasFilters && widget.metricType === 'table' &&
{filterText}
} + {hasFilters && widget.metricType === 'table' && +
+ {filterText} +
}
diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index 67b5d1758..e8ebd3913 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -60,6 +60,7 @@ function WidgetView(props: Props) { const [initialInstance, setInitialInstance] = useState(); const isClickMap = widget.metricType === HEATMAP; + React.useEffect(() => { if (metricId && metricId !== 'create') { metricStore.fetch(metricId, dashboardStore.period).catch((e) => { @@ -81,6 +82,13 @@ function WidgetView(props: Props) { } }, []); + + React.useEffect(() => { + if (metricNotFound) { + history.replace(withSiteId('/metrics', siteId)); + } + }, [metricNotFound, history, siteId]); + const undoChanges = () => { const w = new Widget(); metricStore.merge(w.fromJson(initialInstance), false); @@ -149,17 +157,9 @@ function WidgetView(props: Props) { { label: widget.name }, ]} /> - - -
Metric not found!
-
- } - > + - -
+
+ +
) } @@ -198,31 +200,29 @@ function WidgetView(props: Props) { />
- +
- {widget.metricOf !== FilterKey.SESSIONS && - widget.metricOf !== FilterKey.ERRORS && - (widget.metricType === TABLE || - widget.metricType === TIMESERIES || - widget.metricType === HEATMAP || - widget.metricType === INSIGHTS || - widget.metricType === FUNNEL || - widget.metricType === USER_PATH ? ( - - ) : null)} - {widget.metricType === RETENTION && } + {widget.metricOf !== FilterKey.SESSIONS && + widget.metricOf !== FilterKey.ERRORS && + (widget.metricType === TABLE || + widget.metricType === TIMESERIES || + widget.metricType === HEATMAP || + widget.metricType === INSIGHTS || + widget.metricType === FUNNEL || + widget.metricType === USER_PATH ? ( + + ) : null)} + {widget.metricType === RETENTION && }
- -
- + {/* */}
); } -export default observer(WidgetView); +export default observer(WidgetView); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx index 93fe435bd..d09ca999c 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx @@ -106,8 +106,11 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) { style={{ userSelect: 'none', opacity: isDragging ? 0.5 : 1, - borderColor: - (canDrop && isOver) || active ? '#394EFF' : isPreview ? 'transparent' : '#EEEEEE' + borderColor: (canDrop && isOver) + ? '#454545' + : isPreview ? 'transparent' : '#EEEEEE', + borderStyle: (canDrop && isOver) ? 'dashed' : 'solid', + cursor: isDragging ? 'grabbing' : 'grab' }} ref={dragDropRef} onClick={props.onClick ? props.onClick : () => null} diff --git a/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx b/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx index f963b5a74..c83e0e8f2 100644 --- a/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx +++ b/frontend/app/components/shared/AnimatedSVG/AnimatedSVG.tsx @@ -50,7 +50,7 @@ const ICONS_SVGS = { [ICONS.NO_ANNOUNCEMENTS]: require('../../../svg/ghost.svg').default, [ICONS.NO_ALERTS]: require('../../../svg/ghost.svg').default, [ICONS.NO_NOTES]: require('../../../svg/ghost.svg').default, - [ICONS.NO_CARDS]: require('../../../svg/ghost.svg').default, + [ICONS.NO_CARDS]: require('../../../svg/ca-no-cards.svg').default, [ICONS.NO_RECORDINGS]: require('../../../svg/ghost.svg').default, [ICONS.NO_SEARCH_RESULTS]: require('../../../svg/ghost.svg').default, [ICONS.NO_DASHBOARDS]: require('../../../svg/ca-no-dashboards.svg').default, @@ -63,10 +63,12 @@ const ICONS_SVGS = { interface Props { name: string; size?: number; + disableSize?: boolean; + className?: string; } function AnimatedSVG(props: Props): JSX.Element | null { - const {name, size = 24} = props; + const {name, size = 24, disableSize, className} = props; // @ts-ignore const SvgIcon = ICONS_SVGS[name]; @@ -74,8 +76,8 @@ function AnimatedSVG(props: Props): JSX.Element | null { if (!SvgIcon) { return null; } - - return {name}/; + const style = disableSize ? {} : { width: size + 'px' }; + return {name}; } export default AnimatedSVG; \ No newline at end of file diff --git a/frontend/app/components/shared/SelectDateRange/SelectDateRange.tsx b/frontend/app/components/shared/SelectDateRange/SelectDateRange.tsx index 8329b3c43..51005dc3d 100644 --- a/frontend/app/components/shared/SelectDateRange/SelectDateRange.tsx +++ b/frontend/app/components/shared/SelectDateRange/SelectDateRange.tsx @@ -29,6 +29,7 @@ interface Props { comparison?: boolean; updateInstComparison?: (range: [start: string, end?: string] | null) => void; [x: string]: any; + className?: string; } function SelectDateRange(props: Props) { diff --git a/frontend/app/components/ui/PageTitle/PageTitle.tsx b/frontend/app/components/ui/PageTitle/PageTitle.tsx index b68e21952..d9947e606 100644 --- a/frontend/app/components/ui/PageTitle/PageTitle.tsx +++ b/frontend/app/components/ui/PageTitle/PageTitle.tsx @@ -14,7 +14,7 @@ function PageTitle({ title, actionButton = null, subTitle = '', className = '', return (
-

+

{title}

{ actionButton &&
{actionButton}
} diff --git a/frontend/app/svg/ca-no-cards.svg b/frontend/app/svg/ca-no-cards.svg index 5d03d279d..e11035096 100644 --- a/frontend/app/svg/ca-no-cards.svg +++ b/frontend/app/svg/ca-no-cards.svg @@ -1,59 +1,19 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + diff --git a/frontend/app/svg/ca-no-dashboards.svg b/frontend/app/svg/ca-no-dashboards.svg index 4370b3f8e..eb4758d36 100644 --- a/frontend/app/svg/ca-no-dashboards.svg +++ b/frontend/app/svg/ca-no-dashboards.svg @@ -1,301 +1,35 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - + + + + + -