diff --git a/frontend/app/Router.js b/frontend/app/Router.js
index a6a9ba090..74e8845f7 100644
--- a/frontend/app/Router.js
+++ b/frontend/app/Router.js
@@ -15,7 +15,7 @@ import LiveSessionPure from 'Components/Session/LiveSession';
import AssistPure from 'Components/Assist';
import BugFinderPure from 'Components/BugFinder/BugFinder';
import DashboardPure from 'Components/Dashboard/NewDashboard';
-import WidgetViewPure from 'Components/Dashboard/WidgetView';
+import WidgetViewPure from 'Components/Dashboard/components/WidgetView';
import ErrorsPure from 'Components/Errors/Errors';
import Header from 'Components/Header/Header';
// import ResultsModal from 'Shared/Results/ResultsModal';
@@ -49,10 +49,8 @@ const withSiteId = routes.withSiteId;
const withObTab = routes.withObTab;
const DASHBOARD_PATH = routes.dashboard();
-// const DASHBOARD_WIDGET_CREATE_PATH = routes.dashboardMetricCreate();
-// const DASHBOARD_WIDGET_DETAILS_PATH = routes.dashboardMetricDetails();
-// const METRIC_CREATE_PATH = routes.metricCreate();
-// const METRIC_DETAILS_PATH = routes.metricDetails();
+const DASHBOARD_SELECT_PATH = routes.dashboardSelected();
+const DASHBOARD_METRICS_PATH = routes.dashboardMetrics();
// const WIDGET_PATAH = routes.dashboardMetric();
const SESSIONS_PATH = routes.sessions();
@@ -187,7 +185,11 @@ class Router extends React.Component {
{ siteIdList.length === 0 &&
}
-
+
+
+ {/* */}
+ {/* */}
+
{/*
diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx
index 831e44bd8..77a784ec2 100644
--- a/frontend/app/components/Dashboard/NewDashboard.tsx
+++ b/frontend/app/components/Dashboard/NewDashboard.tsx
@@ -5,12 +5,19 @@ import { observer } from "mobx-react-lite";
import { useDashboardStore } from './store/store';
import { withRouter } from 'react-router-dom';
import DashboardView from './components/DashboardView';
-import { dashboardSelected, dashboardMetricDetails, dashboardMetricCreate, withSiteId } from 'App/routes';
+import {
+ dashboardSelected,
+ dashboardMetricDetails,
+ dashboardMetricCreate,
+ withSiteId,
+ dashboardMetrics,
+} from 'App/routes';
import DashboardSideMenu from './components/DashboardSideMenu';
-import WidgetView from './WidgetView';
+import WidgetView from './components/WidgetView';
+import MetricsView from './components/MetricsView';
function NewDashboard(props) {
- const { match: { params: { siteId, dashboardId, metricId } } } = props;
+ const { history, match: { params: { siteId, dashboardId, metricId } } } = props;
const store: any = useDashboardStore();
const dashboard = store.selectedDashboard;
@@ -19,42 +26,66 @@ function NewDashboard(props) {
}, []);
useEffect(() => {
- console.log('dashboardId', dashboardId);
if (!dashboard || !dashboard.dashboardId) {
if (dashboardId) {
store.selectDashboardById(dashboardId);
} else {
store.selectDefaultDashboard();
}
- }
+ }
+
+ // if (dashboard) {
+ // if (dashboard.dashboardId !== dashboardId) {
+ // history.push(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId)));
+ // }
+
+ // history.replace(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId)));
+ // }
+ // console.log('dashboard', dashboard)
}, [dashboard]);
+ console.log('rendering dashboard', props.match.params);
+
return (
-
-
-
-
-
- { dashboard && dashboard.dashboardId && (
-
-
-
-
-
-
-
-
-
-
- {/*
-
- */}
-
-
- )}
-
-
+ <>
+ {/* { dashboard && dashboard.dashboardId && ( */}
+
+
+
+
+ { dashboardId && (
+ <>
+
+
+
+
+ {/*
+
+
+
+
+ */}
+ {/* */}
+ >
+ )}
+
+ {/* )} */}
+ >
);
}
diff --git a/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx
deleted file mode 100644
index cfa34cd5c..000000000
--- a/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import { withRouter } from 'react-router-dom';
-import { useDashboardStore } from '../store/store';
-
-function WidgetView(props) {
- console.log('WidgetView', props);
- const store: any = useDashboardStore();
- const widget = store.currentWidget;
- return (
-
- );
-}
-
-export default withRouter(WidgetView);
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx
index 4c984904e..7f223bb55 100644
--- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx
+++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx
@@ -3,17 +3,20 @@ import React from 'react';
import { SideMenuitem, SideMenuHeader, Icon } from 'UI';
import { withDashboardStore } from '../../store/store';
import { withRouter } from 'react-router-dom';
-import { withSiteId, dashboardSelected } from 'App/routes';
+import { withSiteId, dashboardSelected, dashboardMetrics } from 'App/routes';
function DashboardSideMenu(props) {
const { store, history } = props;
const { dashboardId } = store.selectedDashboard;
+ const redirect = (path) => {
+ history.push(path);
+ }
+
const onItemClick = (dashboard) => {
store.selectDashboardById(dashboard.dashboardId);
const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(store.siteId));
- // console.log('path', path);
- // history.push(path);
+ history.push(path);
};
return (
@@ -41,7 +44,7 @@ function DashboardSideMenu(props) {
id="menu-manage-alerts"
title="Metrics"
iconName="bar-chart-line"
- // onClick={() => setShowAlerts(true)}
+ onClick={() => redirect(withSiteId(dashboardMetrics(), store.siteId))}
/>
diff --git a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx
new file mode 100644
index 000000000..c43e1b97a
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx
@@ -0,0 +1,111 @@
+import React, { useState } from 'react';
+import FilterList from 'Shared/Filters/FilterList';
+import {
+ edit,
+ updateSeries,
+ addSeriesFilterFilter,
+ removeSeriesFilterFilter,
+ editSeriesFilterFilter,
+ editSeriesFilter,
+} from 'Duck/customMetrics';
+import { connect } from 'react-redux';
+import { IconButton, Icon } from 'UI';
+import FilterSelection from 'Shared/Filters/FilterSelection';
+import SeriesName from './SeriesName';
+import cn from 'classnames';
+import { useDashboardStore } from '../../store/store';
+import { observer, useObserver } from 'mobx-react-lite';
+
+interface Props {
+ seriesIndex: number;
+ series: any;
+ edit: typeof edit;
+ updateSeries: typeof updateSeries;
+ onRemoveSeries: (seriesIndex) => void;
+ canDelete?: boolean;
+ addSeriesFilterFilter: typeof addSeriesFilterFilter;
+ editSeriesFilterFilter: typeof editSeriesFilterFilter;
+ editSeriesFilter: typeof editSeriesFilter;
+ removeSeriesFilterFilter: typeof removeSeriesFilterFilter;
+ hideHeader?: boolean;
+ emptyMessage?: any;
+}
+
+function FilterSeries(props: Props) {
+ const { canDelete, hideHeader = false, emptyMessage = 'Add user event or filter to define the series by clicking Add Step.' } = props;
+ const [expanded, setExpanded] = useState(true)
+ const { series, seriesIndex } = props;
+
+ const onAddFilter = (filter) => {
+ series.filter.addFilter(filter)
+ }
+
+ const onUpdateFilter = (filterIndex, filter) => {
+ series.filter.updateFilter(filterIndex, filter)
+ }
+
+ const onChangeEventsOrder = (e, { name, value }) => {
+ series.filter.updateKey(name, value)
+ // props.editSeriesFilter(seriesIndex, { eventsOrder: value });
+ }
+
+ const onRemoveFilter = (filterIndex) => {
+ series.filter.removeFilter(filterIndex)
+ // props.removeSeriesFilterFilter(seriesIndex, filterIndex);
+ }
+
+ return (
+
+
+
+ props.updateSeries(seriesIndex, { name }) } />
+
+
+
+
+
+
+
+
setExpanded(!expanded)} className="ml-3">
+
+
+
+
+ { expanded && (
+ <>
+
+ { series.filter.filters.length > 0 ? (
+
+ ): (
+
{emptyMessage}
+ )}
+
+
+ >
+ )}
+
+ );
+}
+
+export default connect(null, {
+ edit,
+ updateSeries,
+ addSeriesFilterFilter,
+ editSeriesFilterFilter,
+ editSeriesFilter,
+ removeSeriesFilterFilter,
+})(observer(FilterSeries));
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx
new file mode 100644
index 000000000..5d25e9de9
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx
@@ -0,0 +1,57 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { Icon } from 'UI';
+
+interface Props {
+ name: string;
+ onUpdate: (name) => void;
+ seriesIndex?: number;
+}
+function SeriesName(props: Props) {
+ const { seriesIndex = 1 } = props;
+ const [editing, setEditing] = useState(false)
+ const [name, setName] = useState(props.name)
+ const ref = useRef(null)
+
+ const write = ({ target: { value, name } }) => {
+ setName(value)
+ }
+
+ const onBlur = () => {
+ setEditing(false)
+ props.onUpdate(name)
+ }
+
+ useEffect(() => {
+ if (editing) {
+ ref.current.focus()
+ }
+ }, [editing])
+
+ useEffect(() => {
+ setName(props.name)
+ }, [props.name])
+
+ // const { name } = props;
+ return (
+
+ { editing ? (
+
setEditing(true)}
+ />
+ ) : (
+
{name.trim() === '' ? 'Seriess ' + (seriesIndex + 1) : name }
+ )}
+
+
setEditing(true)}>
+
+ );
+}
+
+export default SeriesName;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts
new file mode 100644
index 000000000..90e63cdb6
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts
@@ -0,0 +1 @@
+export { default } from './SeriesName';
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/FilterSeries/index.ts b/frontend/app/components/Dashboard/components/FilterSeries/index.ts
new file mode 100644
index 000000000..5882e382a
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FilterSeries/index.ts
@@ -0,0 +1 @@
+export { default } from './FilterSeries'
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx
new file mode 100644
index 000000000..d358075db
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Button, PageTitle, Link } from 'UI';
+import { withSiteId, dashboardMetricCreate } from 'App/routes';
+
+function MetricsView(props) {
+ return (
+
+ );
+}
+
+export default MetricsView;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/MetricsView/index.ts b/frontend/app/components/Dashboard/components/MetricsView/index.ts
new file mode 100644
index 000000000..bfebac6b9
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/MetricsView/index.ts
@@ -0,0 +1 @@
+export { default } from './MetricsView';
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx
new file mode 100644
index 000000000..631a3b791
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx
@@ -0,0 +1,160 @@
+import React from 'react';
+import DropdownPlain from 'Shared/DropdownPlain';
+import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions';
+import { FilterKey } from 'Types/filter/filterType';
+import { useDashboardStore } from '../../store/store';
+import { useObserver } from 'mobx-react-lite';
+import { HelpText, Button, Icon } from 'UI'
+import FilterSeries from '../FilterSeries';
+
+interface Props {
+ // metric: any,
+ // editWidget: (metric, shouldFetch?) => void
+}
+
+function WidgetForm(props: Props) {
+ // const { metric } = props;
+ const store: any = useDashboardStore();
+ const metric = store.currentWidget;
+
+ const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries');
+ const tableOptions = metricOf.filter(i => i.type === 'table');
+ const isTable = metric.metricType === 'table';
+ const isTimeSeries = metric.metricType === 'timeseries';
+ const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions);
+
+ const write = ({ target: { value, name } }) => store.editWidget({ [ name ]: value }, false);
+ const writeOption = (e, { value, name }) => {
+ store.editWidget({ [ name ]: value }, false);
+
+ if (name === 'metricValue') {
+ store.editWidget({ metricValue: [value] }, false);
+ }
+
+ if (name === 'metricOf') {
+ if (value === FilterKey.ISSUE) {
+ store.editWidget({ metricValue: ['all'] }, false);
+ }
+ }
+
+ if (name === 'metricType') {
+ if (value === 'timeseries') {
+ store.editWidget({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }, false);
+ } else if (value === 'table') {
+ store.editWidget({ metricOf: tableOptions[0].value, viewType: 'table' }, false);
+ }
+ }
+ };
+
+ return useObserver(() => (
+
+
+
Metric Type
+
+
+
+ {metric.metricType === 'timeseries' && (
+ <>
+ of
+
+ >
+ )}
+
+ {metric.metricType === 'table' && (
+ <>
+ of
+
+ >
+ )}
+
+ {metric.metricOf === FilterKey.ISSUE && (
+ <>
+ issue type
+
+ >
+ )}
+
+ {metric.metricType === 'table' && (
+ <>
+ showing
+
+ >
+ )}
+
+
+
+
+
+ {`${isTable ? 'Filter by' : 'Chart Series'}`}
+ {!isTable && (
+ metric.addSeries()}
+ >Add Series
+ )}
+
+
+ {metric.series.length > 0 && metric.series.slice(0, isTable ? 1 : metric.series.length).map((series: any, index: number) => (
+
+ removeSeries(index)}
+ onRemoveSeries={() => metric.removeSeries(index)}
+ canDelete={metric.series.length > 1}
+ emptyMessage={isTable ?
+ 'Filter data using any event or attribute. Use Add Step button below to do so.' :
+ 'Add user event or filter to define the series by clicking Add Step.'
+ }
+ />
+
+ ))}
+
+
+
+
Save
+
+
+
+ Delete
+
+
+
+ Add to Dashboard
+
+
+
+
+ ));
+}
+
+export default WidgetForm;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetForm/index.ts b/frontend/app/components/Dashboard/components/WidgetForm/index.ts
new file mode 100644
index 000000000..283f9ec23
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetForm/index.ts
@@ -0,0 +1 @@
+export { default } from './WidgetForm';
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx
new file mode 100644
index 000000000..4e6de9662
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import cn from 'classnames';
+import WidgetWrapper from '../../WidgetWrapper';
+import { useDashboardStore } from '../../store/store';
+import { Loader, NoContent, SegmentSelection, Icon } from 'UI';
+import DateRange from 'Shared/DateRange';
+import { useObserver } from 'mobx-react-lite';
+
+interface Props {
+ className?: string;
+}
+function WidgetPreview(props: Props) {
+ const { className = '' } = props;
+ const store: any = useDashboardStore();
+ const metric = store.currentWidget;
+ const isTimeSeries = metric.metricType === 'timeseries';
+ const isTable = metric.metricType === 'table';
+
+ const chagneViewType = (e, { name, value }) => {
+ metric.update({ [ name ]: value });
+ }
+
+ const onDateChange = (changedDates) => {
+ // setPeriod({ ...changedDates, rangeName: changedDates.rangeValue })
+ metric.update({ ...changedDates, rangeName: changedDates.rangeValue });
+ }
+
+ return useObserver(() => (
+
+
+
Trend
+
+ {isTimeSeries && (
+ <>
+
Visualization
+
+ >
+ )}
+
+ {isTable && (
+ <>
+
Visualization
+
+ >
+ )}
+
+
Time Range
+
+
+
+
+
+
+
+ ));
+}
+
+export default WidgetPreview;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/index.ts b/frontend/app/components/Dashboard/components/WidgetPreview/index.ts
new file mode 100644
index 000000000..9d28f8146
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetPreview/index.ts
@@ -0,0 +1 @@
+export { default } from './WidgetPreview';
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx
new file mode 100644
index 000000000..60eb1a569
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import { NoContent } from 'UI';
+import cn from 'classnames';
+import { useDashboardStore } from '../../store/store';
+import SessionItem from 'Shared/SessionItem';
+
+interface Props {
+ className?: string;
+}
+function WidgetSessions(props: Props) {
+ const { className = '' } = props;
+ const store: any = useDashboardStore();
+ const widget = store.currentWidget;
+
+ return (
+
+
+
Sessions
+ {/*
Showing all sessions between {startTime} and {endTime}
*/}
+
+
+
+
+ {widget.sessions.map((session: any) => (
+
+ ))}
+
+
+
+ );
+}
+
+export default WidgetSessions;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/index.ts b/frontend/app/components/Dashboard/components/WidgetSessions/index.ts
new file mode 100644
index 000000000..8f309114a
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetSessions/index.ts
@@ -0,0 +1 @@
+export { default } from './WidgetSessions';
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx
new file mode 100644
index 000000000..cbe2d38e4
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx
@@ -0,0 +1,41 @@
+import React, { useState } from 'react';
+import { withRouter } from 'react-router-dom';
+import { useDashboardStore } from '../../store/store';
+import WidgetForm from '../WidgetForm';
+import WidgetPreview from '../WidgetPreview';
+import WidgetSessions from '../WidgetSessions';
+import { Icon } from 'UI';
+
+interface Props {
+
+}
+function WidgetView(props: Props) {
+ const [expanded, setExpanded] = useState(true);
+ const store: any = useDashboardStore();
+ const widget = store.currentWidget;
+ return (
+
+
+
+
{widget.name}
+
+
setExpanded(!expanded)}
+ className="flex items-center cursor-pointer select-none"
+ >
+ {expanded ? 'Collapse' : 'Expand'}
+
+
+
+
+
+ { expanded &&
}
+
+
+
+
+
+ );
+}
+
+export default withRouter(WidgetView);
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/WidgetView/index.ts b/frontend/app/components/Dashboard/components/WidgetView/index.ts
similarity index 100%
rename from frontend/app/components/Dashboard/WidgetView/index.ts
rename to frontend/app/components/Dashboard/components/WidgetView/index.ts
diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts
index 832988ce9..8f03c4e59 100644
--- a/frontend/app/components/Dashboard/store/dashboardStore.ts
+++ b/frontend/app/components/Dashboard/store/dashboardStore.ts
@@ -18,6 +18,7 @@ export default class DashboardStore {
selectedDashboard: observable,
isLoading: observable,
+ resetCurrentWidget: action,
addDashboard: action,
removeDashboard: action,
updateDashboard: action,
@@ -31,6 +32,7 @@ export default class DashboardStore {
toJson: action,
fromJson: action,
setSiteId: action,
+ editWidget: action,
})
@@ -48,6 +50,14 @@ export default class DashboardStore {
// }, 3000)
}
+ resetCurrentWidget() {
+ this.currentWidget = new Widget()
+ }
+
+ editWidget(widget: Widget) {
+ this.currentWidget.update(widget)
+ }
+
fetchList() {
this.isLoading = true
@@ -210,7 +220,7 @@ function getRandomWidget() {
const widget = new Widget();
widget.widgetId = Math.floor(Math.random() * 100);
widget.name = randomMetricName();
- widget.type = "random";
+ // widget.type = "random";
widget.colSpan = Math.floor(Math.random() * 2) + 1;
return widget;
}
diff --git a/frontend/app/components/Dashboard/store/filter.ts b/frontend/app/components/Dashboard/store/filter.ts
new file mode 100644
index 000000000..c9bdd3452
--- /dev/null
+++ b/frontend/app/components/Dashboard/store/filter.ts
@@ -0,0 +1,37 @@
+import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
+
+export default class Filter {
+ name: string = ''
+ filters: any[] = []
+ eventsOrder: string = 'then'
+
+ constructor() {
+ makeAutoObservable(this, {
+ addFilter: action,
+ removeFilter: action,
+ updateKey: action,
+ })
+ }
+
+ addFilter(filter: any) {
+ filter.value = [""]
+ if (filter.hasOwnProperty('filters')) {
+ filter.filters = filter.filters.map(i => ({ ...i, value: [""] }))
+ }
+ this.filters.push(filter)
+ console.log('addFilter', this.filters)
+ }
+
+ updateFilter(index: number, filter: any) {
+ this.filters[index] = filter
+ console.log('updateFilter', this.filters)
+ }
+
+ updateKey(key, value) {
+ this[key] = value
+ }
+
+ removeFilter(index: number) {
+ this.filters.splice(index, 1)
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/store/filterSeries.ts b/frontend/app/components/Dashboard/store/filterSeries.ts
new file mode 100644
index 000000000..284e50230
--- /dev/null
+++ b/frontend/app/components/Dashboard/store/filterSeries.ts
@@ -0,0 +1,22 @@
+// import Filter from 'Types/filter';
+import Filter from './filter'
+import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
+
+export default class FilterSeries {
+ seriesId?: any = undefined
+ name: string = "Series 1"
+ filter: Filter = new Filter()
+
+ constructor() {
+ makeAutoObservable(this, {
+ name: observable,
+ filter: observable,
+
+ update: action,
+ })
+ }
+
+ update(key, value) {
+ this[key] = value
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts
index 2cd1bee32..27f5dfd46 100644
--- a/frontend/app/components/Dashboard/store/widget.ts
+++ b/frontend/app/components/Dashboard/store/widget.ts
@@ -1,9 +1,17 @@
import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
+import Filter from 'Types/filter';
+import FilterSeries from "./filterSeries";
export default class Widget {
widgetId: any = undefined
name: string = "New Metric"
- type: string = ""
+ metricType: string = "timeseries"
+ metricOf: string = "sessionCount"
+ metricValue: string = ""
+ viewType: string = "lineChart"
+ series: FilterSeries[] = []
+ sessions: [] = []
+
position: number = 0
data: any = {}
isLoading: boolean = false
@@ -15,26 +23,46 @@ export default class Widget {
makeAutoObservable(this, {
widgetId: observable,
name: observable,
- type: observable,
+ metricType: observable,
+ metricOf: observable,
position: observable,
data: observable,
isLoading: observable,
isValid: observable,
dashboardId: observable,
+ addSeries: action,
colSpan: observable,
fromJson: action,
toJson: action,
validate: action,
update: action,
+ udpateKey: action,
})
+
+ const filterSeries = new FilterSeries()
+ this.series.push(filterSeries)
}
+ udpateKey(key: string, value: any) {
+ this[key] = value
+ }
+
+ removeSeries(index: number) {
+ this.series.splice(index, 1)
+ }
+
+ addSeries() {
+ const series = new FilterSeries()
+ series.name = "Series " + (this.series.length + 1)
+ this.series.push(series)
+ }
+
+
fromJson(json: any) {
runInAction(() => {
this.widgetId = json.widgetId
this.name = json.name
- this.type = json.type
this.data = json.data
})
return this
@@ -44,7 +72,6 @@ export default class Widget {
return {
widgetId: this.widgetId,
name: this.name,
- type: this.type,
data: this.data
}
}
diff --git a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx
index 5327308dd..c86f626ac 100644
--- a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx
+++ b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx
@@ -86,13 +86,15 @@ function FilterSeries(props: Props) {
{emptyMessage}
)}
-
-
-
-
+
>
)}
diff --git a/frontend/app/components/shared/DropdownPlain/DropdownPlain.css b/frontend/app/components/shared/DropdownPlain/DropdownPlain.css
index dd7b9a2a5..772060e87 100644
--- a/frontend/app/components/shared/DropdownPlain/DropdownPlain.css
+++ b/frontend/app/components/shared/DropdownPlain/DropdownPlain.css
@@ -1,6 +1,6 @@
.dropdown {
display: flex !important;
- padding: 4px;
+ padding: 4px 8px;
border-radius: 3px;
color: $gray-darkest;
font-weight: 500;
diff --git a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx
index 8eba891bb..110702ac7 100644
--- a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx
+++ b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx
@@ -1,6 +1,8 @@
import React, { useState} from 'react';
import FilterItem from '../FilterItem';
import { SegmentSelection, Popup } from 'UI';
+import { List } from 'immutable';
+import { useObserver } from 'mobx-react-lite';
interface Props {
// filters: any[]; // event/filter
@@ -12,16 +14,16 @@ interface Props {
}
function FilterList(props: Props) {
const { filter, hideEventsOrder = false } = props;
- const filters = filter.filters;
- const hasEvents = filter.filters.filter(i => i.isEvent).size > 0;
- const hasFilters = filter.filters.filter(i => !i.isEvent).size > 0;
+ const filters = List(filter.filters);
+ const hasEvents = filters.filter((i: any) => i.isEvent).size > 0;
+ const hasFilters = filters.filter((i: any) => !i.isEvent).size > 0;
let rowIndex = 0;
const onRemoveFilter = (filterIndex) => {
props.onRemoveFilter(filterIndex);
}
- return (
+ return useObserver(() => (
{ hasEvents && (
<>
@@ -54,7 +56,7 @@ function FilterList(props: Props) {
)}
- {filters.map((filter, filterIndex) => filter.isEvent ? (
+ {filters.map((filter: any, filterIndex: any) => filter.isEvent ? (
{hasEvents &&
}
FILTERS
- {filters.map((filter, filterIndex) => !filter.isEvent ? (
+ {filters.map((filter: any, filterIndex: any) => !filter.isEvent ? (
)}
- );
+ ));
}
export default FilterList;
\ No newline at end of file
diff --git a/frontend/app/components/ui/DropdownPlain/DropdownPlain.js b/frontend/app/components/ui/DropdownPlain/DropdownPlain.js
index 389b75b93..ce1fc506d 100644
--- a/frontend/app/components/ui/DropdownPlain/DropdownPlain.js
+++ b/frontend/app/components/ui/DropdownPlain/DropdownPlain.js
@@ -21,7 +21,6 @@ function DropdownPlain({ name, label, options, onChange, defaultValue, wrapperSt
options={ options }
onChange={ onChange }
defaultValue={ defaultValue || options[ 0 ].value }
- icon={null}
disabled={disabled}
icon={ }
/>
diff --git a/frontend/app/routes.js b/frontend/app/routes.js
index 8693ae3e6..4aea022a4 100644
--- a/frontend/app/routes.js
+++ b/frontend/app/routes.js
@@ -101,6 +101,7 @@ export const testBuilderNew = () => '/test-builder';
export const testBuilder = (testId = ':testId') => `/test-builder/${ testId }`;
export const dashboard = () => '/dashboard';
+export const dashboardMetrics = () => '/dashboard/metrics';
export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash);
export const dashboardMetricDetails = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash);
@@ -121,6 +122,7 @@ const REQUIRED_SITE_ID_ROUTES = [
assist(),
dashboard(''),
dashboardSelected(''),
+ dashboardMetrics(''),
// dashboardMetricCreate(''),
dashboardMetricDetails(''),
metricCreate(''),
@@ -152,7 +154,15 @@ export function isRoute(route, path){
routeParts.every((p, i) => p.startsWith(':') || p === pathParts[ i ]);
}
-const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), dashboardSelected(''), errors(), onboarding('')];
+const SITE_CHANGE_AVALIABLE_ROUTES = [
+ sessions(),
+ assist(),
+ dashboard(),
+ dashboardMetrics(''),
+ dashboardSelected(''),
+ errors(),
+ onboarding('')
+];
export const siteChangeAvaliable = path => SITE_CHANGE_AVALIABLE_ROUTES.some(r => isRoute(r, path));
export const redirects = Object.entries({
diff --git a/frontend/app/svg/icons/bar-chart-line.svg b/frontend/app/svg/icons/bar-chart-line.svg
index e3f0cf255..c46c1d8f8 100644
--- a/frontend/app/svg/icons/bar-chart-line.svg
+++ b/frontend/app/svg/icons/bar-chart-line.svg
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/frontend/app/svg/icons/chevron-double-left.svg b/frontend/app/svg/icons/chevron-double-left.svg
index 7181fd111..8694e8195 100644
--- a/frontend/app/svg/icons/chevron-double-left.svg
+++ b/frontend/app/svg/icons/chevron-double-left.svg
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/frontend/app/svg/icons/chevron-double-right.svg b/frontend/app/svg/icons/chevron-double-right.svg
index 73e1b352d..ee4323e06 100644
--- a/frontend/app/svg/icons/chevron-double-right.svg
+++ b/frontend/app/svg/icons/chevron-double-right.svg
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/frontend/app/svg/icons/columns-gap.svg b/frontend/app/svg/icons/columns-gap.svg
new file mode 100644
index 000000000..f4ed1f9ae
--- /dev/null
+++ b/frontend/app/svg/icons/columns-gap.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/app/svg/icons/controller.svg b/frontend/app/svg/icons/controller.svg
index 15e777456..7c9499541 100644
--- a/frontend/app/svg/icons/controller.svg
+++ b/frontend/app/svg/icons/controller.svg
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/frontend/app/svg/icons/flag-na.svg b/frontend/app/svg/icons/flag-na.svg
index ca42ac405..33796e2f0 100644
--- a/frontend/app/svg/icons/flag-na.svg
+++ b/frontend/app/svg/icons/flag-na.svg
@@ -1,3 +1,3 @@
-
+
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 0da29c3d5..28fd4d982 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -93,7 +93,7 @@ module.exports = {
// 'transitionProperty',
// 'transitionTimingFunction',
// 'translate',
- // 'userSelect',
+ 'userSelect',
// 'verticalAlign',
'visibility',
'whitespace',