diff --git a/frontend/app/Router.js b/frontend/app/Router.js index 64357683b..c5fe4b36c 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -17,8 +17,9 @@ const AssistPure = lazy(() => import('Components/Assist')); const BugFinderPure = lazy(() => import('Components/BugFinder/BugFinder')); const DashboardPure = lazy(() => import('Components/Dashboard/NewDashboard')); const ErrorsPure = lazy(() => import('Components/Errors/Errors')); -const FunnelDetails = lazy(() => import('Components/Funnels/FunnelDetails')); +const FunnelDetailsPure = lazy(() => import('Components/Funnels/FunnelDetails')); const FunnelIssueDetails = lazy(() => import('Components/Funnels/FunnelIssueDetails')); +const FunnelPagePure = lazy(() => import('Components/Funnels/FunnelPage')); import WidgetViewPure from 'Components/Dashboard/components/WidgetView'; import Header from 'Components/Header/Header'; import { fetchList as fetchMetadata } from 'Duck/customField'; @@ -37,20 +38,21 @@ import { ModalProvider } from './components/Modal'; const BugFinder = withSiteIdUpdater(BugFinderPure); const Dashboard = withSiteIdUpdater(DashboardPure); -const WidgetView = withSiteIdUpdater(WidgetViewPure); const Session = withSiteIdUpdater(SessionPure); const LiveSession = withSiteIdUpdater(LiveSessionPure); const Assist = withSiteIdUpdater(AssistPure); const Client = withSiteIdUpdater(ClientPure); const Onboarding = withSiteIdUpdater(OnboardingPure); const Errors = withSiteIdUpdater(ErrorsPure); -const Funnels = withSiteIdUpdater(FunnelDetails); +const FunnelPage = withSiteIdUpdater(FunnelPagePure); +const FunnelsDetails = withSiteIdUpdater(FunnelDetailsPure); const FunnelIssue = withSiteIdUpdater(FunnelIssueDetails); const withSiteId = routes.withSiteId; // const withObTab = routes.withObTab; const METRICS_PATH = routes.metrics(); const METRICS_DETAILS = routes.metricDetails(); +const METRICS_DETAILS_SUB = routes.metricDetailsSub(); const DASHBOARD_PATH = routes.dashboard(); const DASHBOARD_SELECT_PATH = routes.dashboardSelected(); @@ -62,7 +64,8 @@ const SESSIONS_PATH = routes.sessions(); const ASSIST_PATH = routes.assist(); const ERRORS_PATH = routes.errors(); const ERROR_PATH = routes.error(); -const FUNNEL_PATH = routes.funnel(); +const FUNNEL_PATH = routes.funnels(); +const FUNNEL_CREATE_PATH = routes.funnelsCreate(); const FUNNEL_ISSUE_PATH = routes.funnelIssue(); const SESSION_PATH = routes.session(); const LIVE_SESSION_PATH = routes.liveSession(); @@ -199,6 +202,7 @@ class Router extends React.Component { {/* DASHBOARD and Metrics */} + @@ -207,7 +211,9 @@ class Router extends React.Component { - + + + {/* */} diff --git a/frontend/app/components/BugFinder/SessionList/SessionListHeader.js b/frontend/app/components/BugFinder/SessionList/SessionListHeader.js index 167b448f5..7707424b0 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionListHeader.js +++ b/frontend/app/components/BugFinder/SessionList/SessionListHeader.js @@ -60,4 +60,4 @@ export default connect(state => ({ activeTab: state.getIn([ 'search', 'activeTab' ]), period: state.getIn([ 'search', 'period' ]), filter: state.getIn([ 'search', 'instance' ]), -}), { applyFilter })(SessionListHeader); +}), { applyFilter })(SessionListHeader); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/SideMenu/SideMenuDividedItem.js b/frontend/app/components/Dashboard/SideMenu/SideMenuDividedItem.js deleted file mode 100644 index bd8af6281..000000000 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuDividedItem.js +++ /dev/null @@ -1,20 +0,0 @@ -import { SideMenuitem } from "UI"; -import Divider from 'Components/Errors/ui/Divider'; - -function SideMenuDividedItem({ className, noTopDivider = false, noBottomDivider = false, ...props }) { - return ( -
- { !noTopDivider && } - - { !noBottomDivider && } -
- ); -} - -SideMenuDividedItem.displayName = "SideMenuDividedItem"; - -export default SideMenuDividedItem; - diff --git a/frontend/app/components/Dashboard/SideMenu/SideMenuHeader.js b/frontend/app/components/Dashboard/SideMenu/SideMenuHeader.js deleted file mode 100644 index 9ab4f194b..000000000 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuHeader.js +++ /dev/null @@ -1,13 +0,0 @@ -import cn from 'classnames'; -import stl from './sideMenuHeader.module.css'; - -function SideMenuHeader({ text, className }) { - return ( -
- { text } -
- ) -} - -SideMenuHeader.displayName = "SideMenuHeader"; -export default SideMenuHeader; diff --git a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js deleted file mode 100644 index 1c7cd3c43..000000000 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js +++ /dev/null @@ -1,45 +0,0 @@ -import { SideMenuitem } from 'UI'; -import SideMenuHeader from './SideMenuHeader'; -import { setShowAlerts } from 'Duck/dashboard'; -import stl from './sideMenuSection.module.css'; -import { connect } from 'react-redux'; -import { NavLink } from 'react-router-dom'; -import { withSiteId } from 'App/routes'; -import CustomMetrics from 'Shared/CustomMetrics'; - -function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId, activeSection }) { - return ( - <> - - { items.filter(i => i.section === 'metrics').map(item => - onItemClick(item)} - /> - )} - -
-
- setShowAlerts(true)} - /> -
-
-
- -
- - ); -} - -SideMenuSection.displayName = "SideMenuSection"; - -export default connect(state => ({ - siteId: state.getIn([ 'site', 'siteId' ]) -}), { setShowAlerts })(SideMenuSection); diff --git a/frontend/app/components/Dashboard/SideMenu/sideMenuHeader.module.css b/frontend/app/components/Dashboard/SideMenu/sideMenuHeader.module.css deleted file mode 100644 index 5dce4e250..000000000 --- a/frontend/app/components/Dashboard/SideMenu/sideMenuHeader.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.label { - letter-spacing: 0.2em; - color: gray; -} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/SideMenu/sideMenuSection.module.css b/frontend/app/components/Dashboard/SideMenu/sideMenuSection.module.css deleted file mode 100644 index fccde627d..000000000 --- a/frontend/app/components/Dashboard/SideMenu/sideMenuSection.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.divider { - height: 1px; - width: 100%; - background-color: $gray-light; -} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx index 6d1cd01e4..76b8697c1 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx @@ -9,8 +9,6 @@ import { numberWithCommas } from 'App/utils'; interface Props { metric: any, data: any; - params: any; - // seriesMap: any; colors: any; onClick?: (filters) => void; } diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx index b9d0f73a2..272a886f9 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx @@ -26,14 +26,14 @@ const getColumns = (metric) => { interface Props { metric?: any, data: any; - onClick?: (filters) => void; + onClick?: (filters: any) => void; isTemplate?: boolean; } -function CustomMetriTable(props: Props) { +function CustomMetricTable(props: Props) { const { metric = {}, data = { values: [] }, onClick = () => null, isTemplate } = props; const rows = List(data.values); - const onClickHandler = (event, data) => { + const onClickHandler = (event: any, data: any) => { const filters = Array(); let filter = { ...filtersMap[metric.metricOf] } filter.value = [data.name] @@ -64,4 +64,4 @@ function CustomMetriTable(props: Props) { ) } -export default CustomMetriTable; +export default CustomMetricTable; diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx new file mode 100644 index 000000000..3806d55f5 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + + +interface Props { + +} +function CustomMetricTableErrors(props) { + return ( +
+ +
+ ); +} + +export default CustomMetricTableErrors; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/index.ts b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/index.ts new file mode 100644 index 000000000..78590d267 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/index.ts @@ -0,0 +1 @@ +export { default } from './CustomMetricTableErrors'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions/CustomMetricTableSessions.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions/CustomMetricTableSessions.tsx new file mode 100644 index 000000000..45cbd198a --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions/CustomMetricTableSessions.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import SessionItem from 'Shared/SessionItem'; +import { Pagination } from 'UI'; + +const PER_PAGE = 10; +interface Props { + data: any + metric?: any + isTemplate?: boolean; + isEdit?: boolean; +} + +function CustomMetricTableSessions(props: Props) { + const { data = { sessions: [], total: 0 }, isEdit = false, metric = {}, isTemplate } = props; + const currentPage = 1; + + return ( +
+ {data.sessions && data.sessions.map((session: any, index: any) => ( + + ))} + + {isEdit && ( +
+ this.props.updateCurrentPage(page)} + limit={PER_PAGE} + debounceRequest={500} + /> +
+ )} + + {!isEdit && ( + + )} +
+ ); +} + +export default CustomMetricTableSessions; + +const ViewMore = ({ total }: any) => total > PER_PAGE && ( +
+
+
+ All {total} sessions +
+
+
+); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions/index.ts b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions/index.ts new file mode 100644 index 000000000..46889345c --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableSessions/index.ts @@ -0,0 +1 @@ +export { default } from './CustomMetricTableSessions'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx index 51420daf8..fe83c04b8 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx @@ -29,7 +29,6 @@ interface Props { metric: any; // loading?: boolean; data?: any; - showSync?: boolean; compare?: boolean; period?: any; onClickEdit: (e) => void; @@ -37,50 +36,26 @@ interface Props { setShowAlerts: (showAlerts) => void; setAlertMetricId: (id) => void; onAlertClick: (e) => void; - init: (metric) => void; + init: (metric: any) => void; edit: (setDefault?) => void; setActiveWidget: (widget) => void; updateActiveState: (metricId, state) => void; isTemplate?: boolean; } function CustomMetricWidget(props: Props) { - const { metric, showSync, compare, period, isTemplate } = props; + const { metric, period, isTemplate } = props; const [loading, setLoading] = useState(false) const [data, setData] = useState([]); - const [seriesMap, setSeriesMap] = useState([]); + // const [seriesMap, setSeriesMap] = useState([]); const colors = Styles.customMetricColors; const params = customParams(period.rangeName) - const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart', startDate: period.start, endDate: period.end } + // const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart', startDate: period.start, endDate: period.end } const isLineChart = metric.viewType === 'lineChart'; const isProgress = metric.viewType === 'progress'; const isTable = metric.viewType === 'table'; const isPieChart = metric.viewType === 'pieChart'; - // useEffect(() => { - // new APIClient()['post'](`/custom_metrics/${metricParams.metricId}/chart`, { ...metricParams, q: metric.name }) - // .then(response => response.json()) - // .then(({ errors, data }) => { - // if (errors) { - // console.log('err', errors) - // } else { - // const namesMap = data - // .map(i => Object.keys(i)) - // .flat() - // .filter(i => i !== 'time' && i !== 'timestamp') - // .reduce((unique: any, item: any) => { - // if (!unique.includes(item)) { - // unique.push(item); - // } - // return unique; - // }, []); - - // setSeriesMap(namesMap); - // setData(getChartFormatter(period)(data)); - // } - // }).finally(() => setLoading(false)); - // }, [period]) - const clickHandlerTable = (filters) => { const activeWidget = { widget: metric, @@ -147,7 +122,6 @@ function CustomMetricWidget(props: Props) { diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx index 5f90e01ac..5275a9147 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx @@ -1,28 +1,24 @@ import React, { useEffect, useState, useRef } from 'react'; import { connect } from 'react-redux'; -import { Loader, NoContent, SegmentSelection, Icon } from 'UI'; +import { Loader, NoContent, SegmentSelection } from 'UI'; import { Styles } from '../../common'; -// import { ResponsiveContainer, XAxis, YAxis, CartesianGrid, Tooltip, LineChart, Line, Legend } from 'recharts'; -import Period, { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period'; +import Period from 'Types/app/period'; import stl from './CustomMetricWidgetPreview.module.css'; -import { getChartFormatter } from 'Types/dashboard/helper'; import { remove } from 'Duck/customMetrics'; import DateRange from 'Shared/DateRange'; import { edit } from 'Duck/customMetrics'; import CustomMetriLineChart from '../CustomMetriLineChart'; import CustomMetricPercentage from '../CustomMetricPercentage'; import CustomMetricTable from '../CustomMetricTable'; - -import APIClient from 'App/api_client'; import CustomMetricPieChart from '../CustomMetricPieChart'; -const customParams = rangeName => { +const customParams = (rangeName: string) => { const params = { density: 70 } - if (rangeName === LAST_24_HOURS) params.density = 70 - if (rangeName === LAST_30_MINUTES) params.density = 70 - if (rangeName === YESTERDAY) params.density = 70 - if (rangeName === LAST_7_DAYS) params.density = 70 + // if (rangeName === LAST_24_HOURS) params.density = 70 + // if (rangeName === LAST_30_MINUTES) params.density = 70 + // if (rangeName === YESTERDAY) params.density = 70 + // if (rangeName === LAST_7_DAYS) params.density = 70 return params } @@ -30,23 +26,18 @@ const customParams = rangeName => { interface Props { metric: any; data?: any; - showSync?: boolean; - // compare?: boolean; onClickEdit?: (e) => void; remove: (id) => void; edit: (metric) => void; } function CustomMetricWidget(props: Props) { - const { metric, showSync } = props; + const { metric } = props; const [loading, setLoading] = useState(false) const [data, setData] = useState({ chart: [{}] }) - const [seriesMap, setSeriesMap] = useState([]); const [period, setPeriod] = useState(Period({ rangeName: metric.rangeName, startDate: metric.startDate, endDate: metric.endDate })); const colors = Styles.customMetricColors; const params = customParams(period.rangeName) - const gradientDef = Styles.gradientDef(); - const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart' } const prevMetricRef = useRef(); const isTimeSeries = metric.metricType === 'timeseries'; const isTable = metric.metricType === 'table'; @@ -59,29 +50,6 @@ function CustomMetricWidget(props: Props) { }; prevMetricRef.current = metric; setLoading(true); - - // fetch new data for the widget preview - // new APIClient()['post']('/custom_metrics/try', { ...metricParams, ...metric.toSaveData() }) - // .then(response => response.json()) - // .then(({ errors, data }) => { - // if (errors) { - // console.log('err', errors) - // } else { - // const namesMap = data - // .map(i => Object.keys(i)) - // .flat() - // .filter(i => i !== 'time' && i !== 'timestamp') - // .reduce((unique: any, item: any) => { - // if (!unique.includes(item)) { - // unique.push(item); - // } - // return unique; - // }, []); - - // setSeriesMap(namesMap); - // setData(getChartFormatter(period)(data)); - // } - // }).finally(() => setLoading(false)); }, [metric]) const onDateChange = (changedDates) => { @@ -100,7 +68,7 @@ function CustomMetricWidget(props: Props) {
{isTimeSeries && ( <> - Visualization + Visualization )} diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricsWidgets.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricsWidgets.tsx deleted file mode 100644 index 4db7abfd4..000000000 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricsWidgets.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { connect } from 'react-redux'; -import { fetchList } from 'Duck/customMetrics'; -import CustomMetricWidget from './CustomMetricWidget'; -import AlertFormModal from 'App/components/Alerts/AlertFormModal'; -import { init as initAlert } from 'Duck/alerts'; -import LazyLoad from 'react-lazyload'; -import CustomMetrics from 'App/components/shared/CustomMetrics'; - -interface Props { - fetchList: Function; - list: any; - onClickEdit: (e) => void; - initAlert: Function; -} -function CustomMetricsWidgets(props: Props) { - const { list } = props; - const [activeMetricId, setActiveMetricId] = useState(null); - const activeList = list.filter(item => item.active); - - useEffect(() => { - props.fetchList() - }, []) - - return ( - <> -
- {activeList.map((item: any) => ( - - { - setActiveMetricId(item.metricId) - props.initAlert({ query: { left: item.series.first().seriesId }}) - }} - /> - - ))} -
- - {list.size === 0 && ( -
-
Be proactive by monitoring the metrics you care about the most.
- -
- )} - - {list.size > 0 && activeList && activeList.size === 0 && ( -
-
It's blank here, add a metric to this section.
-
- )} - - setActiveMetricId(null)} - /> - - ); -} - -export default connect(state => ({ - list: state.getIn(['customMetrics', 'list']), -}), { fetchList, initAlert })(CustomMetricsWidgets); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx index ad8663390..6b155364d 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx @@ -13,7 +13,7 @@ function SessionsPerBrowser(props: Props) { const getVersions = item => { return Object.keys(item) - .filter(i => i !== 'browser' && i !== 'count') + .filter(i => i !== 'browser' && i !== 'count' && i !== 'time' && i !== 'timestamp') .map(i => ({ key: 'v' +i, value: item[i]})) } return ( diff --git a/frontend/app/components/Dashboard/Widgets/common/Styles.js b/frontend/app/components/Dashboard/Widgets/common/Styles.js index 6da043a28..1804b6dbc 100644 --- a/frontend/app/components/Dashboard/Widgets/common/Styles.js +++ b/frontend/app/components/Dashboard/Widgets/common/Styles.js @@ -1,7 +1,6 @@ import React from 'react'; import { numberWithCommas } from 'App/utils'; - -const colors = ['#3EAAAF', '#5FBABF', '#7BCBCF', '#96DCDF', '#ADDCDF']; +const colors = ['#1E889A', '#239DB2', '#28B2C9', '#36C0D7', '#65CFE1']; const colorsx = ['#256669', '#38999e', '#3eaaaf', '#51b3b7', '#78c4c7', '#9fd5d7', '#c5e6e7'].reverse(); const compareColors = ['#394EFF', '#4D5FFF', '#808DFF', '#B3BBFF', '#E5E8FF']; const compareColorsx = ["#222F99", "#2E3ECC", "#394EFF", "#6171FF", "#8895FF", "#B0B8FF", "#D7DCFF"].reverse(); diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx index 1add670ec..0a89c0072 100644 --- a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx +++ b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx @@ -5,6 +5,7 @@ import { withRouter } from 'react-router-dom'; import { metrics, metricDetails, + metricDetailsSub, dashboardSelected, dashboardMetricCreate, dashboardMetricDetails, @@ -14,6 +15,7 @@ import { import DashboardView from '../DashboardView'; import MetricsView from '../MetricsView'; import WidgetView from '../WidgetView'; +import WidgetSubDetailsView from '../WidgetSubDetailsView'; function DashboardViewSelected({ siteId, dashboardId }) { return ( @@ -37,6 +39,10 @@ function DashboardRouter(props: Props) { + + + + diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index eed6bbaf9..fd6bd2ee3 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -123,15 +123,6 @@ function DashboardView(props: RouteComponentProps) {
- {/* Time Range */} - {/* dashboardStore.setPeriod(period)} - customRangeRight - direction="left" - /> */} + Errors List Item +
+ ); +} + +export default ErrorListItem; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Errors/ErrorListItem/index.ts b/frontend/app/components/Dashboard/components/Errors/ErrorListItem/index.ts new file mode 100644 index 000000000..b37bfbbca --- /dev/null +++ b/frontend/app/components/Dashboard/components/Errors/ErrorListItem/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorListItem' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Errors/ErrorsList/ErrorsList.tsx b/frontend/app/components/Dashboard/components/Errors/ErrorsList/ErrorsList.tsx new file mode 100644 index 000000000..a6a9bca4c --- /dev/null +++ b/frontend/app/components/Dashboard/components/Errors/ErrorsList/ErrorsList.tsx @@ -0,0 +1,21 @@ +import React, { useEffect } from 'react'; +import ErrorListItem from '../ErrorListItem'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; + +function ErrorsList(props) { + const { errorStore, metricStore } = useStore(); + const metric = useObserver(() => metricStore.instance); + + useEffect(() => { + errorStore.fetchErrors(); + }, []); + return ( +
+ Errors List + +
+ ); +} + +export default ErrorsList; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Errors/ErrorsList/index.ts b/frontend/app/components/Dashboard/components/Errors/ErrorsList/index.ts new file mode 100644 index 000000000..7dd28915c --- /dev/null +++ b/frontend/app/components/Dashboard/components/Errors/ErrorsList/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorsList'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Errors/ErrorsWidget/ErrorsWidget.tsx b/frontend/app/components/Dashboard/components/Errors/ErrorsWidget/ErrorsWidget.tsx new file mode 100644 index 000000000..b2862d040 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Errors/ErrorsWidget/ErrorsWidget.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ErrorsList from '../ErrorsList'; + +function ErrorsWidget(props) { + return ( +
+ +
+ ); +} + +export default ErrorsWidget; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Errors/ErrorsWidget/index.ts b/frontend/app/components/Dashboard/components/Errors/ErrorsWidget/index.ts new file mode 100644 index 000000000..8f62f38a4 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Errors/ErrorsWidget/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorsWidget'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.tsx b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.tsx new file mode 100644 index 000000000..215c5f0cc --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.tsx @@ -0,0 +1,46 @@ +import React, { useEffect, useState } from 'react'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; +import { Loader } from 'UI'; +import FunnelIssuesListItem from '../FunnelIssuesListItem'; +import SessionItem from 'App/components/shared/SessionItem/SessionItem'; + +interface Props { + issueId: string; +} +function FunnelIssueDetails(props: Props) { + const { dashboardStore, metricStore } = useStore(); + const { issueId } = props; + const filter = useObserver(() => dashboardStore.drillDownFilter); + const widget = useObserver(() => metricStore.instance); + const [loading, setLoading] = useState(false); + const [funnelIssue, setFunnelIssue] = useState(null); + const [sessions, setSessions] = useState([]); + + useEffect(() => { + setLoading(true); + widget.fetchIssue(widget.metricId, issueId, filter).then((resp: any) => { + setFunnelIssue(resp.issue); + setSessions(resp.sessions); + }).finally(() => { + setLoading(false); + }); + }, []); + + return ( + + {funnelIssue && } + +
+ {sessions.map((session: any) => ( + + ))} +
+
+ ); +} + +export default FunnelIssueDetails; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssueDetails/index.ts b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueDetails/index.ts new file mode 100644 index 000000000..486b120d5 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueDetails/index.ts @@ -0,0 +1 @@ +export { default } from './FunnelIssueDetails'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssueGraph/FunnelIssueGraph.tsx b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueGraph/FunnelIssueGraph.tsx new file mode 100644 index 000000000..8821fe5e9 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueGraph/FunnelIssueGraph.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Popup } from 'UI'; + +const MIN_WIDTH = '20px'; +interface Props { + issue: any +} +function FunnelIssueGraph(props: Props) { + const { issue } = props; + + return ( +
+
+ +
+
{issue.unaffectedSessions}
+ +
+
+ +
+
{issue.affectedSessions}
+ +
+
+ +
+
{issue.lostConversions}
+ +
+
+ ); +} + +export default FunnelIssueGraph; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssueGraph/index.ts b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueGraph/index.ts new file mode 100644 index 000000000..11aed8599 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueGraph/index.ts @@ -0,0 +1 @@ +export { default } from './FunnelIssueGraph'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssueModal/FunnelIssueModal.tsx b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueModal/FunnelIssueModal.tsx new file mode 100644 index 000000000..bf9b6acf8 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueModal/FunnelIssueModal.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { withRouter } from 'react-router-dom'; +import { useStore } from 'App/mstore'; +import { useModal } from 'App/components/Modal'; +import FunnelIssueDetails from '../FunnelIssueDetails'; + +interface Props { + issueId: string; +} +function FunnelIssueModal(props: Props) { + const { issueId } = props; + const { hideModal } = useModal(); + return ( +
+
+ +
+
+ ); +} + +export default FunnelIssueModal; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssueModal/index.ts b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueModal/index.ts new file mode 100644 index 000000000..0261453c0 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssueModal/index.ts @@ -0,0 +1 @@ +export { default } from './FunnelIssueModal'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssues/FunnelIssues.tsx b/frontend/app/components/Dashboard/components/Funnels/FunnelIssues/FunnelIssues.tsx new file mode 100644 index 000000000..d1f817a21 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssues/FunnelIssues.tsx @@ -0,0 +1,73 @@ +import React, { useEffect, useState } from 'react'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; +import { NoContent, Loader } from 'UI'; +import FunnelIssuesDropdown from '../FunnelIssuesDropdown'; +import FunnelIssuesSort from '../FunnelIssuesSort'; +import FunnelIssuesList from '../FunnelIssuesList'; +import { DateTime } from 'luxon'; +import { debounce } from 'App/utils'; +import useIsMounted from 'App/hooks/useIsMounted' +import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; + +function FunnelIssues() { + const { metricStore, dashboardStore } = useStore(); + const [data, setData] = useState({ issues: [] }); + const [loading, setLoading] = useState(false); + const isMounted = useIsMounted() + // const funnel = useObserver(() => funnelStore.instance); + // const funnel = useObserver(() => metricStore.instance); + // const issues = useObserver(() => funnelStore.issues); + // const loading = useObserver(() => funnelStore.isLoadingIssues); + + const fetchIssues = (filter: any) => { + if (!isMounted()) return; + setLoading(true) + widget.fetchIssues(filter).then((res: any) => { + setData(res) + }).finally(() => { + setLoading(false) + }); + } + + const filter = useObserver(() => dashboardStore.drillDownFilter); + const widget: any = useObserver(() => metricStore.instance); + const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const debounceRequest: any = React.useCallback(debounce(fetchIssues, 1000), []); + + + const depsString = JSON.stringify(widget.series); + useEffect(() => { + debounceRequest({ ...filter, series: widget.toJsonDrilldown(), page: metricStore.sessionsPage, limit: metricStore.sessionsPageSize }); + }, [filter.startTimestamp, filter.endTimestamp, filter.filters, depsString, metricStore.sessionsPage]); + + return useObserver(() => ( +
+
+

Most significant issues identified in this funnel

+
+
+ +
+ +
+
+ + + +
No issues found
+
+ } + > + + + +
+ )); +} + +export default FunnelIssues; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssues/index.ts b/frontend/app/components/Dashboard/components/Funnels/FunnelIssues/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesDropdown/FunnelIssuesDropdown.tsx b/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesDropdown/FunnelIssuesDropdown.tsx new file mode 100644 index 000000000..547e896ed --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesDropdown/FunnelIssuesDropdown.tsx @@ -0,0 +1,96 @@ +import React, { Component, ReactNode, FunctionComponent, useEffect } from 'react'; +import Select from 'Shared/Select' +import { components } from 'react-select'; +import { Icon } from 'UI'; +import FunnelIssuesSelectedFilters from '../FunnelIssuesSelectedFilters'; +import { useStore } from 'App/mstore'; + +const options = [ + { value: "click_rage", label: "Click Rage" }, + { value: "dead_click", label: "Dead Click" }, + { value: "excessive_scrolling", label: "Excessive Scrolling" }, + { value: "bad_request", label: "Bad Request" }, + { value: "missing_resource", label: "Missing Image" }, + { value: "memory", label: "High Memory Usage" }, + { value: "cpu", label: "High CPU" }, + { value: "slow_resource", label: "Slow Resource" }, + { value: "slow_page_load", label: "Slow Page" }, + { value: "crash", label: "Crash" }, + { value: "custom_event_error", label: "Custom Error" }, + { value: "js_error", label: "Error" } +] + +function FunnelIssuesDropdown(props) { + const { funnelStore } = useStore(); + const [isOpen, setIsOpen] = React.useState(false); + const [selectedValues, setSelectedValues] = React.useState([]); + const filteredOptions = options.filter((option: any) => { + return !selectedValues.includes(option.value); + }); + + const selectedOptions = options.filter((option: any) => { + return selectedValues.includes(option.value); + }); + + useEffect(() => { + funnelStore.updateKey('issuesFilter', selectedOptions); + }, [selectedOptions]); + + const handleChange = ({ value }: any) => { + toggleSelectedValue(value.value); + } + + const toggleSelectedValue = (value: string) => { + if (selectedValues.includes(value)) { + setSelectedValues(selectedValues.filter(v => v !== value)); + } else { + setSelectedValues([...selectedValues, value]); + } + } + + return ( +
+ +
+ ); +} + +export default FunnelIssuesSort; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesSort/index.ts b/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesSort/index.ts new file mode 100644 index 000000000..895b1eddd --- /dev/null +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesSort/index.ts @@ -0,0 +1 @@ +export { default } from './FunnelIssuesSort'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Sessions/SessionList/SessionList.tsx b/frontend/app/components/Dashboard/components/Sessions/SessionList/SessionList.tsx new file mode 100644 index 000000000..f85110edb --- /dev/null +++ b/frontend/app/components/Dashboard/components/Sessions/SessionList/SessionList.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import SessionListItem from '../SessionListItem'; + +function SessionList(props) { + return ( +
+ Session list + +
+ ); +} + +export default SessionList; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Sessions/SessionList/index.ts b/frontend/app/components/Dashboard/components/Sessions/SessionList/index.ts new file mode 100644 index 000000000..779c9df2a --- /dev/null +++ b/frontend/app/components/Dashboard/components/Sessions/SessionList/index.ts @@ -0,0 +1 @@ +export { default } from './SessionList'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Sessions/SessionListItem/SessionListItem.tsx b/frontend/app/components/Dashboard/components/Sessions/SessionListItem/SessionListItem.tsx new file mode 100644 index 000000000..898d2d341 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Sessions/SessionListItem/SessionListItem.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +interface Props { + session: any; +} +function SessionListItem(props: Props) { + return ( +
+ Session list item +
+ ); +} + +export default SessionListItem; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Sessions/SessionListItem/index.ts b/frontend/app/components/Dashboard/components/Sessions/SessionListItem/index.ts new file mode 100644 index 000000000..1c2791143 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Sessions/SessionListItem/index.ts @@ -0,0 +1 @@ +export { default } from './SessionListItem'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Sessions/SessionWidget/SessionWidget.tsx b/frontend/app/components/Dashboard/components/Sessions/SessionWidget/SessionWidget.tsx new file mode 100644 index 000000000..0eb5ca1a5 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Sessions/SessionWidget/SessionWidget.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import SessionList from '../SessionList'; + +function SessionWidget(props) { + return ( +
+ +
+ ); +} + +export default SessionWidget; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/Sessions/SessionWidget/index.ts b/frontend/app/components/Dashboard/components/Sessions/SessionWidget/index.ts new file mode 100644 index 000000000..64b9563f5 --- /dev/null +++ b/frontend/app/components/Dashboard/components/Sessions/SessionWidget/index.ts @@ -0,0 +1 @@ +export { default } from './SessionWidget'; \ 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 bdd9d220f..83894d299 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -13,6 +13,10 @@ import { getStartAndEndTimestampsByDensity } from 'Types/dashboard/helper'; import { debounce } from 'App/utils'; import useIsMounted from 'App/hooks/useIsMounted' +import FunnelWidget from 'App/components/Funnels/FunnelWidget'; +import ErrorsWidget from '../Errors/ErrorsWidget'; +import SessionWidget from '../Sessions/SessionWidget'; +import CustomMetricTableSessions from '../../Widgets/CustomMetricsWidgets/CustomMetricTableSessions'; interface Props { metric: any; isWidget?: boolean; @@ -35,6 +39,7 @@ function WidgetChart(props: Props) { const isTableWidget = metric.metricType === 'table' && metric.viewType === 'table'; const isPieChart = metric.metricType === 'table' && metric.viewType === 'pieChart'; + const isFunnel = metric.metricType === 'funnel'; const onChartClick = (event: any) => { if (event) { @@ -60,7 +65,7 @@ function WidgetChart(props: Props) { const depsString = JSON.stringify(_metric.series); - const fetchMetricChartData = (metric, payload, isWidget) => { + const fetchMetricChartData = (metric: any, payload: any, isWidget: any) => { if (!isMounted()) return; setLoading(true) dashboardStore.fetchMetricChartData(metric, payload, isWidget).then((res: any) => { @@ -82,7 +87,19 @@ function WidgetChart(props: Props) { }, [period, depsString]); const renderChart = () => { - const { metricType, viewType } = metric; + const { metricType, viewType, metricOf } = metric; + + if (metricType === 'sessions') { + return + } + + if (metricType === 'errors') { + return + } + + if (metricType === 'funnel') { + return + } if (metricType === 'predefined') { if (isOverviewWidget) { @@ -113,6 +130,14 @@ function WidgetChart(props: Props) { } if (metricType === 'table') { + if (metricOf === 'SESSIONS') { + return + } if (viewType === 'table') { return Unknown
; } return useObserver(() => ( - + {renderChart()} )); diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 83dab5b7d..9d95fc503 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import DropdownPlain from 'Shared/DropdownPlain'; import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions'; import { FilterKey } from 'Types/filter/filterType'; import { useStore } from 'App/mstore'; @@ -7,6 +6,7 @@ import { useObserver } from 'mobx-react-lite'; import { Button, Icon } from 'UI' import FilterSeries from '../FilterSeries'; import { confirm } from 'UI'; +import Select from 'Shared/Select' import { withSiteId, dashboardMetricDetails, metricDetails } from 'App/routes' import DashboardSelectionModal from '../DashboardSelectionModal/DashboardSelectionModal'; @@ -22,27 +22,36 @@ function WidgetForm(props: Props) { const { metricStore, dashboardStore } = useStore(); const dashboards = dashboardStore.dashboards; const isSaving = useObserver(() => metricStore.isSaving); - const metric: any = useObserver(() => metricStore.instance); + const metric: any = useObserver(() => metricStore.instance) const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries'); const tableOptions = metricOf.filter(i => i.type === 'table'); const isTable = metric.metricType === 'table'; - const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions); + const isFunnel = metric.metricType === 'funnel'; + const isErrors = metric.metricType === 'errors'; + const isSessions = metric.metricType === 'sessions'; + const _issueOptions = [{ label: 'All', value: 'all' }].concat(issueOptions); const canAddToDashboard = metric.exists() && dashboards.length > 0; const canAddSeries = metric.series.length < 3; - const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value }); - const writeOption = (e, { value, name }) => { - const obj = { [ name ]: value }; + // const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value }); + const writeOption = ({ value, name }: any) => { + value = Array.isArray(value) ? value : value.value + const obj: any = { [ name ]: value }; if (name === 'metricValue') { - obj['metricValue'] = [value]; + obj['metricValue'] = value; + + // handle issues (remove all when other option is selected) + if (Array.isArray(obj['metricValue']) && obj['metricValue'].length > 1) { + obj['metricValue'] = obj['metricValue'].filter(i => i.value !== 'all'); + } } if (name === 'metricOf') { - if (value === FilterKey.ISSUE) { - obj['metricValue'] = ['all']; - } + // if (value === FilterKey.ISSUE) { + // obj['metricValue'] = [{ value: 'all', label: 'All' }]; + // } } if (name === 'metricType') { @@ -60,14 +69,13 @@ function WidgetForm(props: Props) { const onSave = () => { const wasCreating = !metric.exists() - metricStore.save(metric, dashboardId).then((metric) => { + metricStore.save(metric, dashboardId).then((metric: any) => { if (wasCreating) { if (parseInt(dashboardId) > 0) { history.replace(withSiteId(dashboardMetricDetails(parseInt(dashboardId), metric.metricId), siteId)); } else { history.replace(withSiteId(metricDetails(metric.metricId), siteId)); } - } }); } @@ -87,24 +95,24 @@ function WidgetForm(props: Props) { } return useObserver(() => ( -
+
- i.value === metric.metricType) || metricTypes[0]} onChange={ writeOption } /> {metric.metricType === 'timeseries' && ( <> of - @@ -113,10 +121,10 @@ function WidgetForm(props: Props) { {metric.metricType === 'table' && ( <> of - @@ -125,11 +133,13 @@ function WidgetForm(props: Props) { {metric.metricOf === FilterKey.ISSUE && ( <> issue type - )} @@ -137,12 +147,12 @@ function WidgetForm(props: Props) { {metric.metricType === 'table' && ( <> showing - @@ -151,9 +161,9 @@ function WidgetForm(props: Props) {
-
- {`${isTable ? 'Filter by' : 'Chart Series'}`} - {!isTable && ( +
+ {`${(isTable || isFunnel) ? 'Filter by' : 'Chart Series'}`} + {!isTable && !isFunnel && ( +
+
+ ))} +
+
+
+ Lost conversions +
+ {funnel.lostConversions} + ({funnel.lostConversionsPercentage}%) +
+
+
+
+ Total conversions +
+ {funnel.totalConversions} + ({funnel.totalConversionsPercentage}%) +
+
+
+
+ Affected users +
+ {funnel.affectedUsers} + {/* (12%) */} +
+
+
+ + )); +} + +export default FunnelWidget; \ No newline at end of file diff --git a/frontend/app/components/Funnels/FunnelWidget/index.ts b/frontend/app/components/Funnels/FunnelWidget/index.ts new file mode 100644 index 000000000..e2e5d1797 --- /dev/null +++ b/frontend/app/components/Funnels/FunnelWidget/index.ts @@ -0,0 +1 @@ +export { default } from './FunnelWidget'; \ No newline at end of file diff --git a/frontend/app/components/Header/Header.js b/frontend/app/components/Header/Header.js index 5b4c14b27..9803a06f2 100644 --- a/frontend/app/components/Header/Header.js +++ b/frontend/app/components/Header/Header.js @@ -7,6 +7,7 @@ import { assist, client, errors, + funnels, dashboard, withSiteId, CLIENT_DEFAULT_TAB, @@ -31,6 +32,7 @@ const DASHBOARD_PATH = dashboard(); const SESSIONS_PATH = sessions(); const ASSIST_PATH = assist(); const ERRORS_PATH = errors(); +const FUNNELS_PATH = funnels(); const CLIENT_PATH = client(CLIENT_DEFAULT_TAB); const AUTOREFRESH_INTERVAL = 30 * 1000; @@ -105,6 +107,13 @@ const Header = (props) => { > { 'Errors' } + + { 'Funnels' } + { var initOpts = { projectKey: "PROJECT_KEY", ingestPoint: "https://${window.location.hostname}/ingest", - defaultInputMode: ${inputModeOptionsMap[gdpr.defaultInputMode]}, + defaultInputMode: ${gdpr.defaultInputMode}, obscureTextNumbers: ${gdpr.maskNumbers}, obscureTextEmails: ${gdpr.maskEmails}, }; @@ -65,18 +66,13 @@ const ProjectCodeSnippet = props => { } const onChangeSelect = ({ name, value }) => { - // console.log(name, value) - // const { gdpr } = site; const _gdpr = { ...gdpr.toData() }; - props.editGDPR({ [ name ]: value }); _gdpr[name] = value; props.editGDPR({ [ name ]: value }); saveGDPR(_gdpr) }; const onChangeOption = ({ target: { name, checked } }) => { - // const { gdpr } = site; - console.log(name, checked) const _gdpr = { ...gdpr.toData() }; _gdpr[name] = checked; props.editGDPR({ [ name ]: checked }); @@ -121,7 +117,7 @@ const ProjectCodeSnippet = props => {