feat(ui) - dashboards wip
This commit is contained in:
parent
391189e894
commit
94370f028f
16 changed files with 223 additions and 90 deletions
|
|
@ -48,8 +48,13 @@ const FunnelIssue = withSiteIdUpdater(FunnelIssueDetails);
|
|||
const withSiteId = routes.withSiteId;
|
||||
const withObTab = routes.withObTab;
|
||||
|
||||
const DASHBOARD_PATH = routes.dashboardSelected();
|
||||
const WIDGET_PATAH = routes.dashboardMetric();
|
||||
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 WIDGET_PATAH = routes.dashboardMetric();
|
||||
const SESSIONS_PATH = routes.sessions();
|
||||
const ASSIST_PATH = routes.assist();
|
||||
const ERRORS_PATH = routes.errors();
|
||||
|
|
@ -182,8 +187,13 @@ class Router extends React.Component {
|
|||
{ siteIdList.length === 0 &&
|
||||
<Redirect to={ routes.client(routes.CLIENT_TABS.SITES) } />
|
||||
}
|
||||
<Route exact strict path={ withSiteId(DASHBOARD_PATH, siteIdList) } component={ Dashboard } />
|
||||
<Route path={ withSiteId(DASHBOARD_PATH, siteIdList) } component={ Dashboard } />
|
||||
{/* <Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } /> */}
|
||||
|
||||
<Route exact strict path={ withSiteId(ASSIST_PATH, siteIdList) } component={ Assist } />
|
||||
<Route exact strict path={ withSiteId(ERRORS_PATH, siteIdList) } component={ Errors } />
|
||||
<Route exact strict path={ withSiteId(ERROR_PATH, siteIdList) } component={ Errors } />
|
||||
|
|
|
|||
|
|
@ -1,46 +1,63 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Switch, Route, Redirect } from 'react-router';
|
||||
import withPageTitle from 'HOCs/withPageTitle';
|
||||
import { observer, useObserver } from "mobx-react-lite";
|
||||
import { withDashboardStore } from './store/store';
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useDashboardStore } from './store/store';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import DashboardView from './components/DashboardView';
|
||||
import { dashboardSelected, dashboardMetric, withSiteId } from 'App/routes';
|
||||
import { dashboardSelected, dashboardMetricDetails, dashboardMetricCreate, withSiteId } from 'App/routes';
|
||||
import DashboardSideMenu from './components/DashboardSideMenu';
|
||||
import WidgetView from './WidgetView';
|
||||
|
||||
function NewDashboard(props) {
|
||||
const { store, match: { params: { siteId, dashboardId, metricId } } } = props;
|
||||
const { match: { params: { siteId, dashboardId, metricId } } } = props;
|
||||
const store: any = useDashboardStore();
|
||||
const dashboard = store.selectedDashboard;
|
||||
|
||||
useEffect(() => {
|
||||
store.setSiteId(siteId);
|
||||
if (dashboardId) {
|
||||
store.selectDashboardById(dashboardId);
|
||||
} else {
|
||||
store.selectDefaultDashboard();
|
||||
}
|
||||
}, [dashboardId]);
|
||||
}, []);
|
||||
|
||||
return useObserver(() => (
|
||||
useEffect(() => {
|
||||
console.log('dashboardId', dashboardId);
|
||||
if (!dashboard || !dashboard.dashboardId) {
|
||||
if (dashboardId) {
|
||||
store.selectDashboardById(dashboardId);
|
||||
} else {
|
||||
store.selectDefaultDashboard();
|
||||
}
|
||||
}
|
||||
|
||||
}, [dashboard]);
|
||||
|
||||
return (
|
||||
<div className="page-margin container-90">
|
||||
<div className="side-menu">
|
||||
<DashboardSideMenu />
|
||||
</div>
|
||||
<div className="side-menu-margined">
|
||||
<Switch>
|
||||
<Route exact path={withSiteId(dashboardSelected(dashboardId), siteId)}>
|
||||
<DashboardView dashboard={dashboard} />
|
||||
</Route>
|
||||
<Route exact path={withSiteId(dashboardMetric(dashboardId, metricId), siteId)}>
|
||||
<h1>Metric</h1>
|
||||
</Route>
|
||||
<Redirect to={withSiteId(dashboardSelected(dashboardId), siteId )} />
|
||||
</Switch>
|
||||
{ dashboard && dashboard.dashboardId && (
|
||||
<Switch>
|
||||
<Route exact strict path={withSiteId(dashboardSelected(dashboard.dashboardId), siteId)}>
|
||||
<DashboardView dashboard={dashboard} />
|
||||
</Route>
|
||||
<Route exact strict path={withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId)}>
|
||||
<WidgetView />
|
||||
</Route>
|
||||
<Route exact strict path={withSiteId(dashboardMetricDetails(dashboard.dashboardId, metricId), siteId)}>
|
||||
<WidgetView />
|
||||
</Route>
|
||||
{/* <Route exact strict path={withSiteId((dashboard.dashboardId), siteId)}>
|
||||
<WidgetView />
|
||||
</Route> */}
|
||||
<Redirect exact strict to={withSiteId(dashboardSelected(dashboard.dashboardId), siteId )} />
|
||||
</Switch>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
export default withPageTitle('New Dashboard')(
|
||||
withRouter(withDashboardStore(NewDashboard))
|
||||
withRouter(observer(NewDashboard))
|
||||
);
|
||||
|
|
@ -22,7 +22,7 @@ function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) {
|
|||
)}
|
||||
|
||||
<div className={stl.divider} />
|
||||
<div className="my-3">
|
||||
<div className="my-3">
|
||||
<SideMenuitem
|
||||
id="menu-manage-alerts"
|
||||
title="Manage Alerts"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
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 (
|
||||
<div>
|
||||
Widget view
|
||||
<div className="bg-white rounded border">
|
||||
<div className="p-3">
|
||||
<h1 className="mb-0 text-2xl">{widget.name}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WidgetView;
|
||||
export default withRouter(WidgetView);
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react';
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useDashboardStore } from '../store/store';
|
||||
import cn from 'classnames';
|
||||
import { Link } from 'UI';
|
||||
import { dashboardMetric, withSiteId } from 'App/routes';
|
||||
import { ItemMenu } from 'UI';
|
||||
|
||||
function WidgetWrapper(props) {
|
||||
const { widget } = props;
|
||||
|
|
@ -12,23 +10,33 @@ function WidgetWrapper(props) {
|
|||
const siteId = store.siteId;
|
||||
|
||||
return (
|
||||
<div className={cn("border rounded", 'col-span-' + widget.colSpan)} style={{ userSelect: 'none'}}>
|
||||
<Link to={withSiteId(dashboardMetric(12, widget.widgetId), siteId)}>
|
||||
<div className="p-3 cursor-pointer bg-white border-b flex items-center justify-between">
|
||||
<div className={cn("border rounded bg-white", 'col-span-' + widget.colSpan)} style={{ userSelect: 'none'}}>
|
||||
{/* <Link to={withSiteId(dashboardMetricDetails(dashboard.dashboardId, widget.widgetId), siteId)}> */}
|
||||
<div className="p-3 cursor-pointe border-b flex items-center justify-between">
|
||||
{widget.name} - {widget.position}
|
||||
<div>
|
||||
<button className="btn btn-sm btn-outline-primary" onClick={() => dashboard.removeWidget(widget.widgetId)}>
|
||||
<ItemMenu
|
||||
items={[
|
||||
{
|
||||
text: 'Edit',
|
||||
onClick: () => {
|
||||
console.log('edit');
|
||||
}
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/* <button className="btn btn-sm btn-outline-primary" onClick={() => dashboard.removeWidget(widget.widgetId)}>
|
||||
remove
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white h-40">
|
||||
<div className="h-40">
|
||||
|
||||
</div>
|
||||
</Link>
|
||||
{/* </Link> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(WidgetWrapper);
|
||||
export default WidgetWrapper;
|
||||
|
|
@ -1,39 +1,51 @@
|
|||
import { useObserver } from 'mobx-react-lite';
|
||||
import { useObserver, observer, useLocalObservable } from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
import { SideMenuitem, SideMenuHeader } from 'UI';
|
||||
import { SideMenuitem, SideMenuHeader, Icon } from 'UI';
|
||||
import { withDashboardStore } from '../../store/store';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { withSiteId, dashboardSelected } from 'App/routes';
|
||||
|
||||
function DashboardSideMenu(props) {
|
||||
const { store } = props;
|
||||
const { selectedDashboard } = store;
|
||||
const { store, history } = props;
|
||||
const { dashboardId } = store.selectedDashboard;
|
||||
|
||||
const onItemClick = (dashboard) => {
|
||||
store.selectDashboardById(dashboard.dashboardId);
|
||||
const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(store.siteId));
|
||||
// console.log('path', path);
|
||||
// history.push(path);
|
||||
};
|
||||
|
||||
return useObserver(() => (
|
||||
return (
|
||||
<div>
|
||||
<SideMenuHeader className="mb-4" text="Dashboards" />
|
||||
{store.dashboards.map(item => (
|
||||
<SideMenuitem
|
||||
key={ item.key }
|
||||
active={ item.active }
|
||||
key={ item.key }
|
||||
active={item.dashboardId === dashboardId}
|
||||
title={ item.name }
|
||||
iconName={ item.icon }
|
||||
onClick={() => onItemClick(item)}
|
||||
|
||||
leading = {(
|
||||
<div className="ml-2 flex items-center">
|
||||
<div className="p-1"><Icon name="user-friends" color="gray-light" size="16" /></div>
|
||||
{item.isPinned && <div className="p-1"><Icon name="pin-fill" size="16" /></div>}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
<div className="border-t w-full my-2" />
|
||||
<div className="">
|
||||
<div className="w-full">
|
||||
<SideMenuitem
|
||||
id="menu-manage-alerts"
|
||||
title="Metrics"
|
||||
iconName="bar-chart-line"
|
||||
// onClick={() => setShowAlerts(true)}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
<div className="border-t w-full my-2" />
|
||||
<div className="my-3">
|
||||
<div className="my-3 w-full">
|
||||
<SideMenuitem
|
||||
id="menu-manage-alerts"
|
||||
title="Alerts"
|
||||
|
|
@ -42,7 +54,7 @@ function DashboardSideMenu(props) {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
export default withDashboardStore(DashboardSideMenu);
|
||||
export default withDashboardStore(withRouter(observer(DashboardSideMenu)));
|
||||
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import WidgetWrapper from '../../WidgetWrapper';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { withDashboardStore } from '../../store/store';
|
||||
import { Button, PageTitle } from 'UI';
|
||||
import { Button, PageTitle, Link } from 'UI';
|
||||
import { withSiteId, dashboardMetricCreate } from 'App/routes';
|
||||
|
||||
function DashboardView(props) {
|
||||
const { store } = props;
|
||||
|
|
@ -12,7 +13,7 @@ function DashboardView(props) {
|
|||
<div>
|
||||
<div className="flex items-center mb-4">
|
||||
<PageTitle title={dashboard.name} className="mr-3" />
|
||||
<Button primary size="small">Add Metric</Button>
|
||||
<Link to={withSiteId(dashboardMetricCreate(dashboard.dashboardId), store.siteId)}><Button primary size="small">Add Metric</Button></Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{list && list.map(item => <WidgetWrapper widget={item} key={item.widgetId} />)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { makeAutoObservable, makeObservable, observable, action, runInAction, computed, reaction } from "mobx"
|
||||
import { makeAutoObservable, observable, action, runInAction } from "mobx"
|
||||
import Widget from "./widget"
|
||||
// import APIClient from 'App/api_client';
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ export default class Dashboard {
|
|||
widgets: Widget[] = []
|
||||
isValid: boolean = false
|
||||
isPinned: boolean = false
|
||||
currentWidget: Widget = new Widget()
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this, {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default class DashboardStore {
|
|||
selectedDashboard: Dashboard | null = new Dashboard()
|
||||
isLoading: boolean = false
|
||||
siteId: any = null
|
||||
currentWidget: Widget = new Widget()
|
||||
|
||||
private client = new APIClient()
|
||||
|
||||
|
|
@ -25,7 +26,8 @@ export default class DashboardStore {
|
|||
getDashboardByIndex: action,
|
||||
getDashboardCount: action,
|
||||
getDashboardIndexByDashboardId: action,
|
||||
selectDashboardById: action,
|
||||
selectDashboardById: action,
|
||||
selectDefaultDashboard: action,
|
||||
toJson: action,
|
||||
fromJson: action,
|
||||
setSiteId: action,
|
||||
|
|
@ -34,7 +36,7 @@ export default class DashboardStore {
|
|||
|
||||
// TODO remove this sample data
|
||||
this.dashboards = sampleDashboards
|
||||
this.selectedDashboard = sampleDashboards[0]
|
||||
// this.selectedDashboard = sampleDashboards[0]
|
||||
|
||||
// setInterval(() => {
|
||||
// this.selectedDashboard?.addWidget(getRandomWidget())
|
||||
|
|
@ -185,7 +187,7 @@ export default class DashboardStore {
|
|||
}
|
||||
|
||||
selectDashboardById = (dashboardId: any) => {
|
||||
this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || null;;
|
||||
this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard();
|
||||
}
|
||||
|
||||
setSiteId = (siteId: any) => {
|
||||
|
|
@ -193,15 +195,13 @@ export default class DashboardStore {
|
|||
}
|
||||
|
||||
selectDefaultDashboard = () => {
|
||||
const pinnedDashboard = this.dashboards.find(d => d.isPinned)
|
||||
if (this.dashboards.length > 0) {
|
||||
const pinnedDashboard = this.dashboards.find(d => d.isPinned)
|
||||
if (pinnedDashboard) {
|
||||
this.selectedDashboard = pinnedDashboard
|
||||
} else {
|
||||
this.selectedDashboard = this.dashboards[0]
|
||||
}
|
||||
} else {
|
||||
this.selectedDashboard = new Dashboard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -209,17 +209,38 @@ export default class DashboardStore {
|
|||
function getRandomWidget() {
|
||||
const widget = new Widget();
|
||||
widget.widgetId = Math.floor(Math.random() * 100);
|
||||
widget.name = "Widget " + Math.floor(Math.random() * 100);
|
||||
widget.name = randomMetricName();
|
||||
widget.type = "random";
|
||||
widget.colSpan = Math.floor(Math.random() * 2) + 1;
|
||||
return widget;
|
||||
}
|
||||
|
||||
function getRandomDashboard(id: any = null) {
|
||||
function generateRandomPlaceName() {
|
||||
const placeNames = [
|
||||
"New York",
|
||||
"Los Angeles",
|
||||
"Chicago",
|
||||
"Houston",
|
||||
"Philadelphia",
|
||||
"Phoenix",
|
||||
"San Antonio",
|
||||
"San Diego",
|
||||
]
|
||||
return placeNames[Math.floor(Math.random() * placeNames.length)]
|
||||
}
|
||||
|
||||
|
||||
function randomMetricName () {
|
||||
const metrics = ["Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders"];
|
||||
return metrics[Math.floor(Math.random() * metrics.length)];
|
||||
}
|
||||
|
||||
function getRandomDashboard(id: any = null, isPinned = false) {
|
||||
const dashboard = new Dashboard();
|
||||
dashboard.name = "Random Dashboard";
|
||||
dashboard.dashboardId = id ? id : "random-dashboard-" + Math.floor(Math.random() * 10);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
dashboard.name = generateRandomPlaceName();
|
||||
dashboard.dashboardId = id ? id : Math.floor(Math.random() * 10);
|
||||
dashboard.isPinned = isPinned;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const widget = getRandomWidget();
|
||||
widget.position = i;
|
||||
dashboard.addWidget(widget);
|
||||
|
|
@ -228,8 +249,8 @@ function getRandomDashboard(id: any = null) {
|
|||
}
|
||||
|
||||
const sampleDashboards = [
|
||||
getRandomDashboard(12),
|
||||
getRandomDashboard(),
|
||||
getRandomDashboard(),
|
||||
getRandomDashboard(),
|
||||
getRandomDashboard(1, true),
|
||||
getRandomDashboard(2),
|
||||
getRandomDashboard(3),
|
||||
getRandomDashboard(4),
|
||||
]
|
||||
|
|
@ -2,7 +2,7 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "m
|
|||
|
||||
export default class Widget {
|
||||
widgetId: any = undefined
|
||||
name: string = ""
|
||||
name: string = "New Metric"
|
||||
type: string = ""
|
||||
position: number = 0
|
||||
data: any = {}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,22 @@ export default class ItemMenu extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className={ styles.wrapper }>
|
||||
<div
|
||||
{/* <div
|
||||
ref={ (ref) => { this.menuBtnRef = ref; } }
|
||||
className={ styles.menuBtn }
|
||||
onClick={ this.toggleMenu }
|
||||
role="button"
|
||||
tabIndex="-1"
|
||||
/>
|
||||
/> */}
|
||||
<div
|
||||
ref={ (ref) => { this.menuBtnRef = ref; } }
|
||||
className="w-10 h-10 cursor-pointer bg-white rounded-full flex items-center justify-center hover:bg-gray-lightest"
|
||||
onClick={ this.toggleMenu }
|
||||
role="button"
|
||||
tabIndex="-1"
|
||||
>
|
||||
<Icon name="ellipsis-v" size="16" />
|
||||
</div>
|
||||
<div
|
||||
className={ styles.menu }
|
||||
data-displayed={ displayed }
|
||||
|
|
@ -58,9 +67,11 @@ export default class ItemMenu extends React.PureComponent {
|
|||
role="menuitem"
|
||||
tabIndex="-1"
|
||||
>
|
||||
<div className={ styles.iconWrapper }>
|
||||
<Icon name={ icon } size="13" color="gray-dark" />
|
||||
</div>
|
||||
{ icon && (
|
||||
<div className={ styles.iconWrapper }>
|
||||
<Icon name={ icon } size="13" color="gray-dark" />
|
||||
</div>
|
||||
)}
|
||||
<div>{ text }</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
}
|
||||
|
||||
.menuBtn {
|
||||
@mixin icon-before ellipsis-v, $gray-darkest, 25px {
|
||||
@mixin icon-before ellipsis-v, $gray-darkest, 18px {
|
||||
margin: 5px;
|
||||
}
|
||||
width: 36px;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,20 @@ import { Icon, Popup } from 'UI';
|
|||
import cn from 'classnames';
|
||||
import stl from './sideMenuItem.css';
|
||||
|
||||
function SideMenuitem({ iconBg = false, iconColor = "gray-dark", iconSize = 18, className, iconName = null, title, active = false, disabled = false, onClick, deleteHandler, ...props }) {
|
||||
function SideMenuitem({
|
||||
iconBg = false,
|
||||
iconColor = "gray-dark",
|
||||
iconSize = 18,
|
||||
className,
|
||||
iconName = null,
|
||||
title,
|
||||
active = false,
|
||||
disabled = false,
|
||||
onClick,
|
||||
deleteHandler,
|
||||
leading = null,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<Popup
|
||||
trigger={
|
||||
|
|
@ -17,14 +30,17 @@ function SideMenuitem({ iconBg = false, iconColor = "gray-dark", iconSize = 18,
|
|||
onClick={disabled ? null : onClick}
|
||||
{...props}
|
||||
>
|
||||
<div className={ cn(stl.iconLabel, 'flex items-center', { [stl.disabled] : disabled })}>
|
||||
<div className={ cn('flex items-center w-full', { [stl.disabled] : disabled })}>
|
||||
<div className={cn("flex items-center", stl.iconLabel)}>
|
||||
{ iconName && (
|
||||
<div className="flex items-center justify-center w-8 h-8 mr-2">
|
||||
<div className={cn({ "w-8 h-8 rounded-full relative opacity-20" : iconBg }, iconBg)} style={{ opacity: '0.2'}} />
|
||||
<Icon name={ iconName } size={ iconSize } color={active ? 'teal' : iconColor} className="absolute" />
|
||||
</div>
|
||||
)}
|
||||
<span className={stl.title}>{ title }</span>
|
||||
<div className="flex items-center justify-center w-8 h-8 mr-2">
|
||||
<div className={cn({ "w-8 h-8 rounded-full relative opacity-20" : iconBg }, iconBg)} style={{ opacity: '0.2'}} />
|
||||
<Icon name={ iconName } size={ iconSize } color={active ? 'teal' : iconColor} className="absolute" />
|
||||
</div>
|
||||
)}
|
||||
<span className={stl.title}>{ title }</span>
|
||||
</div>
|
||||
{ leading && leading }
|
||||
</div>
|
||||
{deleteHandler &&
|
||||
<div onClick={deleteHandler} className={stl.actions}><Icon name="trash" size="14" /></div>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@
|
|||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $teal;
|
||||
& svg {
|
||||
fill: $teal;
|
||||
& .iconLabel {
|
||||
color: $teal;
|
||||
|
||||
& svg {
|
||||
fill: $teal;
|
||||
}
|
||||
}
|
||||
|
||||
& .actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,14 +102,34 @@ export const testBuilder = (testId = ':testId') => `/test-builder/${ testId }`;
|
|||
|
||||
export const dashboard = () => '/dashboard';
|
||||
export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash);
|
||||
export const dashboardMetric = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash);
|
||||
|
||||
export const dashboardMetricDetails = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash);
|
||||
export const dashboardMetricCreate = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }/metric/create`, hash);
|
||||
export const metricCreate = () => `/metric/create`;
|
||||
export const metricDetails = (id = ':metricId', hash) => hashed(`/metric/${ id }`, hash);
|
||||
|
||||
|
||||
export const RESULTS_QUERY_KEY = 'results';
|
||||
export const METRICS_QUERY_KEY = 'metrics';
|
||||
export const SOURCE_QUERY_KEY = 'source';
|
||||
export const WIDGET_QUERY_KEY = 'widget';
|
||||
|
||||
const REQUIRED_SITE_ID_ROUTES = [ liveSession(''), session(''), sessions(), assist(), dashboard(''), error(''), errors(), onboarding(''), funnel(''), funnelIssue(''), ];
|
||||
const REQUIRED_SITE_ID_ROUTES = [
|
||||
liveSession(''),
|
||||
session(''),
|
||||
sessions(),
|
||||
assist(),
|
||||
dashboard(''),
|
||||
dashboardSelected(''),
|
||||
// dashboardMetricCreate(''),
|
||||
dashboardMetricDetails(''),
|
||||
metricCreate(''),
|
||||
error(''),
|
||||
errors(),
|
||||
onboarding(''),
|
||||
funnel(''),
|
||||
funnelIssue(''),
|
||||
];
|
||||
const routeNeedsSiteId = path => REQUIRED_SITE_ID_ROUTES.some(r => path.startsWith(r));
|
||||
const siteIdToUrl = (siteId = ':siteId') => {
|
||||
if (Array.isArray(siteId)) {
|
||||
|
|
@ -132,7 +152,7 @@ export function isRoute(route, path){
|
|||
routeParts.every((p, i) => p.startsWith(':') || p === pathParts[ i ]);
|
||||
}
|
||||
|
||||
const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), errors(), onboarding('')];
|
||||
const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), dashboardSelected(''), errors(), onboarding('')];
|
||||
export const siteChangeAvaliable = path => SITE_CHANGE_AVALIABLE_ROUTES.some(r => isRoute(r, path));
|
||||
|
||||
export const redirects = Object.entries({
|
||||
|
|
|
|||
3
frontend/app/svg/icons/pin-fill.svg
Normal file
3
frontend/app/svg/icons/pin-fill.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="bi bi-pin-fill" viewBox="0 0 16 16">
|
||||
<path d="M4.146.146A.5.5 0 0 1 4.5 0h7a.5.5 0 0 1 .5.5c0 .68-.342 1.174-.646 1.479-.126.125-.25.224-.354.298v4.431l.078.048c.203.127.476.314.751.555C12.36 7.775 13 8.527 13 9.5a.5.5 0 0 1-.5.5h-4v4.5c0 .276-.224 1.5-.5 1.5s-.5-1.224-.5-1.5V10h-4a.5.5 0 0 1-.5-.5c0-.973.64-1.725 1.17-2.189A5.921 5.921 0 0 1 5 6.708V2.277a2.77 2.77 0 0 1-.354-.298C4.342 1.674 4 1.179 4 .5a.5.5 0 0 1 .146-.354z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 490 B |
Loading…
Add table
Reference in a new issue