fix(ui) - duration and not data message for sessions and errors

This commit is contained in:
Shekar Siri 2022-06-27 16:32:03 +02:00
parent a3e99a6217
commit db6609d908
8 changed files with 775 additions and 545 deletions

4
frontend/.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"tabWidth": 4,
"useTabs": false
}

View file

@ -1,59 +1,75 @@
import React, { useEffect } from 'react';
import { Pagination, NoContent } from 'UI';
import ErrorListItem from 'App/components/Dashboard/components/Errors/ErrorListItem';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useModal } from 'App/components/Modal';
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
import React, { useEffect } from "react";
import { Pagination, NoContent } from "UI";
import ErrorListItem from "App/components/Dashboard/components/Errors/ErrorListItem";
import { withRouter, RouteComponentProps } from "react-router-dom";
import { useModal } from "App/components/Modal";
import ErrorDetailsModal from "App/components/Dashboard/components/Errors/ErrorDetailsModal";
import { useStore } from "App/mstore";
import { overPastString } from "App/dateRange";
interface Props {
metric: any,
isEdit: any,
history: any,
location: any,
metric: any;
isEdit: any;
history: any;
location: any;
}
function CustomMetricTableErrors(props: RouteComponentProps<Props>) {
const { metric, isEdit = false } = props;
const errorId = new URLSearchParams(props.location.search).get("errorId");
const { showModal, hideModal } = useModal();
const { dashboardStore } = useStore();
const period = dashboardStore.period;
const onErrorClick = (e: any, error: any) => {
e.stopPropagation();
props.history.replace({search: (new URLSearchParams({errorId : error.errorId})).toString()});
}
props.history.replace({
search: new URLSearchParams({ errorId: error.errorId }).toString(),
});
};
useEffect(() => {
if (!errorId) return;
showModal(<ErrorDetailsModal errorId={errorId} />, { right: true, onClose: () => {
if (props.history.location.pathname.includes("/dashboard")) {
props.history.replace({search: ""});
}
}});
showModal(<ErrorDetailsModal errorId={errorId} />, {
right: true,
onClose: () => {
if (props.history.location.pathname.includes("/dashboard")) {
props.history.replace({ search: "" });
}
},
});
return () => {
hideModal();
}
}, [errorId])
};
}, [errorId]);
return (
<NoContent
title="No data available over the "
title={`No errors found ${overPastString(period)}`}
show={!metric.data.errors || metric.data.errors.length === 0}
size="small"
>
<div className="pb-4">
{metric.data.errors && metric.data.errors.map((error: any, index: any) => (
<div key={index} className="broder-b last:border-none">
<ErrorListItem error={error} onClick={(e) => onErrorClick(e, error)} />
</div>
))}
{metric.data.errors &&
metric.data.errors.map((error: any, index: any) => (
<div key={index} className="broder-b last:border-none">
<ErrorListItem
error={error}
onClick={(e) => onErrorClick(e, error)}
/>
</div>
))}
{isEdit && (
<div className="my-6 flex items-center justify-center">
<Pagination
page={metric.page}
totalPages={Math.ceil(metric.data.total / metric.limit)}
onPageChange={(page: any) => metric.updateKey('page', page)}
totalPages={Math.ceil(
metric.data.total / metric.limit
)}
onPageChange={(page: any) =>
metric.updateKey("page", page)
}
limit={metric.limit}
debounceRequest={500}
/>
@ -68,14 +84,17 @@ function CustomMetricTableErrors(props: RouteComponentProps<Props>) {
);
}
export default withRouter(CustomMetricTableErrors) as React.FunctionComponent<RouteComponentProps<Props>>;
export default withRouter(CustomMetricTableErrors) as React.FunctionComponent<
RouteComponentProps<Props>
>;
const ViewMore = ({ total, limit }: any) => total > limit && (
<div className="mt-4 flex items-center justify-center cursor-pointer w-fit mx-auto">
<div className="text-center">
<div className="color-teal text-lg">
All <span className="font-medium">{total}</span> errors
const ViewMore = ({ total, limit }: any) =>
total > limit && (
<div className="mt-4 flex items-center justify-center cursor-pointer w-fit mx-auto">
<div className="text-center">
<div className="color-teal text-lg">
All <span className="font-medium">{total}</span> errors
</div>
</div>
</div>
</div>
);
);

View file

@ -1,7 +1,9 @@
import { useObserver } from 'mobx-react-lite';
import React from 'react';
import SessionItem from 'Shared/SessionItem';
import { Pagination, NoContent } from 'UI';
import { useObserver } from "mobx-react-lite";
import React from "react";
import SessionItem from "Shared/SessionItem";
import { Pagination, NoContent } from "UI";
import { useStore } from "App/mstore";
import { overPastString } from "App/dateRange";
interface Props {
metric: any;
@ -11,26 +13,41 @@ interface Props {
function CustomMetricTableSessions(props: Props) {
const { isEdit = false, metric } = props;
const { dashboardStore } = useStore();
const period = dashboardStore.period;
return useObserver(() => (
<NoContent
show={!metric || !metric.data || !metric.data.sessions || metric.data.sessions.length === 0}
show={
!metric ||
!metric.data ||
!metric.data.sessions ||
metric.data.sessions.length === 0
}
size="small"
title="No sessions found over the selected time period"
title={`No sessions found ${overPastString(period)}`}
>
<div className="pb-4">
{metric.data.sessions && metric.data.sessions.map((session: any, index: any) => (
<div className="border-b last:border-none" key={session.sessionId}>
<SessionItem session={session} />
</div>
))}
{metric.data.sessions &&
metric.data.sessions.map((session: any, index: any) => (
<div
className="border-b last:border-none"
key={session.sessionId}
>
<SessionItem session={session} />
</div>
))}
{isEdit && (
<div className="mt-6 flex items-center justify-center">
<Pagination
page={metric.page}
totalPages={Math.ceil(metric.data.total / metric.limit)}
onPageChange={(page: any) => metric.updateKey('page', page)}
totalPages={Math.ceil(
metric.data.total / metric.limit
)}
onPageChange={(page: any) =>
metric.updateKey("page", page)
}
limit={metric.data.total}
debounceRequest={500}
/>
@ -47,12 +64,13 @@ function CustomMetricTableSessions(props: Props) {
export default CustomMetricTableSessions;
const ViewMore = ({ total, limit }: any) => total > limit && (
<div className="mt-4 flex items-center justify-center cursor-pointer w-fit mx-auto">
<div className="text-center">
<div className="color-teal text-lg">
All <span className="font-medium">{total}</span> sessions
const ViewMore = ({ total, limit }: any) =>
total > limit && (
<div className="mt-4 flex items-center justify-center cursor-pointer w-fit mx-auto">
<div className="text-center">
<div className="color-teal text-lg">
All <span className="font-medium">{total}</span> sessions
</div>
</div>
</div>
</div>
);
);

View file

@ -1,27 +1,27 @@
import React, { useEffect } from 'react';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import { Button, PageTitle, Loader, NoContent } from 'UI';
import { withSiteId } from 'App/routes';
import withModal from 'App/components/Modal/withModal';
import DashboardWidgetGrid from '../DashboardWidgetGrid';
import { confirm } from 'UI';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useModal } from 'App/components/Modal';
import DashboardModal from '../DashboardModal';
import DashboardEditModal from '../DashboardEditModal';
import AlertFormModal from 'App/components/Alerts/AlertFormModal';
import withPageTitle from 'HOCs/withPageTitle';
import withReport from 'App/components/hocs/withReport';
import DashboardOptions from '../DashboardOptions';
import SelectDateRange from 'Shared/SelectDateRange';
import DashboardIcon from '../../../../svg/dashboard-icn.svg';
import { Tooltip } from 'react-tippy';
import React, { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { useStore } from "App/mstore";
import { Button, PageTitle, Loader, NoContent } from "UI";
import { withSiteId } from "App/routes";
import withModal from "App/components/Modal/withModal";
import DashboardWidgetGrid from "../DashboardWidgetGrid";
import { confirm } from "UI";
import { withRouter, RouteComponentProps } from "react-router-dom";
import { useModal } from "App/components/Modal";
import DashboardModal from "../DashboardModal";
import DashboardEditModal from "../DashboardEditModal";
import AlertFormModal from "App/components/Alerts/AlertFormModal";
import withPageTitle from "HOCs/withPageTitle";
import withReport from "App/components/hocs/withReport";
import DashboardOptions from "../DashboardOptions";
import SelectDateRange from "Shared/SelectDateRange";
import DashboardIcon from "../../../../svg/dashboard-icn.svg";
import { Tooltip } from "react-tippy";
interface IProps {
siteId: string;
dashboardId: any
renderReport?: any
dashboardId: any;
renderReport?: any;
}
type Props = IProps & RouteComponentProps;
@ -39,76 +39,108 @@ function DashboardView(props: Props) {
const dashboard: any = dashboardStore.selectedDashboard;
const period = dashboardStore.period;
const queryParams = new URLSearchParams(props.location.search)
const queryParams = new URLSearchParams(props.location.search);
useEffect(() => {
if (!dashboard || !dashboard.dashboardId) return;
dashboardStore.fetch(dashboard.dashboardId)
dashboardStore.fetch(dashboard.dashboardId);
}, [dashboard]);
const trimQuery = () => {
if (!queryParams.has('modal')) return;
queryParams.delete('modal')
if (!queryParams.has("modal")) return;
queryParams.delete("modal");
props.history.replace({
search: queryParams.toString(),
})
}
});
};
const pushQuery = () => {
if (!queryParams.has('modal')) props.history.push('?modal=addMetric')
}
if (!queryParams.has("modal")) props.history.push("?modal=addMetric");
};
useEffect(() => {
if (!dashboardId) dashboardStore.selectDefaultDashboard();
if (queryParams.has('modal')) {
if (queryParams.has("modal")) {
onAddWidgets();
trimQuery();
}
}, []);
const onAddWidgets = () => {
dashboardStore.initDashboard(dashboard)
showModal(<DashboardModal siteId={siteId} onMetricAdd={pushQuery} dashboardId={dashboardId} />, { right: true })
}
dashboardStore.initDashboard(dashboard);
showModal(
<DashboardModal
siteId={siteId}
onMetricAdd={pushQuery}
dashboardId={dashboardId}
/>,
{ right: true }
);
};
const onEdit = (isTitle: boolean) => {
dashboardStore.initDashboard(dashboard)
dashboardStore.initDashboard(dashboard);
setFocusedInput(isTitle);
setShowEditModal(true)
}
setShowEditModal(true);
};
const onDelete = async () => {
if (await confirm({
header: 'Confirm',
confirmButton: 'Yes, delete',
confirmation: `Are you sure you want to permanently delete this Dashboard?`
})) {
if (
await confirm({
header: "Confirm",
confirmButton: "Yes, delete",
confirmation: `Are you sure you want to permanently delete this Dashboard?`,
})
) {
dashboardStore.deleteDashboard(dashboard).then(() => {
dashboardStore.selectDefaultDashboard().then(({ dashboardId }) => {
props.history.push(withSiteId(`/dashboard/${dashboardId}`, siteId));
}, () => {
props.history.push(withSiteId('/dashboard', siteId));
})
dashboardStore.selectDefaultDashboard().then(
({ dashboardId }) => {
props.history.push(
withSiteId(`/dashboard/${dashboardId}`, siteId)
);
},
() => {
props.history.push(withSiteId("/dashboard", siteId));
}
);
});
}
}
};
return (
<Loader loading={loading}>
<NoContent
show={dashboards.length === 0 || !dashboard || !dashboard.dashboardId}
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" />
<span>Gather and analyze <br /> important metrics in one place.</span>
<object
style={{ width: "180px" }}
type="image/svg+xml"
data={DashboardIcon}
className="no-result-icon"
/>
<span>
Gather and analyze <br /> important metrics in one
place.
</span>
</div>
}
size="small"
subtext={
<Button variant="primary" size="small" onClick={onAddWidgets}>+ Create Dashboard</Button>
<Button
variant="primary"
size="small"
onClick={onAddWidgets}
>
+ Create Dashboard
</Button>
}
>
<div style={{ maxWidth: '1300px', margin: 'auto'}}>
<div style={{ maxWidth: "1300px", margin: "auto" }}>
<DashboardEditModal
show={showEditModal}
closeHandler={() => setShowEditModal(false)}
@ -118,23 +150,41 @@ function DashboardView(props: Props) {
<div className="flex items-center" style={{ flex: 3 }}>
<PageTitle
// @ts-ignore
title={<Tooltip delay={100} arrow title="Double click to rename">{dashboard?.name}</Tooltip>}
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>
<Button
variant="primary"
onClick={onAddWidgets}
>
Add Metric
</Button>
}
/>
</div>
<div className="flex items-center" style={{ flex: 1, justifyContent: 'end' }}>
<div className="flex items-center flex-shrink-0 justify-end" style={{ width: '300px'}}>
<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'}}
fluid
plain
style={{ width: "300px" }}
period={period}
onChange={(period: any) => dashboardStore.setPeriod(period)}
onChange={(period: any) =>
dashboardStore.setPeriod(period)
}
right={true}
/>
</div>
@ -150,7 +200,9 @@ function DashboardView(props: Props) {
</div>
</div>
<div>
<h2 className="my-4 font-normal color-gray-dark">{dashboard?.description}</h2>
<h2 className="my-4 font-normal color-gray-dark">
{dashboard?.description}
</h2>
</div>
<DashboardWidgetGrid
siteId={siteId}
@ -160,7 +212,9 @@ function DashboardView(props: Props) {
/>
<AlertFormModal
showModal={showAlertModal}
onClose={() => dashboardStore.updateKey('showAlertModal', false)}
onClose={() =>
dashboardStore.updateKey("showAlertModal", false)
}
/>
</div>
</NoContent>
@ -168,6 +222,6 @@ function DashboardView(props: Props) {
);
}
export default withPageTitle('Dashboards - OpenReplay')(
export default withPageTitle("Dashboards - OpenReplay")(
withReport(withRouter(withModal(observer(DashboardView))))
);

View file

@ -1,21 +1,21 @@
import React, { useEffect, useState } from 'react';
import { NoContent, Loader, Pagination } from 'UI';
import Select from 'Shared/Select';
import cn from 'classnames';
import { useStore } from 'App/mstore';
import SessionItem from 'Shared/SessionItem';
import { observer, useObserver } from 'mobx-react-lite';
import { DateTime } from 'luxon';
import { debounce } from 'App/utils';
import useIsMounted from 'App/hooks/useIsMounted'
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import React, { useEffect, useState } from "react";
import { NoContent, Loader, Pagination } from "UI";
import Select from "Shared/Select";
import cn from "classnames";
import { useStore } from "App/mstore";
import SessionItem from "Shared/SessionItem";
import { observer, useObserver } from "mobx-react-lite";
import { DateTime } from "luxon";
import { debounce } from "App/utils";
import useIsMounted from "App/hooks/useIsMounted";
import AnimatedSVG, { ICONS } from "Shared/AnimatedSVG/AnimatedSVG";
interface Props {
className?: string;
}
function WidgetSessions(props: Props) {
const { className = '' } = props;
const [activeSeries, setActiveSeries] = useState('all');
const { className = "" } = props;
const [activeSeries, setActiveSeries] = useState("all");
const [data, setData] = useState<any>([]);
const isMounted = useIsMounted();
const [loading, setLoading] = useState(false);
@ -23,15 +23,14 @@ function WidgetSessions(props: Props) {
const { dashboardStore, metricStore } = useStore();
const filter = useObserver(() => dashboardStore.drillDownFilter);
const widget: any = useObserver(() => metricStore.instance);
// const drillDownPeriod = useObserver(() => dashboardStore.drillDownPeriod);
const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm');
const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm');
// const [timestamps, setTimestamps] = useState<any>({
// startTimestamp: 0,
// endTimestamp: 0,
// });
const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat(
"LLL dd, yyyy HH:mm"
);
const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat(
"LLL dd, yyyy HH:mm"
);
const [seriesOptions, setSeriesOptions] = useState([
{ label: 'All', value: 'all' },
{ label: "All", value: "all" },
]);
const writeOption = ({ value }: any) => setActiveSeries(value.value);
@ -41,49 +40,68 @@ function WidgetSessions(props: Props) {
label: item.seriesName,
value: item.seriesId,
}));
setSeriesOptions([
{ label: 'All', value: 'all' },
...seriesOptions,
]);
setSeriesOptions([{ label: "All", value: "all" }, ...seriesOptions]);
}, [data]);
const fetchSessions = (metricId: any, filter: any) => {
if (!isMounted()) return;
setLoading(true)
widget.fetchSessions(metricId, filter).then((res: any) => {
setData(res)
}).finally(() => {
setLoading(false)
});
}
const debounceRequest: any = React.useCallback(debounce(fetchSessions, 1000), []);
setLoading(true);
widget
.fetchSessions(metricId, filter)
.then((res: any) => {
setData(res);
})
.finally(() => {
setLoading(false);
});
};
const debounceRequest: any = React.useCallback(
debounce(fetchSessions, 1000),
[]
);
const depsString = JSON.stringify(widget.series);
useEffect(() => {
debounceRequest(widget.metricId, { ...filter, series: widget.toJsonDrilldown(), page: metricStore.sessionsPage, limit: metricStore.sessionsPageSize });
}, [filter.startTimestamp, filter.endTimestamp, filter.filters, depsString, metricStore.sessionsPage]);
// useEffect(() => {
// const timestamps = drillDownPeriod.toTimestamps();
// // console.log('timestamps', timestamps);
// debounceRequest(widget.metricId, { startTime: timestamps.startTimestamp, endTime: timestamps.endTimestamp, series: widget.toJsonDrilldown(), page: metricStore.sessionsPage, limit: metricStore.sessionsPageSize });
// }, [drillDownPeriod]);
debounceRequest(widget.metricId, {
...filter,
series: widget.toJsonDrilldown(),
page: metricStore.sessionsPage,
limit: metricStore.sessionsPageSize,
});
}, [
filter.startTimestamp,
filter.endTimestamp,
filter.filters,
depsString,
metricStore.sessionsPage,
]);
return useObserver(() => (
<div className={cn(className)}>
<div className="flex items-center justify-between">
<div className="flex items-baseline">
<h2 className="text-2xl">Sessions</h2>
<div className="ml-2 color-gray-medium">between <span className="font-medium color-gray-darkest">{startTime}</span> and <span className="font-medium color-gray-darkest">{endTime}</span> </div>
<div className="ml-2 color-gray-medium">
between{" "}
<span className="font-medium color-gray-darkest">
{startTime}
</span>{" "}
and{" "}
<span className="font-medium color-gray-darkest">
{endTime}
</span>{" "}
</div>
</div>
{ widget.metricType !== 'table' && (
{widget.metricType !== "table" && (
<div className="flex items-center ml-6">
<span className="mr-2 color-gray-medium">Filter by Series</span>
<span className="mr-2 color-gray-medium">
Filter by Series
</span>
<Select
options={ seriesOptions }
defaultValue={ 'all' }
onChange={ writeOption }
options={seriesOptions}
defaultValue={"all"}
onChange={writeOption}
plain
/>
</div>
@ -95,15 +113,20 @@ function WidgetSessions(props: Props) {
<NoContent
title={
<div className="flex flex-col items-center justify-center">
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
<div className="mt-6 text-2xl">No recordings found</div>
<AnimatedSVG
name={ICONS.NO_RESULTS}
size="170"
/>
<div className="mt-6 text-2xl">
No recordings found
</div>
</div>
}
show={filteredSessions.sessions.length === 0}
>
{filteredSessions.sessions.map((session: any) => (
<React.Fragment key={ session.sessionId }>
<SessionItem session={ session } />
<React.Fragment key={session.sessionId}>
<SessionItem session={session} />
<div className="border-b" />
</React.Fragment>
))}
@ -111,8 +134,13 @@ function WidgetSessions(props: Props) {
<div className="w-full flex items-center justify-center py-6">
<Pagination
page={metricStore.sessionsPage}
totalPages={Math.ceil(filteredSessions.total / metricStore.sessionsPageSize)}
onPageChange={(page: any) => metricStore.updateKey('sessionsPage', page)}
totalPages={Math.ceil(
filteredSessions.total /
metricStore.sessionsPageSize
)}
onPageChange={(page: any) =>
metricStore.updateKey("sessionsPage", page)
}
limit={metricStore.sessionsPageSize}
debounceRequest={500}
/>
@ -127,18 +155,22 @@ function WidgetSessions(props: Props) {
const getListSessionsBySeries = (data: any, seriesId: any) => {
const arr: any = { sessions: [], total: 0 };
data.forEach((element: any) => {
if (seriesId === 'all') {
if (seriesId === "all") {
const sessionIds = arr.sessions.map((i: any) => i.sessionId);
arr.sessions.push(...element.sessions.filter((i: any) => !sessionIds.includes(i.sessionId)));
arr.total = element.total
arr.sessions.push(
...element.sessions.filter(
(i: any) => !sessionIds.includes(i.sessionId)
)
);
arr.total = element.total;
} else {
if (element.seriesId === seriesId) {
arr.sessions.push(...element.sessions)
arr.total = element.total
arr.sessions.push(...element.sessions);
arr.total = element.total;
}
}
});
return arr;
}
};
export default observer(WidgetSessions);

View file

@ -1,92 +1,82 @@
import origMoment from 'moment';
import { extendMoment } from 'moment-range';
import origMoment from "moment";
import { extendMoment } from "moment-range";
export const moment = extendMoment(origMoment);
import { DateTime } from "luxon";
export const CUSTOM_RANGE = 'CUSTOM_RANGE';
export const CUSTOM_RANGE = "CUSTOM_RANGE";
const DATE_RANGE_LABELS = {
// LAST_30_MINUTES: '30 Minutes',
// TODAY: 'Today',
LAST_24_HOURS: 'Last 24 Hours',
// YESTERDAY: 'Yesterday',
LAST_7_DAYS: 'Past 7 Days',
LAST_30_DAYS: 'Past 30 Days',
//THIS_MONTH: 'This Month',
//LAST_MONTH: 'Previous Month',
//THIS_YEAR: 'This Year',
[ CUSTOM_RANGE ]: 'Custom Range',
// LAST_30_MINUTES: '30 Minutes',
// TODAY: 'Today',
LAST_24_HOURS: "Last 24 Hours",
// YESTERDAY: 'Yesterday',
LAST_7_DAYS: "Past 7 Days",
LAST_30_DAYS: "Past 30 Days",
//THIS_MONTH: 'This Month',
//LAST_MONTH: 'Previous Month',
//THIS_YEAR: 'This Year',
[CUSTOM_RANGE]: "Custom Range",
};
const DATE_RANGE_VALUES = {};
Object.keys(DATE_RANGE_LABELS).forEach((key) => { DATE_RANGE_VALUES[ key ] = key; });
Object.keys(DATE_RANGE_LABELS).forEach((key) => {
DATE_RANGE_VALUES[key] = key;
});
export { DATE_RANGE_VALUES };
export const dateRangeValues = Object.keys(DATE_RANGE_VALUES);
export const DATE_RANGE_OPTIONS = Object.keys(DATE_RANGE_LABELS).map((key) => {
return {
label: DATE_RANGE_LABELS[ key ],
value: key,
};
return {
label: DATE_RANGE_LABELS[key],
value: key,
};
});
export function getDateRangeFromTs(start, end) {
return moment.range(
moment(start),
moment(end),
);
return moment.range(moment(start), moment(end));
}
export function getDateRangeLabel(value) {
return DATE_RANGE_LABELS[ value ];
return DATE_RANGE_LABELS[value];
}
export function getDateRangeFromValue(value) {
switch (value) {
case DATE_RANGE_VALUES.LAST_30_MINUTES:
return moment.range(
moment().startOf('hour').subtract(30, 'minutes'),
moment().startOf('hour'),
);
case DATE_RANGE_VALUES.TODAY:
return moment.range(
moment().startOf('day'),
moment().endOf('day'),
);
case DATE_RANGE_VALUES.YESTERDAY:
return moment.range(
moment().subtract(1, 'days').startOf('day'),
moment().subtract(1, 'days').endOf('day'),
);
case DATE_RANGE_VALUES.LAST_24_HOURS:
return moment.range(
moment().subtract(24, 'hours'),
moment(),
);
case DATE_RANGE_VALUES.LAST_7_DAYS:
return moment.range(
moment().subtract(7, 'days').startOf('day'),
moment().endOf('day'),
);
case DATE_RANGE_VALUES.LAST_30_DAYS:
return moment.range(
moment().subtract(30, 'days').startOf('day'),
moment().endOf('day'),
);
case DATE_RANGE_VALUES.THIS_MONTH:
return moment().range('month');
case DATE_RANGE_VALUES.LAST_MONTH:
return moment().subtract(1, 'months').range('month');
case DATE_RANGE_VALUES.THIS_YEAR:
return moment().range('year');
case DATE_RANGE_VALUES.CUSTOM_RANGE:
return moment.range(
moment(),
moment(),
);
}
return null;
switch (value) {
case DATE_RANGE_VALUES.LAST_30_MINUTES:
return moment.range(
moment().startOf("hour").subtract(30, "minutes"),
moment().startOf("hour")
);
case DATE_RANGE_VALUES.TODAY:
return moment.range(moment().startOf("day"), moment().endOf("day"));
case DATE_RANGE_VALUES.YESTERDAY:
return moment.range(
moment().subtract(1, "days").startOf("day"),
moment().subtract(1, "days").endOf("day")
);
case DATE_RANGE_VALUES.LAST_24_HOURS:
return moment.range(moment().subtract(24, "hours"), moment());
case DATE_RANGE_VALUES.LAST_7_DAYS:
return moment.range(
moment().subtract(7, "days").startOf("day"),
moment().endOf("day")
);
case DATE_RANGE_VALUES.LAST_30_DAYS:
return moment.range(
moment().subtract(30, "days").startOf("day"),
moment().endOf("day")
);
case DATE_RANGE_VALUES.THIS_MONTH:
return moment().range("month");
case DATE_RANGE_VALUES.LAST_MONTH:
return moment().subtract(1, "months").range("month");
case DATE_RANGE_VALUES.THIS_YEAR:
return moment().range("year");
case DATE_RANGE_VALUES.CUSTOM_RANGE:
return moment.range(moment(), moment());
}
return null;
}
/**
@ -96,13 +86,25 @@ export function getDateRangeFromValue(value) {
* @return {String} Formated date string.
*/
export const checkForRecent = (date, format) => {
const d = new Date();
// Today
if (date.hasSame(d, 'day')) return 'Today';
const d = new Date();
// Today
if (date.hasSame(d, "day")) return "Today";
// Yesterday
if (date.hasSame(d.setDate(d.getDate() - 1), 'day')) return 'Yesterday';
// Yesterday
if (date.hasSame(d.setDate(d.getDate() - 1), "day")) return "Yesterday";
// Formatted
return date.toFormat(format);
// Formatted
return date.toFormat(format);
};
export const overPastString = (period) => {
if (period.rangeName === DATE_RANGE_VALUES.CUSTOM_RANGE) {
const format = "LLL dd, yyyy HH:mm";
const { startTimestamp, endTimestamp } = period.toTimestamps();
const start = DateTime.fromMillis(startTimestamp).toFormat(format);
const end = DateTime.fromMillis(endTimestamp).toFormat(format);
return ` between ${start} - ${end}`;
}
return ' over the ' + DATE_RANGE_LABELS[period.rangeName];
};

View file

@ -1,104 +1,114 @@
import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
import Dashboard, { IDashboard } from "./types/dashboard"
import {
makeAutoObservable,
runInAction,
observable,
action,
} from "mobx";
import Dashboard, { IDashboard } from "./types/dashboard";
import Widget, { IWidget } from "./types/widget";
import { dashboardService, metricService } from "App/services";
import { toast } from 'react-toastify';
import Period, { LAST_24_HOURS, LAST_7_DAYS, LAST_30_DAYS } from 'Types/app/period';
import { getChartFormatter } from 'Types/dashboard/helper';
import { toast } from "react-toastify";
import Period, {
LAST_24_HOURS,
LAST_7_DAYS,
} from "Types/app/period";
import { getChartFormatter } from "Types/dashboard/helper";
import Filter, { IFilter } from "./types/filter";
import Funnel from "./types/funnel";
import Session from "./types/session";
import Error from "./types/error";
import { FilterKey } from 'Types/filter/filterType';
import { FilterKey } from "Types/filter/filterType";
export interface IDashboardSotre {
dashboards: IDashboard[]
selectedDashboard: IDashboard | null
dashboardInstance: IDashboard
selectedWidgets: IWidget[]
startTimestamp: number
endTimestamp: number
period: Period
drillDownFilter: IFilter
drillDownPeriod: Period
dashboards: IDashboard[];
selectedDashboard: IDashboard | null;
dashboardInstance: IDashboard;
selectedWidgets: IWidget[];
startTimestamp: number;
endTimestamp: number;
period: Period;
drillDownFilter: IFilter;
drillDownPeriod: Period;
siteId: any
currentWidget: Widget
widgetCategories: any[]
widgets: Widget[]
metricsPage: number
metricsPageSize: number
metricsSearch: string
siteId: any;
currentWidget: Widget;
widgetCategories: any[];
widgets: Widget[];
metricsPage: number;
metricsPageSize: number;
metricsSearch: string;
isLoading: boolean
isSaving: boolean
isDeleting: boolean
fetchingDashboard: boolean
sessionsLoading: boolean
isLoading: boolean;
isSaving: boolean;
isDeleting: boolean;
fetchingDashboard: boolean;
sessionsLoading: boolean;
showAlertModal: boolean
showAlertModal: boolean;
selectWidgetsByCategory: (category: string) => void
toggleAllSelectedWidgets: (isSelected: boolean) => void
removeSelectedWidgetByCategory(category: string): void
toggleWidgetSelection(widget: IWidget): void
selectWidgetsByCategory: (category: string) => void;
toggleAllSelectedWidgets: (isSelected: boolean) => void;
removeSelectedWidgetByCategory(category: string): void;
toggleWidgetSelection(widget: IWidget): void;
initDashboard(dashboard?: IDashboard): void
updateKey(key: string, value: any): void
resetCurrentWidget(): void
editWidget(widget: any): void
fetchList(): Promise<any>
fetch(dashboardId: string): Promise<any>
save(dashboard: IDashboard): Promise<any>
saveDashboardWidget(dashboard: Dashboard, widget: Widget)
deleteDashboard(dashboard: IDashboard): Promise<any>
toJson(): void
fromJson(json: any): void
// initDashboard(dashboard: IDashboard): void
addDashboard(dashboard: IDashboard): void
removeDashboard(dashboard: IDashboard): void
getDashboard(dashboardId: string): IDashboard|null
getDashboardCount(): void
updateDashboard(dashboard: IDashboard): void
selectDashboardById(dashboardId: string): void
setSiteId(siteId: any): void
selectDefaultDashboard(): Promise<IDashboard>
initDashboard(dashboard?: IDashboard): void;
updateKey(key: string, value: any): void;
resetCurrentWidget(): void;
editWidget(widget: any): void;
fetchList(): Promise<any>;
fetch(dashboardId: string): Promise<any>;
save(dashboard: IDashboard): Promise<any>;
deleteDashboard(dashboard: IDashboard): Promise<any>;
toJson(): void;
fromJson(json: any): void;
addDashboard(dashboard: IDashboard): void;
removeDashboard(dashboard: IDashboard): void;
getDashboard(dashboardId: string): IDashboard | null;
getDashboardCount(): void;
updateDashboard(dashboard: IDashboard): void;
selectDashboardById(dashboardId: string): void;
setSiteId(siteId: any): void;
selectDefaultDashboard(): Promise<IDashboard>;
saveMetric(metric: IWidget, dashboardId?: string): Promise<any>
fetchTemplates(hardRefresh: boolean): Promise<any>
deleteDashboardWidget(dashboardId: string, widgetId: string): Promise<any>
addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise<any>
saveMetric(metric: IWidget, dashboardId?: string): Promise<any>;
fetchTemplates(hardRefresh: boolean): Promise<any>;
deleteDashboardWidget(dashboardId: string, widgetId: string): Promise<any>;
addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise<any>;
updatePinned(dashboardId: string): Promise<any>
fetchMetricChartData(metric: IWidget, data: any, isWidget: boolean): Promise<any>
setPeriod(period: any): void
updatePinned(dashboardId: string): Promise<any>;
fetchMetricChartData(
metric: IWidget,
data: any,
isWidget: boolean
): Promise<any>;
setPeriod(period: any): void;
}
export default class DashboardStore implements IDashboardSotre {
siteId: any = null
siteId: any = null;
// Dashbaord / Widgets
dashboards: Dashboard[] = []
selectedDashboard: Dashboard | null = null
dashboardInstance: IDashboard = new Dashboard()
dashboards: Dashboard[] = [];
selectedDashboard: Dashboard | null = null;
dashboardInstance: IDashboard = new Dashboard();
selectedWidgets: IWidget[] = [];
currentWidget: Widget = new Widget()
widgetCategories: any[] = []
widgets: Widget[] = []
period: Period = Period({ rangeName: LAST_24_HOURS })
drillDownFilter: Filter = new Filter()
currentWidget: Widget = new Widget();
widgetCategories: any[] = [];
widgets: Widget[] = [];
period: Period = Period({ rangeName: LAST_24_HOURS });
drillDownFilter: Filter = new Filter();
drillDownPeriod: Period = Period({ rangeName: LAST_7_DAYS });
startTimestamp: number = 0
endTimestamp: number = 0
startTimestamp: number = 0;
endTimestamp: number = 0;
// Metrics
metricsPage: number = 1
metricsPageSize: number = 10
metricsSearch: string = ''
metricsPage: number = 1;
metricsPageSize: number = 10;
metricsSearch: string = "";
// Loading states
isLoading: boolean = true
isSaving: boolean = false
isDeleting: boolean = false
fetchingDashboard: boolean = false
isLoading: boolean = true;
isSaving: boolean = false;
isDeleting: boolean = false;
fetchingDashboard: boolean = false;
sessionsLoading: boolean = false;
showAlertModal: boolean = false;
@ -135,378 +145,469 @@ export default class DashboardStore implements IDashboardSotre {
setPeriod: action,
setDrillDownPeriod: action,
fetchMetricChartData: action
})
fetchMetricChartData: action,
});
this.drillDownPeriod = Period({ rangeName: LAST_7_DAYS });
const timeStamps = this.drillDownPeriod.toTimestamps();
this.drillDownFilter.updateKey('startTimestamp', timeStamps.startTimestamp)
this.drillDownFilter.updateKey('endTimestamp', timeStamps.endTimestamp)
this.drillDownFilter.updateKey(
"startTimestamp",
timeStamps.startTimestamp
);
this.drillDownFilter.updateKey("endTimestamp", timeStamps.endTimestamp);
}
toggleAllSelectedWidgets(isSelected: boolean) {
if (isSelected) {
const allWidgets = this.widgetCategories.reduce((acc, cat) => {
return acc.concat(cat.widgets)
}, [])
return acc.concat(cat.widgets);
}, []);
this.selectedWidgets = allWidgets
this.selectedWidgets = allWidgets;
} else {
this.selectedWidgets = []
this.selectedWidgets = [];
}
}
selectWidgetsByCategory(category: string) {
const selectedWidgetIds = this.selectedWidgets.map((widget: any) => widget.metricId);
const widgets = this.widgetCategories.find(cat => cat.name === category)?.widgets.filter(widget => !selectedWidgetIds.includes(widget.metricId))
this.selectedWidgets = this.selectedWidgets.concat(widgets) || []
const selectedWidgetIds = this.selectedWidgets.map(
(widget: any) => widget.metricId
);
const widgets = this.widgetCategories
.find((cat) => cat.name === category)
?.widgets.filter(
(widget: any) => !selectedWidgetIds.includes(widget.metricId)
);
this.selectedWidgets = this.selectedWidgets.concat(widgets) || [];
}
removeSelectedWidgetByCategory = (category: any) => {
const categoryWidgetIds = category.widgets.map(w => w.metricId)
this.selectedWidgets = this.selectedWidgets.filter((widget: any) => !categoryWidgetIds.includes(widget.metricId));
}
const categoryWidgetIds = category.widgets.map((w) => w.metricId);
this.selectedWidgets = this.selectedWidgets.filter(
(widget: any) => !categoryWidgetIds.includes(widget.metricId)
);
};
toggleWidgetSelection = (widget: any) => {
const selectedWidgetIds = this.selectedWidgets.map((widget: any) => widget.metricId);
const selectedWidgetIds = this.selectedWidgets.map(
(widget: any) => widget.metricId
);
if (selectedWidgetIds.includes(widget.metricId)) {
this.selectedWidgets = this.selectedWidgets.filter((w: any) => w.metricId !== widget.metricId);
this.selectedWidgets = this.selectedWidgets.filter(
(w: any) => w.metricId !== widget.metricId
);
} else {
this.selectedWidgets.push(widget);
}
};
findByIds(ids: string[]) {
return this.dashboards.filter(d => ids.includes(d.dashboardId))
return this.dashboards.filter((d) => ids.includes(d.dashboardId));
}
initDashboard(dashboard: Dashboard) {
this.dashboardInstance = dashboard ? new Dashboard().fromJson(dashboard) : new Dashboard()
this.selectedWidgets = []
this.dashboardInstance = dashboard
? new Dashboard().fromJson(dashboard)
: new Dashboard();
this.selectedWidgets = [];
}
updateKey(key: any, value: any) {
this[key] = value
this[key] = value;
}
resetCurrentWidget() {
this.currentWidget = new Widget()
this.currentWidget = new Widget();
}
editWidget(widget: any) {
this.currentWidget.update(widget)
this.currentWidget.update(widget);
}
fetchList(): Promise<any> {
this.isLoading = true
this.isLoading = true;
return dashboardService.getDashboards()
return dashboardService
.getDashboards()
.then((list: any) => {
runInAction(() => {
this.dashboards = list.map(d => new Dashboard().fromJson(d))
})
}).finally(() => {
runInAction(() => {
this.isLoading = false
})
this.dashboards = list.map((d) =>
new Dashboard().fromJson(d)
);
});
})
.finally(() => {
runInAction(() => {
this.isLoading = false;
});
});
}
fetch(dashboardId: string): Promise<any> {
this.fetchingDashboard = true
return dashboardService.getDashboard(dashboardId).then(response => {
// const widgets = new Dashboard().fromJson(response).widgets
this.selectedDashboard?.update({ 'widgets' : new Dashboard().fromJson(response).widgets})
}).finally(() => {
this.fetchingDashboard = false
})
this.fetchingDashboard = true;
return dashboardService
.getDashboard(dashboardId)
.then((response) => {
// const widgets = new Dashboard().fromJson(response).widgets
this.selectedDashboard?.update({
widgets: new Dashboard().fromJson(response).widgets,
});
})
.finally(() => {
this.fetchingDashboard = false;
});
}
save(dashboard: IDashboard): Promise<any> {
this.isSaving = true
const isCreating = !dashboard.dashboardId
this.isSaving = true;
const isCreating = !dashboard.dashboardId;
dashboard.metrics = this.selectedWidgets.map(w => w.metricId)
dashboard.metrics = this.selectedWidgets.map((w) => w.metricId);
return new Promise((resolve, reject) => {
dashboardService.saveDashboard(dashboard).then(_dashboard => {
runInAction(() => {
if (isCreating) {
toast.success('Dashboard created successfully')
this.addDashboard(new Dashboard().fromJson(_dashboard))
} else {
toast.success('Dashboard updated successfully')
this.updateDashboard(new Dashboard().fromJson(_dashboard))
}
resolve(_dashboard)
dashboardService
.saveDashboard(dashboard)
.then((_dashboard) => {
runInAction(() => {
if (isCreating) {
toast.success("Dashboard created successfully");
this.addDashboard(
new Dashboard().fromJson(_dashboard)
);
} else {
toast.success("Dashboard updated successfully");
this.updateDashboard(
new Dashboard().fromJson(_dashboard)
);
}
resolve(_dashboard);
});
})
}).catch(error => {
toast.error('Error saving dashboard')
reject()
}).finally(() => {
runInAction(() => {
this.isSaving = false
.catch((error) => {
toast.error("Error saving dashboard");
reject();
})
})
})
.finally(() => {
runInAction(() => {
this.isSaving = false;
});
});
});
}
saveMetric(metric: IWidget, dashboardId: string): Promise<any> {
const isCreating = !metric.widgetId
return dashboardService.saveMetric(metric, dashboardId).then(metric => {
runInAction(() => {
if (isCreating) {
this.selectedDashboard?.widgets.push(metric)
} else {
this.selectedDashboard?.widgets.map(w => {
if (w.widgetId === metric.widgetId) {
w.update(metric)
}
})
}
})
})
}
saveDashboardWidget(dashboard: Dashboard, widget: Widget) {
widget.validate()
if (widget.isValid) {
this.isLoading = true
}
const isCreating = !metric.widgetId;
return dashboardService
.saveMetric(metric, dashboardId)
.then((metric) => {
runInAction(() => {
if (isCreating) {
this.selectedDashboard?.widgets.push(metric);
} else {
this.selectedDashboard?.widgets.map((w) => {
if (w.widgetId === metric.widgetId) {
w.update(metric);
}
});
}
});
});
}
deleteDashboard(dashboard: Dashboard): Promise<any> {
this.isDeleting = true
return dashboardService.deleteDashboard(dashboard.dashboardId).then(() => {
toast.success('Dashboard deleted successfully')
runInAction(() => {
this.removeDashboard(dashboard)
this.isDeleting = true;
return dashboardService
.deleteDashboard(dashboard.dashboardId)
.then(() => {
toast.success("Dashboard deleted successfully");
runInAction(() => {
this.removeDashboard(dashboard);
});
})
})
.catch(() => {
toast.error('Dashboard could not be deleted')
})
.finally(() => {
runInAction(() => {
this.isDeleting = false
.catch(() => {
toast.error("Dashboard could not be deleted");
})
})
.finally(() => {
runInAction(() => {
this.isDeleting = false;
});
});
}
toJson() {
return {
dashboards: this.dashboards.map(d => d.toJson())
}
dashboards: this.dashboards.map((d) => d.toJson()),
};
}
fromJson(json: any) {
runInAction(() => {
this.dashboards = json.dashboards.map(d => new Dashboard().fromJson(d))
})
return this
this.dashboards = json.dashboards.map((d) =>
new Dashboard().fromJson(d)
);
});
return this;
}
addDashboard(dashboard: Dashboard) {
this.dashboards.push(new Dashboard().fromJson(dashboard))
this.dashboards.push(new Dashboard().fromJson(dashboard));
}
removeDashboard(dashboard: Dashboard) {
this.dashboards = this.dashboards.filter(d => d.dashboardId !== dashboard.dashboardId)
this.dashboards = this.dashboards.filter(
(d) => d.dashboardId !== dashboard.dashboardId
);
}
getDashboard(dashboardId: string): IDashboard|null {
return this.dashboards.find(d => d.dashboardId === dashboardId) || null
getDashboard(dashboardId: string): IDashboard | null {
return (
this.dashboards.find((d) => d.dashboardId === dashboardId) || null
);
}
getDashboardByIndex(index: number) {
return this.dashboards[index]
return this.dashboards[index];
}
getDashboardCount() {
return this.dashboards.length
return this.dashboards.length;
}
updateDashboard(dashboard: Dashboard) {
const index = this.dashboards.findIndex(d => d.dashboardId === dashboard.dashboardId)
const index = this.dashboards.findIndex(
(d) => d.dashboardId === dashboard.dashboardId
);
if (index >= 0) {
this.dashboards[index] = dashboard
this.dashboards[index] = dashboard;
if (this.selectedDashboard?.dashboardId === dashboard.dashboardId) {
this.selectDashboardById(dashboard.dashboardId)
this.selectDashboardById(dashboard.dashboardId);
}
}
}
selectDashboardById = (dashboardId: any) => {
this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard();
// if (this.selectedDashboard.dashboardId) {
// this.fetch(this.selectedDashboard.dashboardId)
// }
}
this.selectedDashboard =
this.dashboards.find((d) => d.dashboardId == dashboardId) ||
new Dashboard();
};
setSiteId = (siteId: any) => {
this.siteId = siteId
}
this.siteId = siteId;
};
selectDefaultDashboard = (): Promise<Dashboard> => {
return new Promise((resolve, reject) => {
if (this.dashboards.length > 0) {
const pinnedDashboard = this.dashboards.find(d => d.isPinned)
const pinnedDashboard = this.dashboards.find((d) => d.isPinned);
if (pinnedDashboard) {
this.selectedDashboard = pinnedDashboard
this.selectedDashboard = pinnedDashboard;
} else {
this.selectedDashboard = this.dashboards[0]
this.selectedDashboard = this.dashboards[0];
}
resolve(this.selectedDashboard)
resolve(this.selectedDashboard);
}
reject(new Error("No dashboards found"))
})
}
reject(new Error("No dashboards found"));
});
};
fetchTemplates(hardRefresh): Promise<any> {
return new Promise((resolve, reject) => {
if (this.widgetCategories.length > 0 && !hardRefresh) {
resolve(this.widgetCategories)
resolve(this.widgetCategories);
} else {
metricService.getTemplates().then(response => {
const categories: any[] = []
response.forEach(category => {
const widgets: any[] = []
// TODO speed_location is not supported yet
category.widgets.filter(w => w.predefinedKey !== 'speed_locations').forEach(widget => {
const w = new Widget().fromJson(widget)
widgets.push(w)
})
const c: any = {}
c.widgets = widgets
c.name = category.category
c.description = category.description
categories.push(c)
metricService
.getTemplates()
.then((response) => {
const categories: any[] = [];
response.forEach((category: any) => {
const widgets: any[] = [];
// TODO speed_location is not supported yet
category.widgets
.filter(
(w: any) => w.predefinedKey !== "speed_locations"
)
.forEach((widget: any) => {
const w = new Widget().fromJson(widget);
widgets.push(w);
});
const c: any = {};
c.widgets = widgets;
c.name = category.category;
c.description = category.description;
categories.push(c);
});
this.widgetCategories = categories;
resolve(this.widgetCategories);
})
this.widgetCategories = categories
resolve(this.widgetCategories)
}).catch(error => {
reject(error)
})
.catch((error) => {
reject(error);
});
}
})
});
}
deleteDashboardWidget(dashboardId: string, widgetId: string) {
this.isDeleting = true
return dashboardService.deleteWidget(dashboardId, widgetId).then(() => {
toast.success('Dashboard updated successfully')
runInAction(() => {
this.selectedDashboard?.removeWidget(widgetId)
this.isDeleting = true;
return dashboardService
.deleteWidget(dashboardId, widgetId)
.then(() => {
toast.success("Dashboard updated successfully");
runInAction(() => {
this.selectedDashboard?.removeWidget(widgetId);
});
})
}).finally(() => {
this.isDeleting = false
})
.finally(() => {
this.isDeleting = false;
});
}
addWidgetToDashboard(dashboard: IDashboard, metricIds: any) : Promise<any> {
this.isSaving = true
return dashboardService.addWidget(dashboard, metricIds)
.then(response => {
toast.success('Widget added successfully')
}).catch(() => {
toast.error('Widget could not be added')
}).finally(() => {
this.isSaving = false
addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise<any> {
this.isSaving = true;
return dashboardService
.addWidget(dashboard, metricIds)
.then((response) => {
toast.success("Widget added successfully");
})
.catch(() => {
toast.error("Widget could not be added");
})
.finally(() => {
this.isSaving = false;
});
}
updatePinned(dashboardId: string): Promise<any> {
// this.isSaving = true
return dashboardService.updatePinned(dashboardId).then(() => {
toast.success('Dashboard pinned successfully')
this.dashboards.forEach(d => {
if (d.dashboardId === dashboardId) {
d.isPinned = true
} else {
d.isPinned = false
}
return dashboardService
.updatePinned(dashboardId)
.then(() => {
toast.success("Dashboard pinned successfully");
this.dashboards.forEach((d) => {
if (d.dashboardId === dashboardId) {
d.isPinned = true;
} else {
d.isPinned = false;
}
});
})
}).catch(() => {
toast.error('Dashboard could not be pinned')
}).finally(() => {
// this.isSaving = false
})
.catch(() => {
toast.error("Dashboard could not be pinned");
})
.finally(() => {
// this.isSaving = false
});
}
setPeriod(period: any) {
this.period = new Period({ start: period.startDate, end: period.endDate, rangeName: period.rangeName })
this.period = Period({
start: period.start,
end: period.end,
rangeName: period.rangeName,
});
}
setDrillDownPeriod(period: any) {
this.drillDownPeriod = new Period({ start: period.startDate, end: period.endDate, rangeName: period.rangeName })
this.drillDownPeriod = Period({
start: period.start,
end: period.end,
rangeName: period.rangeName,
});
}
fetchMetricChartData(metric: IWidget, data: any, isWidget: boolean = false): Promise<any> {
const period = this.period.toTimestamps()
const params = { ...period, ...data, key: metric.predefinedKey }
fetchMetricChartData(
metric: IWidget,
data: any,
isWidget: boolean = false
): Promise<any> {
const period = this.period.toTimestamps();
const params = { ...period, ...data, key: metric.predefinedKey };
if (metric.page && metric.limit) {
params['page'] = metric.page
params['limit'] = metric.limit
params["page"] = metric.page;
params["limit"] = metric.limit;
}
return new Promise((resolve, reject) => {
return metricService.getMetricChartData(metric, params, isWidget)
return metricService
.getMetricChartData(metric, params, isWidget)
.then((data: any) => {
if (metric.metricType === 'predefined' && metric.viewType === 'overview') {
const _data = { ...data, chart: getChartFormatter(this.period)(data.chart) }
metric.setData(_data)
if (
metric.metricType === "predefined" &&
metric.viewType === "overview"
) {
const _data = {
...data,
chart: getChartFormatter(this.period)(data.chart),
};
metric.setData(_data);
resolve(_data);
} else if (metric.metricType === 'funnel') {
const _data = { ...data }
_data.funnel = new Funnel().fromJSON(data)
metric.setData(_data)
} else if (metric.metricType === "funnel") {
const _data = { ...data };
_data.funnel = new Funnel().fromJSON(data);
metric.setData(_data);
resolve(_data);
} else {
const _data = {
...data,
}
};
// TODO refactor to widget class
if (metric.metricOf === FilterKey.SESSIONS) {
_data['sessions'] = data.sessions.map((s: any) => new Session().fromJson(s))
_data["sessions"] = data.sessions.map((s: any) =>
new Session().fromJson(s)
);
} else if (metric.metricOf === FilterKey.ERRORS) {
_data['errors'] = data.errors.map((s: any) => new Error().fromJSON(s))
_data["errors"] = data.errors.map((s: any) =>
new Error().fromJSON(s)
);
} else {
if (data.hasOwnProperty('chart')) {
_data['chart'] = getChartFormatter(this.period)(data.chart)
_data['namesMap'] = data.chart
.map(i => Object.keys(i))
if (data.hasOwnProperty("chart")) {
_data["chart"] = getChartFormatter(this.period)(
data.chart
);
_data["namesMap"] = data.chart
.map((i: any) => Object.keys(i))
.flat()
.filter(i => i !== 'time' && i !== 'timestamp')
.filter(
(i: any) => i !== "time" && i !== "timestamp"
)
.reduce((unique: any, item: any) => {
if (!unique.includes(item)) {
unique.push(item);
}
return unique;
}, [])
}, []);
} else {
_data['chart'] = getChartFormatter(this.period)(Array.isArray(data) ? data : []);
_data['namesMap'] = Array.isArray(data) ? 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;
}, []) : []
_data["chart"] = getChartFormatter(this.period)(
Array.isArray(data) ? data : []
);
_data["namesMap"] = Array.isArray(data)
? 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;
}, [])
: [];
}
}
metric.setData(_data)
metric.setData(_data);
resolve(_data);
}
}).catch((err: any) => {
reject(err)
})
})
.catch((err: any) => {
reject(err);
});
});
}
}

View file

@ -120,7 +120,7 @@ export default Record(
endTimestamp: this.end,
};
},
rangeFormatted(format = "MMM Do YY, hh:mm A") {
rangeFormatted(format = "MMM Do YY, HH:mm") {
return (
this.range.start.format(format) +
" - " +