fix(ui) - duration and not data message for sessions and errors
This commit is contained in:
parent
a3e99a6217
commit
db6609d908
8 changed files with 775 additions and 545 deletions
4
frontend/.prettierrc
Normal file
4
frontend/.prettierrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": false
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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))))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) +
|
||||
" - " +
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue