fix(ui): add new metric selection modals, fix dashboard view

This commit is contained in:
sylenien 2022-08-10 15:39:03 +02:00 committed by Delirium
parent 58a42c72d5
commit 7806c15806
14 changed files with 503 additions and 166 deletions

View file

@ -9,12 +9,20 @@ import cn from 'classnames';
import { withSiteId } from 'App/routes';
import withPermissions from 'HOCs/withPermissions'
function NewDashboard(props: RouteComponentProps<{}>) {
const { history, match: { params: { siteId, dashboardId, metricId } } } = props;
interface RouterProps {
siteId: string;
dashboardId: string;
metricId: string;
}
function NewDashboard(props: RouteComponentProps<RouterProps>) {
const { history, match: { params: { siteId, dashboardId } } } = props;
const { dashboardStore } = useStore();
const loading = useObserver(() => dashboardStore.isLoading);
const isMetricDetails = history.location.pathname.includes('/metrics/') || history.location.pathname.includes('/metric/');
const isDashboardDetails = history.location.pathname.includes('/dashboard/')
const shouldHideMenu = isMetricDetails || isDashboardDetails;
useEffect(() => {
dashboardStore.fetchList().then((resp) => {
if (parseInt(dashboardId) > 0) {
@ -33,16 +41,16 @@ function NewDashboard(props: RouteComponentProps<{}>) {
return useObserver(() => (
<Loader loading={loading}>
<div className="page-margin container-90">
<div className={cn("side-menu", { 'hidden' : isMetricDetails })}>
<div className={cn("side-menu", { 'hidden' : shouldHideMenu })}>
<DashboardSideMenu siteId={siteId} />
</div>
<div
className={cn({
"side-menu-margined" : !isMetricDetails,
"container-70" : isMetricDetails
"side-menu-margined" : !shouldHideMenu,
"container-70" : shouldHideMenu
})}
>
<DashboardRouter siteId={siteId} />
<DashboardRouter />
</div>
</div>
</Loader>

View file

@ -1,17 +1,32 @@
import React from 'react';
import { Button, PageTitle, Icon, Link } from 'UI';
import { Button, PageTitle, Icon } from 'UI';
import withPageTitle from 'HOCs/withPageTitle';
import { useStore } from 'App/mstore';
import { withSiteId } from 'App/routes';
import DashboardList from './DashboardList';
import DashboardSearch from './DashboardSearch';
function DashboardsView() {
function DashboardsView({ history, siteId }: { history: any, siteId: string }) {
const { dashboardStore } = useStore();
const onAddDashboardClick = () => {
dashboardStore.initDashboard();
dashboardStore
.save(dashboardStore.dashboardInstance)
.then(async (syncedDashboard) => {
dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
history.push(withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId))
})
}
return (
<div style={{ maxWidth: '1300px', margin: 'auto'}} className="bg-white rounded p-4">
<div className="flex items-center mb-4 justify-between px-4">
<div className="flex items-baseline mr-3">
<PageTitle title="Dashboards" className="" />
</div>
<Link to={'/metrics/create'}><Button variant="primary">Create Dashboard</Button></Link>
<Button variant="primary" onClick={onAddDashboardClick}>Create Dashboard</Button>
<div className="ml-auto w-1/4">
<DashboardSearch />
</div>

View file

@ -6,9 +6,16 @@ import cn from 'classnames';
import { useStore } from 'App/mstore';
import { Loader } from 'UI';
function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds }) {
interface IWiProps {
category: Record<string, any>
onClick: (category: Record<string, any>) => void
isSelected: boolean
selectedWidgetIds: string[]
}
export function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds }: IWiProps) {
const selectedCategoryWidgetsCount = useObserver(() => {
return category.widgets.filter(widget => selectedWidgetIds.includes(widget.metricId)).length;
return category.widgets.filter((widget: any) => selectedWidgetIds.includes(widget.metricId)).length;
});
return (
<div

View file

@ -3,23 +3,22 @@ import { useObserver } from 'mobx-react-lite';
import DashboardMetricSelection from '../DashboardMetricSelection';
import DashboardForm from '../DashboardForm';
import { Button } from 'UI';
import { withRouter } from 'react-router-dom';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal';
import { dashboardMetricCreate, withSiteId, dashboardSelected } from 'App/routes';
import { dashboardMetricCreate, withSiteId } from 'App/routes';
interface Props {
interface Props extends RouteComponentProps {
history: any
siteId?: string
dashboardId?: string
onMetricAdd?: () => void;
}
function DashboardModal(props) {
function DashboardModal(props: Props) {
const { history, siteId, dashboardId } = props;
const { dashboardStore } = useStore();
const selectedWidgetsCount = useObserver(() => dashboardStore.selectedWidgets.length);
const { hideModal } = useModal();
const loadingTemplates = useObserver(() => dashboardStore.loadingTemplates);
const dashboard = useObserver(() => dashboardStore.dashboardInstance);
const loading = useObserver(() => dashboardStore.isSaving);

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Switch, Route } from 'react-router';
import { withRouter } from 'react-router-dom';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import {
metrics,
@ -18,18 +18,18 @@ import WidgetView from '../WidgetView';
import WidgetSubDetailsView from '../WidgetSubDetailsView';
import DashboardsView from '../DashboardList';
function DashboardViewSelected({ siteId, dashboardId }) {
function DashboardViewSelected({ siteId, dashboardId }: { siteId: string, dashboardId: string }) {
return (
<DashboardView siteId={siteId} dashboardId={dashboardId} />
)
}
interface Props {
history: any
interface Props extends RouteComponentProps {
match: any
}
function DashboardRouter(props: Props) {
const { match: { params: { siteId, dashboardId, metricId } } } = props;
const { match: { params: { siteId, dashboardId } }, history } = props;
return (
<div>
<Switch>
@ -46,7 +46,7 @@ function DashboardRouter(props: Props) {
</Route>
<Route exact path={withSiteId(dashboard(), siteId)}>
<DashboardsView />
<DashboardsView siteId={siteId} history={history} />
</Route>
<Route exact strict path={withSiteId(dashboardMetricDetails(dashboardId), siteId)}>

View file

@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { useStore } from "App/mstore";
import { Button, PageTitle, Loader, NoContent } from "UI";
import { Button, PageTitle, Loader } from "UI";
import { withSiteId } from "App/routes";
import withModal from "App/components/Modal/withModal";
import DashboardWidgetGrid from "../DashboardWidgetGrid";
@ -15,8 +15,6 @@ import withPageTitle from "HOCs/withPageTitle";
import withReport from "App/components/hocs/withReport";
import DashboardOptions from "../DashboardOptions";
import SelectDateRange from "Shared/SelectDateRange";
// @ts-ignore
import DashboardIcon from "../../../../svg/dashboard-icn.svg";
import { Tooltip } from "react-tippy";
import Breadcrumb from 'Shared/Breadcrumb';
@ -31,23 +29,18 @@ type Props = IProps & RouteComponentProps;
function DashboardView(props: Props) {
const { siteId, dashboardId } = props;
const { dashboardStore } = useStore();
const { showModal } = useModal();
const [focusTitle, setFocusedInput] = React.useState(true);
const [showEditModal, setShowEditModal] = React.useState(false);
const { showModal } = useModal();
const showAlertModal = dashboardStore.showAlertModal;
const loading = dashboardStore.fetchingDashboard;
const dashboards = dashboardStore.dashboards;
const dashboard: any = dashboardStore.selectedDashboard;
const period = dashboardStore.period;
const queryParams = new URLSearchParams(props.location.search);
useEffect(() => {
if (!dashboard || !dashboard.dashboardId) return;
dashboardStore.fetch(dashboard.dashboardId);
}, [dashboard]);
const trimQuery = () => {
if (!queryParams.has("modal")) return;
queryParams.delete("modal");
@ -60,21 +53,24 @@ function DashboardView(props: Props) {
};
useEffect(() => {
if (!dashboardId || (!dashboard && dashboardStore.dashboards.length > 0)) dashboardStore.selectDefaultDashboard();
if (queryParams.has("modal")) {
onAddWidgets();
trimQuery();
}
}, []);
useEffect(() => {
dashboardStore.selectDefaultDashboard();
}, [siteId])
useEffect(() => {
dashboardStore.selectDashboardById(dashboardId);
const isExists = dashboardStore.getDashboardById(dashboardId);
if (!isExists) {
props.history.push(withSiteId(`/dashboard`, siteId))
}
}, [dashboardId])
useEffect(() => {
if (!dashboard || !dashboard.dashboardId) return;
dashboardStore.fetch(dashboard.dashboardId);
}, [dashboard]);
const onAddWidgets = () => {
dashboardStore.initDashboard(dashboard);
showModal(
@ -87,11 +83,6 @@ function DashboardView(props: Props) {
);
};
const onAddDashboardClick = () => {
dashboardStore.initDashboard();
showModal(<DashboardModal siteId={siteId} />, { right: true })
}
const onEdit = (isTitle: boolean) => {
dashboardStore.initDashboard(dashboard);
setFocusedInput(isTitle);
@ -107,131 +98,104 @@ function DashboardView(props: Props) {
})
) {
dashboardStore.deleteDashboard(dashboard).then(() => {
dashboardStore.selectDefaultDashboard().then(
({ dashboardId }) => {
props.history.push(
withSiteId(`/dashboard/${dashboardId}`, siteId)
);
},
() => {
props.history.push(withSiteId("/dashboard", siteId));
}
);
props.history.push(withSiteId(`/dashboard`, siteId));
});
}
};
if (!dashboard) return null;
return (
<Loader loading={loading}>
<NoContent
show={
dashboards.length === 0 ||
!dashboard ||
!dashboard.dashboardId
}
title={
<div className="flex items-center justify-center flex-col">
<object
style={{ width: "180px" }}
type="image/svg+xml"
data={DashboardIcon}
className="no-result-icon"
<div style={{ maxWidth: "1300px", margin: "auto" }}>
<DashboardEditModal
show={showEditModal}
closeHandler={() => setShowEditModal(false)}
focusTitle={focusTitle}
/>
<Breadcrumb
items={[
{
label: 'Dashboards',
to: withSiteId('/dashboard', siteId),
},
{ label: dashboard && dashboard.name || '' },
]}
/>
<div className="flex items-center mb-4 justify-between">
<div className="flex items-center" style={{ flex: 3 }}>
<PageTitle
// @ts-ignore
title={
<Tooltip
delay={100}
arrow
title="Double click to rename"
>
{dashboard?.name}
</Tooltip>
}
onDoubleClick={() => onEdit(true)}
className="mr-3 select-none hover:border-dotted hover:border-b border-gray-medium cursor-pointer"
actionButton={
<Button
variant="primary"
onClick={onAddWidgets}
>
Add Metric
</Button>
}
/>
<span>
Gather and analyze <br /> important metrics in one
place.
</span>
</div>
}
size="small"
>
<div style={{ maxWidth: "1300px", margin: "auto" }}>
<DashboardEditModal
show={showEditModal}
closeHandler={() => setShowEditModal(false)}
focusTitle={focusTitle}
/>
<Breadcrumb
items={[
{
label: 'Dashboards',
to: withSiteId('/dashboard', siteId),
},
{ label: dashboard && dashboard.name || '' },
]}
/>
<div className="flex items-center mb-4 justify-between">
<div className="flex items-center" style={{ flex: 3 }}>
<PageTitle
// @ts-ignore
title={
<Tooltip
delay={100}
arrow
title="Double click to rename"
>
{dashboard?.name}
</Tooltip>
}
onDoubleClick={() => onEdit(true)}
className="mr-3 select-none hover:border-dotted hover:border-b border-gray-medium cursor-pointer"
actionButton={
<Button
variant="primary"
onClick={onAddWidgets}
>
Add Metric
</Button>
<div
className="flex items-center"
style={{ flex: 1, justifyContent: "end" }}
>
<div
className="flex items-center flex-shrink-0 justify-end"
style={{ width: "300px" }}
>
<SelectDateRange
style={{ width: "300px" }}
period={period}
onChange={(period: any) =>
dashboardStore.setPeriod(period)
}
right={true}
/>
</div>
<div
className="flex items-center"
style={{ flex: 1, justifyContent: "end" }}
>
<div
className="flex items-center flex-shrink-0 justify-end"
style={{ width: "300px" }}
>
<SelectDateRange
style={{ width: "300px" }}
period={period}
onChange={(period: any) =>
dashboardStore.setPeriod(period)
}
right={true}
/>
</div>
<div className="mx-4" />
<div className="flex items-center flex-shrink-0">
<DashboardOptions
editHandler={onEdit}
deleteHandler={onDelete}
renderReport={props.renderReport}
isTitlePresent={!!dashboard?.description}
/>
</div>
<div className="mx-4" />
<div className="flex items-center flex-shrink-0">
<DashboardOptions
editHandler={onEdit}
deleteHandler={onDelete}
renderReport={props.renderReport}
isTitlePresent={!!dashboard?.description}
/>
</div>
</div>
<div>
<h2 className="my-4 font-normal color-gray-dark">
{dashboard?.description}
</h2>
</div>
<DashboardWidgetGrid
siteId={siteId}
dashboardId={dashboardId}
onEditHandler={onAddWidgets}
id="report"
/>
<AlertFormModal
showModal={showAlertModal}
onClose={() =>
dashboardStore.updateKey("showAlertModal", false)
}
/>
</div>
</NoContent>
<div>
<h2
className="my-4 w-fit font-normal color-gray-dark border-dashed border-b cursor-pointer border-b-gray-medium"
onDoubleClick={() => onEdit(false)}
>
{dashboard?.description || "Describe the purpose of this dashboard"}
</h2>
</div>
<DashboardWidgetGrid
siteId={siteId}
dashboardId={dashboardId}
onEditHandler={onAddWidgets}
id="report"
/>
<AlertFormModal
showModal={showAlertModal}
onClose={() =>
dashboardStore.updateKey("showAlertModal", false)
}
/>
</div>
</Loader>
);
}

View file

@ -0,0 +1,98 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Button } from 'UI';
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal';
import { dashboardMetricCreate, withSiteId } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom';
interface IProps extends RouteComponentProps {
metrics: any[];
siteId: string;
title: string;
description: string;
}
function AddMetric({ metrics, history, siteId, title, description }: IProps) {
const { dashboardStore } = useStore();
const { hideModal } = useModal();
const dashboard = dashboardStore.selectedDashboard;
const selectedWidgetIds = dashboardStore.selectedWidgets.map((widget: any) => widget.metricId);
const queryParams = new URLSearchParams(location.search);
const onSave = () => {
if (selectedWidgetIds.length === 0) return;
dashboardStore
.save(dashboard)
.then(async (syncedDashboard) => {
if (dashboard.exists()) {
await dashboardStore.fetch(dashboard.dashboardId);
}
dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
})
.then(hideModal);
};
const onCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId);
if (!queryParams.has('modal')) history.push('?modal=addMetric');
history.push(path);
hideModal();
};
return (
<div style={{ maxWidth: '85vw', width: 1200 }}>
<div className="border-l shadow h-screen" style={{ backgroundColor: '#FAFAFA', zIndex: 999, width: '100%' }}>
<div className="mb-6 pt-8 px-8 flex items-start justify-between">
<div className="flex flex-col">
<h1 className="text-2xl">{title}</h1>
<div className="text-disabled-text">{description}</div>
</div>
{title.includes('Custom') ? (
<div>
<span className="text-md link" onClick={onCreateNew}>
+ Create new
</span>
</div>
) : (
<div>
Dont find the one you need?
<span className="text-md link ml-2" onClick={onCreateNew}>
+ Create custom metric
</span>
</div>
)}
</div>
<div className="grid h-full grid-cols-4 gap-4 px-8 items-start py-1" style={{ maxHeight: 'calc(100vh - 160px)', overflowY: 'auto' }}>
{metrics.map((metric: any) => (
<WidgetWrapper
key={metric.metricId}
widget={metric}
active={selectedWidgetIds.includes(metric.metricId)}
isTemplate={true}
isWidget={metric.metricType === 'predefined'}
onClick={() => dashboardStore.toggleWidgetSelection(metric)}
/>
))}
</div>
<div className="py-4 border-t px-8 bg-white w-full flex items-center justify-between">
<div>
{'Selected '}
<span className="font-semibold">{selectedWidgetIds.length}</span>
{' out of '}
<span className="font-semibold">{metrics.length}</span>
</div>
<Button variant="primary" disabled={selectedWidgetIds.length === 0} onClick={onSave}>
Add Selected
</Button>
</div>
</div>
</div>
);
}
export default withRouter(observer(AddMetric));

View file

@ -0,0 +1,83 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Icon } from 'UI';
import { useModal } from 'App/components/Modal';
import { useStore } from 'App/mstore';
import AddMetric from './AddMetric';
import AddPredefinedMetric from './AddPredefinedMetric';
interface AddMetricButtonProps {
iconName: string;
title: string;
description: string;
onClick: () => void;
}
function AddMetricButton({ iconName, title, description, onClick }: AddMetricButtonProps) {
return (
<div
onClick={onClick}
className="px-4 py-8 flex items-center flex-col bg-tealx-lightest hover:bg-active-blue group border-teal-light border cursor-pointer"
>
<div className="p-4 mb-2 bg-gray-light rounded-full group-hover:bg-teal-light">
<Icon name={iconName} size={26} />
</div>
<div className="font-bold mb-2">{title}</div>
<div className="text-disabled-test w-2/3">{description}</div>
</div>
);
}
function AddMetricContainer({ siteId }: any) {
const { showModal } = useModal();
const [categories, setCategories] = React.useState<Record<string, any>[]>([]);
const { dashboardStore } = useStore();
React.useEffect(() => {
dashboardStore?.fetchTemplates(true).then((cats) => setCategories(cats));
}, []);
const onAddCustomMetrics = () => {
dashboardStore.initDashboard(dashboardStore.selectedDashboard);
showModal(
<AddMetric
siteId={siteId}
title="Custom Metrics"
description="Metrics that are manually created by you or your team."
metrics={categories.find((category) => category.name === 'custom')?.widgets}
/>,
{ right: true }
);
};
const onAddPredefinedMetrics = () => {
dashboardStore.initDashboard(dashboardStore.selectedDashboard);
showModal(
<AddPredefinedMetric
siteId={siteId}
title="Ready-Made Metrics"
description="Curated metrics predfined by OpenReplay."
categories={categories.filter((category) => category.name !== 'custom')}
/>,
{ right: true }
);
};
return (
<div className="bg-white rounded p-8 grid grid-cols-2 gap-4 w-4/5 m-auto">
<AddMetricButton
title="+ Add custom Metric"
description="Metrics that are manually created by you or your team"
iconName="bar-chart-line"
onClick={onAddCustomMetrics}
/>
<AddMetricButton
title="+ Add Ready-Made Metric"
description="Curated metrics predfined by OpenReplay."
iconName="bar-chart-line"
onClick={onAddPredefinedMetrics}
/>
</div>
);
}
export default observer(AddMetricContainer);

View file

@ -0,0 +1,152 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Button } from 'UI';
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal';
import { dashboardMetricCreate, withSiteId } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { WidgetCategoryItem } from 'App/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection';
interface IProps extends RouteComponentProps {
categories: Record<string, any>[];
siteId: string;
title: string;
description: string;
}
function AddPredefinedMetric({ categories, history, siteId, title, description }: IProps) {
const { dashboardStore } = useStore();
const { hideModal } = useModal();
const [allCheck, setAllCheck] = React.useState(false);
const [activeCategory, setActiveCategory] = React.useState<Record<string, any>>();
const scrollContainer = React.useRef<HTMLDivElement>(null);
const dashboard = dashboardStore.selectedDashboard;
const selectedWidgetIds = dashboardStore.selectedWidgets.map((widget: any) => widget.metricId);
const queryParams = new URLSearchParams(location.search);
const totalMetricCount = categories.reduce((acc, category) => acc + category.widgets.length, 0);
React.useEffect(() => {
dashboardStore?.fetchTemplates(true).then((categories) => {
const defaultCategory = categories.filter((category: any) => category.name !== 'custom')[0];
setActiveCategory(defaultCategory);
});
}, []);
React.useEffect(() => {
if (scrollContainer.current) {
scrollContainer.current.scrollTop = 0;
}
}, [activeCategory, scrollContainer.current]);
const handleWidgetCategoryClick = (category: any) => {
setActiveCategory(category);
setAllCheck(false);
};
const onSave = () => {
if (selectedWidgetIds.length === 0) return;
dashboardStore
.save(dashboard)
.then(async (syncedDashboard) => {
if (dashboard.exists()) {
await dashboardStore.fetch(dashboard.dashboardId);
}
dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
})
.then(hideModal);
};
const onCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId);
if (!queryParams.has('modal')) history.push('?modal=addMetric');
history.push(path);
hideModal();
};
const toggleAllMetrics = ({ target: { checked } }: any) => {
setAllCheck(checked);
if (checked) {
dashboardStore.selectWidgetsByCategory(activeCategory.name);
} else {
dashboardStore.removeSelectedWidgetByCategory(activeCategory);
}
};
return (
<div style={{ maxWidth: '85vw', width: 1200 }}>
<div className="border-l shadow h-screen" style={{ backgroundColor: '#FAFAFA', zIndex: 999, width: '100%' }}>
<div className="mb-6 pt-8 px-8 flex items-start justify-between">
<div className="flex flex-col">
<h1 className="text-2xl">{title}</h1>
<div className="text-disabled-text">{description}</div>
</div>
{title.includes('Custom') ? (
<div>
<span className="text-md link" onClick={onCreateNew}>
+ Create new
</span>
</div>
) : (
<div>
Dont find the one you need?
<span className="text-md link ml-2" onClick={onCreateNew}>
+ Create custom metric
</span>
</div>
)}
</div>
<div className="flex px-8">
<div style={{ flex: 3 }}>
<div className="grid grid-cols-1 gap-4 py-1 pr-2" style={{ maxHeight: 'calc(100vh - 160px)', overflowY: 'auto' }}>
{activeCategory &&
categories.map((category) => (
<WidgetCategoryItem
key={category.name}
onClick={handleWidgetCategoryClick}
category={category}
isSelected={activeCategory.name === category.name}
selectedWidgetIds={selectedWidgetIds}
/>
))}
</div>
</div>
<div
className="grid h-full grid-cols-4 gap-4 items-start py-1"
style={{ maxHeight: 'calc(100vh - 160px)', overflowY: 'auto', flex: 9 }}
>
{activeCategory &&
activeCategory.widgets.map((metric: any) => (
<WidgetWrapper
key={metric.metricId}
widget={metric}
active={selectedWidgetIds.includes(metric.metricId)}
isTemplate={true}
isWidget={metric.metricType === 'predefined'}
onClick={() => dashboardStore.toggleWidgetSelection(metric)}
/>
))}
</div>
</div>
<div className="py-4 border-t px-8 bg-white w-full flex items-center justify-between">
<div>
{'Selected '}
<span className="font-semibold">{selectedWidgetIds.length}</span>
{' out of '}
<span className="font-semibold">{totalMetricCount}</span>
</div>
<Button variant="primary" disabled={selectedWidgetIds.length === 0} onClick={onSave}>
Add Selected
</Button>
</div>
</div>
</div>
);
}
export default withRouter(observer(AddPredefinedMetric));

View file

@ -1,8 +1,9 @@
import React from 'react';
import { useStore } from 'App/mstore';
import WidgetWrapper from '../WidgetWrapper';
import { NoContent, Button, Loader } from 'UI';
import { NoContent, Loader } from 'UI';
import { useObserver } from 'mobx-react-lite';
import AddMetricContainer from './AddMetricContainer'
interface Props {
siteId: string,
@ -18,16 +19,14 @@ function DashboardWidgetGrid(props: Props) {
const list: any = useObserver(() => dashboard?.widgets);
return useObserver(() => (
// @ts-ignore
<Loader loading={loading}>
<NoContent
show={list.length === 0}
icon="no-metrics-chart"
title="No metrics added to this dashboard"
title="Build your dashboard"
subtext={
<div className="flex items-center justify-center flex-col">
<p>Metrics helps you visualize trends from sessions captured by OpenReplay</p>
<Button variant="primary" onClick={props.onEditHandler}>Add Metric</Button>
</div>
<AddMetricContainer siteId={siteId} />
}
>
<div className="grid gap-4 grid-cols-4 items-start pb-10" id={props.id}>

View file

@ -10,9 +10,7 @@ interface Props{
siteId: number;
}
function MetricsView(props: Props) {
const { siteId } = props;
const { metricStore } = useStore();
const metricsCount = useObserver(() => metricStore.metrics.length);
React.useEffect(() => {
metricStore.fetchList();
@ -22,7 +20,6 @@ function MetricsView(props: Props) {
<div className="flex items-center mb-4 justify-between px-4">
<div className="flex items-baseline mr-3">
<PageTitle title="Metrics" className="" />
<span className="text-2xl color-gray-medium ml-2">{metricsCount}</span>
</div>
<Link to={'/metrics/create'}><Button variant="primary">Create Metric</Button></Link>
<div className="ml-auto w-1/4">

View file

@ -54,7 +54,8 @@ export default class SiteDropdown extends React.PureComponent {
this.props.clearSearchLive();
mstore.initClient();
};
mstore.dashboardStore.selectDefaultDashboard();
}
render() {
const {

View file

@ -8,6 +8,7 @@ interface Props {
onClick?: () => void;
disabled?: boolean;
type?: 'button' | 'submit' | 'reset';
variant?: 'default' | 'primary' | 'text' | 'text-primary' | 'text-red' | 'outline'
loading?: boolean;
icon?: string;
rounded?: boolean;

View file

@ -53,7 +53,7 @@ export interface IDashboardSotre {
selectWidgetsByCategory: (category: string) => void;
toggleAllSelectedWidgets: (isSelected: boolean) => void;
removeSelectedWidgetByCategory(category: string): void;
removeSelectedWidgetByCategory(category: Record<string, any>): void;
toggleWidgetSelection(widget: IWidget): void;
initDashboard(dashboard?: IDashboard): void;
@ -72,6 +72,7 @@ export interface IDashboardSotre {
getDashboardCount(): void;
updateDashboard(dashboard: IDashboard): void;
selectDashboardById(dashboardId: string): void;
getDashboardById(dashboardId: string): boolean;
setSiteId(siteId: any): void;
selectDefaultDashboard(): Promise<IDashboard>;
@ -372,6 +373,18 @@ export default class DashboardStore implements IDashboardSotre {
new Dashboard();
};
getDashboardById = (dashboardId: string) => {
const dashboard = this.dashboards.find((d) => d.dashboardId == dashboardId)
if (dashboard) {
this.selectedDashboard = dashboard
return true;
} else {
this.selectedDashboard = null
return false;
}
}
setSiteId = (siteId: any) => {
this.siteId = siteId;
};