ui: delete deprecated components, fix widgetchart props, fix dashboard page reload check
This commit is contained in:
parent
b8f97ad15b
commit
8e0b30ece4
13 changed files with 61 additions and 651 deletions
|
|
@ -1,10 +1,7 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import { Icon, Loader } from 'UI';
|
||||
import { Icon } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import { useStore } from 'App/mstore';
|
||||
import WidgetWrapper from '../WidgetWrapper';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface IWiProps {
|
||||
category: Record<string, any>;
|
||||
|
|
@ -57,139 +54,3 @@ export function WidgetCategoryItem({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
handleCreateNew?: () => void;
|
||||
isDashboardExists?: boolean;
|
||||
}
|
||||
|
||||
function DashboardMetricSelection(props: IProps) {
|
||||
const { t } = useTranslation();
|
||||
const { dashboardStore } = useStore();
|
||||
const widgetCategories: any[] = useObserver(
|
||||
() => dashboardStore.widgetCategories,
|
||||
);
|
||||
const loadingTemplates = useObserver(() => dashboardStore.loadingTemplates);
|
||||
const [activeCategory, setActiveCategory] = React.useState<any>();
|
||||
const [selectAllCheck, setSelectAllCheck] = React.useState(false);
|
||||
const selectedWidgetIds = useObserver(() =>
|
||||
dashboardStore.selectedWidgets.map((widget: any) => widget.metricId),
|
||||
);
|
||||
const scrollContainer = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
dashboardStore?.fetchTemplates(true).then((categories) => {
|
||||
setActiveCategory(categories[0]);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollContainer.current) {
|
||||
scrollContainer.current.scrollTop = 0;
|
||||
}
|
||||
}, [activeCategory, scrollContainer.current]);
|
||||
|
||||
const handleWidgetCategoryClick = (category: any) => {
|
||||
setActiveCategory(category);
|
||||
setSelectAllCheck(false);
|
||||
};
|
||||
|
||||
const toggleAllWidgets = ({ target: { checked } }) => {
|
||||
setSelectAllCheck(checked);
|
||||
if (checked) {
|
||||
dashboardStore.selectWidgetsByCategory(activeCategory.name);
|
||||
} else {
|
||||
dashboardStore.removeSelectedWidgetByCategory(activeCategory);
|
||||
}
|
||||
};
|
||||
|
||||
return useObserver(() => (
|
||||
<Loader loading={loadingTemplates}>
|
||||
<div className="grid grid-cols-12 gap-4 my-3 items-end">
|
||||
<div className="col-span-3">
|
||||
<div className="uppercase color-gray-medium text-lg">{t('Type')}</div>
|
||||
</div>
|
||||
|
||||
<div className="col-span-9 flex items-center">
|
||||
{activeCategory && (
|
||||
<>
|
||||
<div className="flex items-baseline">
|
||||
<h2 className="text-2xl capitalize">{activeCategory.name}</h2>
|
||||
<span className="text-2xl color-gray-medium ml-2">
|
||||
{activeCategory.widgets.length}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="ml-auto">
|
||||
<label className="flex items-center ml-3 cursor-pointer select-none">
|
||||
<input
|
||||
type="checkbox"
|
||||
onChange={toggleAllWidgets}
|
||||
checked={selectAllCheck}
|
||||
/>
|
||||
<div className="ml-2">{t('Select All')}</div>
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-3">
|
||||
<div
|
||||
className="grid grid-cols-1 gap-4 py-1 pr-2"
|
||||
style={{
|
||||
maxHeight: `calc(100vh - ${props.isDashboardExists ? 175 : 300}px)`,
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
>
|
||||
{activeCategory &&
|
||||
widgetCategories.map((category, index) => (
|
||||
<WidgetCategoryItem
|
||||
key={category.name}
|
||||
onClick={handleWidgetCategoryClick}
|
||||
category={category}
|
||||
isSelected={activeCategory.name === category.name}
|
||||
selectedWidgetIds={selectedWidgetIds}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-9">
|
||||
<div
|
||||
className="grid grid-cols-4 gap-4 -mx-4 px-4 pb-40 items-start py-1"
|
||||
style={{ maxHeight: 'calc(100vh - 170px)', overflowY: 'auto' }}
|
||||
ref={scrollContainer}
|
||||
>
|
||||
{activeCategory &&
|
||||
activeCategory.widgets.map((widget: any) => (
|
||||
<WidgetWrapper
|
||||
key={widget.metricId}
|
||||
widget={widget}
|
||||
active={selectedWidgetIds.includes(widget.metricId)}
|
||||
isTemplate
|
||||
isSaved={widget.metricType === 'predefined'}
|
||||
onClick={() => dashboardStore.toggleWidgetSelection(widget)}
|
||||
/>
|
||||
))}
|
||||
{props.isDashboardExists && activeCategory?.name === 'custom' && (
|
||||
<div
|
||||
className={cn(
|
||||
'relative rounded border col-span-1 cursor-pointer',
|
||||
'flex flex-col items-center justify-center bg-white',
|
||||
'hover:bg-active-blue hover:shadow-border-main text-center py-16',
|
||||
)}
|
||||
onClick={props.handleCreateNew}
|
||||
>
|
||||
<Icon name="plus" size="16" />
|
||||
<span className="mt-2">{t('Create Metric')}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Loader>
|
||||
));
|
||||
}
|
||||
|
||||
export default DashboardMetricSelection;
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
import React from 'react';
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import { Button } from 'antd';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { dashboardMetricCreate, withSiteId } from 'App/routes';
|
||||
import DashboardForm from '../DashboardForm';
|
||||
import DashboardMetricSelection from '../DashboardMetricSelection';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PANEL_SIZES } from 'App/constants/panelSizes'
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
history: any;
|
||||
siteId?: string;
|
||||
dashboardId?: string;
|
||||
onMetricAdd?: () => void;
|
||||
}
|
||||
function DashboardModal(props: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { history, siteId, dashboardId } = props;
|
||||
const { dashboardStore } = useStore();
|
||||
const selectedWidgetsCount = useObserver(
|
||||
() => dashboardStore.selectedWidgets.length,
|
||||
);
|
||||
const { hideModal } = useModal();
|
||||
const dashboard = useObserver(() => dashboardStore.dashboardInstance);
|
||||
const loading = useObserver(() => dashboardStore.isSaving);
|
||||
|
||||
const onSave = () => {
|
||||
dashboardStore
|
||||
.save(dashboard)
|
||||
.then(async (syncedDashboard) => {
|
||||
if (dashboard.exists()) {
|
||||
await dashboardStore.fetch(dashboard.dashboardId);
|
||||
}
|
||||
dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
|
||||
history.push(
|
||||
withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId),
|
||||
);
|
||||
})
|
||||
.then(hideModal);
|
||||
};
|
||||
|
||||
const handleCreateNew = () => {
|
||||
const path = withSiteId(dashboardMetricCreate(dashboardId), siteId);
|
||||
props.onMetricAdd();
|
||||
history.push(path);
|
||||
hideModal();
|
||||
};
|
||||
const isDashboardExists = dashboard.exists();
|
||||
|
||||
return useObserver(() => (
|
||||
<div style={{ maxWidth: '85vw' }}>
|
||||
<div
|
||||
className="border-r shadow p-4 h-screen"
|
||||
style={{
|
||||
backgroundColor: '#FAFAFA',
|
||||
zIndex: 999,
|
||||
width: '100%',
|
||||
maxWidth: PANEL_SIZES.maxWidth,
|
||||
}}
|
||||
>
|
||||
<div className="mb-6 flex items-end justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl">
|
||||
{isDashboardExists
|
||||
? t('Add metrics to dashboard')
|
||||
: t('Create Dashboard')}
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-md">{t('Past 7 days data')}</span>
|
||||
</div>
|
||||
</div>
|
||||
{!isDashboardExists && (
|
||||
<>
|
||||
<DashboardForm />
|
||||
<p>
|
||||
{t(
|
||||
'Create new dashboard by choosing from the range of predefined metrics that you care about. You can always add your custom metrics later.',
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
<DashboardMetricSelection
|
||||
handleCreateNew={handleCreateNew}
|
||||
isDashboardExists={isDashboardExists}
|
||||
/>
|
||||
|
||||
<div className="flex items-center absolute bottom-0 left-0 right-0 bg-white border-t p-3">
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!dashboard.isValid || loading}
|
||||
onClick={onSave}
|
||||
className="flaot-left mr-2"
|
||||
>
|
||||
{isDashboardExists ? t('Add Selected to Dashboard') : t('Create')}
|
||||
</Button>
|
||||
<span className="ml-2 color-gray-medium">
|
||||
{selectedWidgetsCount} {t('Metrics')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
export default withRouter(DashboardModal);
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default } from './DashboardModal';
|
||||
|
|
@ -11,7 +11,6 @@ import withPageTitle from 'HOCs/withPageTitle';
|
|||
import withReport from 'App/components/hocs/withReport';
|
||||
import { useHistory } from 'react-router';
|
||||
import DashboardHeader from '../DashboardHeader';
|
||||
import DashboardModal from '../DashboardModal';
|
||||
import DashboardWidgetGrid from '../DashboardWidgetGrid';
|
||||
import AiQuery from './AiQuery';
|
||||
import { PANEL_SIZES } from 'App/constants/panelSizes'
|
||||
|
|
@ -69,15 +68,18 @@ function DashboardView(props: Props) {
|
|||
onAddWidgets();
|
||||
trimQuery();
|
||||
}
|
||||
dashboardStore.resetDensity();
|
||||
|
||||
return () => dashboardStore.resetSelectedDashboard();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const isExists = dashboardStore.getDashboardById(dashboardId);
|
||||
if (!isExists) {
|
||||
history.push(withSiteId('/dashboard', siteId));
|
||||
}
|
||||
const isExists = async () => dashboardStore.getDashboardById(dashboardId);
|
||||
isExists().then((res) => {
|
||||
if (!res) {
|
||||
history.push(withSiteId('/dashboard', siteId));
|
||||
}
|
||||
})
|
||||
}, [dashboardId]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -85,18 +87,6 @@ function DashboardView(props: Props) {
|
|||
dashboardStore.fetch(dashboard.dashboardId);
|
||||
}, [dashboard]);
|
||||
|
||||
const onAddWidgets = () => {
|
||||
dashboardStore.initDashboard(dashboard);
|
||||
showModal(
|
||||
<DashboardModal
|
||||
siteId={siteId}
|
||||
onMetricAdd={pushQuery}
|
||||
dashboardId={dashboardId}
|
||||
/>,
|
||||
{ right: true },
|
||||
);
|
||||
};
|
||||
|
||||
if (!dashboard) return null;
|
||||
|
||||
const originStr = window.env.ORIGIN || window.location.origin;
|
||||
|
|
@ -117,7 +107,6 @@ function DashboardView(props: Props) {
|
|||
<DashboardWidgetGrid
|
||||
siteId={siteId}
|
||||
dashboardId={dashboardId}
|
||||
onEditHandler={onAddWidgets}
|
||||
id="report"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,129 +0,0 @@
|
|||
import React from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Loader } from 'UI';
|
||||
import { Button } from 'antd';
|
||||
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { dashboardMetricCreate, withSiteId } from 'App/routes';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
siteId: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
function AddMetric({ history, siteId, title, description }: IProps) {
|
||||
const { t } = useTranslation();
|
||||
const [metrics, setMetrics] = React.useState<Record<string, any>[]>([]);
|
||||
|
||||
const { dashboardStore } = useStore();
|
||||
const { hideModal } = useModal();
|
||||
|
||||
React.useEffect(() => {
|
||||
dashboardStore?.fetchTemplates(true).then((cats: any[]) => {
|
||||
const customMetrics =
|
||||
cats.find((category) => category.name === 'custom')?.widgets || [];
|
||||
|
||||
setMetrics(customMetrics);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const dashboard = dashboardStore.selectedDashboard;
|
||||
const selectedWidgetIds = dashboardStore.selectedWidgets.map(
|
||||
(widget: any) => widget.metricId,
|
||||
);
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
|
||||
const onSave = () => {
|
||||
if (selectedWidgetIds.length === 0) return;
|
||||
dashboardStore
|
||||
.save(dashboard)
|
||||
.then(async (syncedDashboard: Record<string, any>) => {
|
||||
if (dashboard.exists()) {
|
||||
await dashboardStore.fetch(dashboard.dashboardId);
|
||||
}
|
||||
dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
|
||||
})
|
||||
.then(hideModal);
|
||||
};
|
||||
|
||||
const onCreateNew = () => {
|
||||
const path = withSiteId(
|
||||
dashboardMetricCreate(dashboard.dashboardId),
|
||||
siteId,
|
||||
);
|
||||
if (!queryParams.has('modal')) history.push('?modal=addMetric');
|
||||
history.push(path);
|
||||
hideModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '85vw', width: 1200 }}>
|
||||
<div
|
||||
className="border-l shadow h-screen"
|
||||
style={{ backgroundColor: '#FAFAFA', zIndex: 999, width: '100%' }}
|
||||
>
|
||||
<div className="py-6 px-8 flex items-start justify-between">
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-2xl">{title}</h1>
|
||||
<div className="text-disabled-text">{description}</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="text"
|
||||
className="text-main font-medium ml-2"
|
||||
onClick={onCreateNew}
|
||||
>
|
||||
+ {t('Create New')}
|
||||
</Button>
|
||||
</div>
|
||||
<Loader loading={dashboardStore.loadingTemplates}>
|
||||
<div
|
||||
className="grid h-full grid-cols-4 gap-4 px-8 items-start py-1"
|
||||
style={{
|
||||
maxHeight: 'calc(100vh - 160px)',
|
||||
overflowY: 'auto',
|
||||
gridAutoRows: 'max-content',
|
||||
}}
|
||||
>
|
||||
{metrics ? (
|
||||
metrics.map((metric: any) => (
|
||||
<WidgetWrapper
|
||||
key={metric.metricId}
|
||||
widget={metric}
|
||||
active={selectedWidgetIds.includes(metric.metricId)}
|
||||
isTemplate
|
||||
isSaved={metric.metricType === 'predefined'}
|
||||
onClick={() => dashboardStore.toggleWidgetSelection(metric)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div>{t('No custom metrics created.')}</div>
|
||||
)}
|
||||
</div>
|
||||
</Loader>
|
||||
|
||||
<div className="py-4 border-t px-8 bg-white w-full flex items-center justify-between">
|
||||
<div>
|
||||
{t('Selected')}
|
||||
<span className="font-medium">{selectedWidgetIds.length}</span>
|
||||
{t('out of')}
|
||||
<span className="font-medium">{metrics ? metrics.length : 0}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={selectedWidgetIds.length === 0}
|
||||
onClick={onSave}
|
||||
>
|
||||
{t('Add Selected')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(observer(AddMetric));
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
import React from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Icon } from 'UI';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { useStore } from 'App/mstore';
|
||||
import cn from 'classnames';
|
||||
import AddMetric from './AddMetric';
|
||||
import AddPredefinedMetric from './AddPredefinedMetric';
|
||||
|
||||
interface AddMetricButtonProps {
|
||||
iconName: 'bar-pencil' | 'grid-check';
|
||||
title: string;
|
||||
description: string;
|
||||
isPremade?: boolean;
|
||||
isPopup?: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
function AddMetricButton({
|
||||
iconName,
|
||||
title,
|
||||
description,
|
||||
onClick,
|
||||
isPremade,
|
||||
isPopup,
|
||||
}: AddMetricButtonProps) {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'flex items-center hover:bg-gray-lightest group rounded border cursor-pointer',
|
||||
isPremade
|
||||
? 'bg-figmaColors-primary-outlined-hover-background hover:!border-tealx'
|
||||
: 'hover:!border-teal bg-figmaColors-secondary-outlined-hover-background',
|
||||
isPopup ? 'p-4 z-50' : 'px-4 py-8 flex-col',
|
||||
)}
|
||||
style={{ borderColor: 'rgb(238, 238, 238)' }}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'p-6 my-3 rounded-full group-hover:bg-gray-light',
|
||||
isPremade
|
||||
? 'bg-figmaColors-primary-outlined-hover-background fill-figmaColors-accent-secondary group-hover:!bg-figmaColors-accent-secondary group-hover:!fill-white'
|
||||
: 'bg-figmaColors-secondary-outlined-hover-background fill-figmaColors-secondary-outlined-resting-border group-hover:!bg-teal group-hover:!fill-white',
|
||||
)}
|
||||
>
|
||||
<Icon name={iconName} size={26} style={{ fill: 'inherit' }} />
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
isPopup
|
||||
? 'flex flex-col text-left ml-4'
|
||||
: 'flex flex-col text-center items-center'
|
||||
}
|
||||
>
|
||||
<div className="font-bold text-base text-figmaColors-text-primary">
|
||||
{title}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'text-disabled-test text-figmaColors-text-primary text-base',
|
||||
isPopup ? 'w-full' : 'mt-2 w-2/3 text-center',
|
||||
)}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
siteId: string;
|
||||
isPopup?: boolean;
|
||||
onAction?: () => void;
|
||||
}
|
||||
|
||||
function AddMetricContainer({ siteId, isPopup, onAction }: Props) {
|
||||
const { showModal } = useModal();
|
||||
const { dashboardStore } = useStore();
|
||||
|
||||
const onAddCustomMetrics = () => {
|
||||
onAction?.();
|
||||
dashboardStore.initDashboard(dashboardStore.selectedDashboard);
|
||||
showModal(
|
||||
<AddMetric
|
||||
siteId={siteId}
|
||||
title="Custom Metrics"
|
||||
description="Metrics that are manually created by you or your team."
|
||||
/>,
|
||||
{ right: true },
|
||||
);
|
||||
};
|
||||
|
||||
const onAddPredefinedMetrics = () => {
|
||||
onAction?.();
|
||||
dashboardStore.initDashboard(dashboardStore.selectedDashboard);
|
||||
showModal(
|
||||
<AddPredefinedMetric
|
||||
siteId={siteId}
|
||||
title="Ready-Made Metrics"
|
||||
description="Curated metrics predfined by OpenReplay."
|
||||
/>,
|
||||
{ right: true },
|
||||
);
|
||||
};
|
||||
|
||||
const classes = isPopup
|
||||
? 'bg-white border rounded p-4 grid grid-rows-2 gap-4'
|
||||
: 'bg-white border border-dashed hover:!border-gray-medium rounded p-8 grid grid-cols-2 gap-8';
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
borderColor: 'rgb(238, 238, 238)',
|
||||
height: isPopup ? undefined : 300,
|
||||
}}
|
||||
className={classes}
|
||||
>
|
||||
<AddMetricButton
|
||||
title="+ Add Custom Metric"
|
||||
description="Metrics that are manually created by you or your team"
|
||||
iconName="bar-pencil"
|
||||
onClick={onAddCustomMetrics}
|
||||
isPremade
|
||||
isPopup={isPopup}
|
||||
/>
|
||||
<AddMetricButton
|
||||
title="+ Add Ready-Made Metric"
|
||||
description="Curated metrics predfined by OpenReplay."
|
||||
iconName="grid-check"
|
||||
onClick={onAddPredefinedMetrics}
|
||||
isPopup={isPopup}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(AddMetricContainer);
|
||||
|
|
@ -12,7 +12,6 @@ import { useTranslation } from 'react-i18next';
|
|||
interface Props {
|
||||
siteId: string;
|
||||
dashboardId: string;
|
||||
onEditHandler: () => void;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
import React from 'react';
|
||||
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { withSiteId } from 'App/routes';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
list: any;
|
||||
siteId: any;
|
||||
selectedList: any;
|
||||
}
|
||||
function GridView(props: Props) {
|
||||
const { siteId, list, selectedList, history } = props;
|
||||
|
||||
const onItemClick = (metricId: number) => {
|
||||
const path = withSiteId(`/metrics/${metricId}`, siteId);
|
||||
history.push(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-4 m-4 items-start">
|
||||
{list.map((metric: any) => (
|
||||
<React.Fragment key={metric.metricId}>
|
||||
<WidgetWrapper
|
||||
key={metric.metricId}
|
||||
widget={metric}
|
||||
isGridView
|
||||
active={selectedList.includes(metric.metricId)}
|
||||
isSaved
|
||||
onClick={() => onItemClick(parseInt(metric.metricId))}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(GridView);
|
||||
|
|
@ -54,7 +54,7 @@ function WidgetChart(props: Props) {
|
|||
});
|
||||
const { isSaved = false, metric, isTemplate } = props;
|
||||
const { dashboardStore, metricStore } = useStore();
|
||||
const _metric: any = props.isPreview ? metricStore.instance : props.metric;
|
||||
const _metric: any = props.metric;
|
||||
const data = _metric.data;
|
||||
const { period } = dashboardStore;
|
||||
const { drillDownPeriod } = dashboardStore;
|
||||
|
|
@ -66,7 +66,6 @@ function WidgetChart(props: Props) {
|
|||
const metricParams = _metric.params;
|
||||
const prevMetricRef = useRef<any>();
|
||||
const isMounted = useIsMounted();
|
||||
const [metricData, setMetricData] = useState<any>(data);
|
||||
const [compData, setCompData] = useState<any>(null);
|
||||
const [enabledRows, setEnabledRows] = useState<string[]>(
|
||||
_metric.series.map((s) => s.name),
|
||||
|
|
@ -158,16 +157,9 @@ function WidgetChart(props: Props) {
|
|||
setStale(true);
|
||||
}, 4000);
|
||||
dashboardStore
|
||||
.fetchMetricChartData(
|
||||
metric,
|
||||
payload,
|
||||
isSaved,
|
||||
period,
|
||||
isComparison
|
||||
)
|
||||
.fetchMetricChartData(metric, payload, isSaved, period, isComparison)
|
||||
.then((res) => {
|
||||
if (isComparison) setCompData(res);
|
||||
else setMetricData(res);
|
||||
clearTimeout(tm);
|
||||
setStale(false);
|
||||
})
|
||||
|
|
@ -189,10 +181,10 @@ function WidgetChart(props: Props) {
|
|||
}
|
||||
prevMetricRef.current = _metric;
|
||||
const timestmaps = drillDownPeriod.toTimestamps();
|
||||
const density = props.isPreview ? metric.density : dashboardStore.selectedDensity
|
||||
const density = dashboardStore.selectedDensity;
|
||||
const payload = isSaved
|
||||
? { ...metricParams, density }
|
||||
: { ...params, ...timestmaps, ..._metric.toJson(), density };
|
||||
? { ...metricParams, density }
|
||||
: { ...params, ...timestmaps, ..._metric.toJson(), density };
|
||||
debounceRequest(
|
||||
_metric,
|
||||
payload,
|
||||
|
|
@ -260,7 +252,7 @@ function WidgetChart(props: Props) {
|
|||
const renderChart = React.useCallback(() => {
|
||||
const { metricType, metricOf } = _metric;
|
||||
const { viewType } = _metric;
|
||||
const metricWithData = { ..._metric, data: metricData };
|
||||
const metricWithData = { ..._metric, data };
|
||||
|
||||
if (metricType === FUNNEL) {
|
||||
if (viewType === 'table') {
|
||||
|
|
@ -274,7 +266,7 @@ function WidgetChart(props: Props) {
|
|||
valueLabel?: string;
|
||||
}[] = [
|
||||
{
|
||||
value: metricData.funnel.totalConversionsPercentage,
|
||||
value: data.funnel.totalConversionsPercentage,
|
||||
compData: compData
|
||||
? compData.funnel.totalConversionsPercentage
|
||||
: undefined,
|
||||
|
|
@ -298,7 +290,7 @@ function WidgetChart(props: Props) {
|
|||
return (
|
||||
<FunnelWidget
|
||||
metric={_metric}
|
||||
data={metricData}
|
||||
data={data}
|
||||
compData={compData}
|
||||
isWidget={isSaved || isTemplate}
|
||||
/>
|
||||
|
|
@ -314,14 +306,14 @@ function WidgetChart(props: Props) {
|
|||
<WidgetPredefinedChart
|
||||
isTemplate={isTemplate}
|
||||
metric={defaultMetric}
|
||||
data={metricData}
|
||||
data={data}
|
||||
predefinedKey={_metric.metricOf}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (metricType === TIMESERIES) {
|
||||
const chartData = { ...metricData };
|
||||
const chartData = { ...data };
|
||||
chartData.namesMap = Array.isArray(chartData.namesMap)
|
||||
? chartData.namesMap.map((n) => (enabledRows.includes(n) ? n : null))
|
||||
: chartData.namesMap;
|
||||
|
|
@ -419,7 +411,7 @@ function WidgetChart(props: Props) {
|
|||
return (
|
||||
<CustomMetricPercentage
|
||||
inGrid={!props.isPreview}
|
||||
data={metricData[0]}
|
||||
data={data[0]}
|
||||
colors={colors}
|
||||
params={params}
|
||||
label={
|
||||
|
|
@ -436,14 +428,14 @@ function WidgetChart(props: Props) {
|
|||
if (viewType === 'metric') {
|
||||
const values: { value: number; compData?: number; series: string }[] =
|
||||
[];
|
||||
for (let i = 0; i < metricData.namesMap.length; i++) {
|
||||
if (!metricData.namesMap[i]) {
|
||||
for (let i = 0; i < data.namesMap.length; i++) {
|
||||
if (!data.namesMap[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
values.push({
|
||||
value: metricData.chart.reduce(
|
||||
(acc, curr) => acc + curr[metricData.namesMap[i]],
|
||||
value: data.chart.reduce(
|
||||
(acc, curr) => acc + curr[data.namesMap[i]],
|
||||
0,
|
||||
),
|
||||
compData: compData
|
||||
|
|
@ -452,7 +444,7 @@ function WidgetChart(props: Props) {
|
|||
0,
|
||||
)
|
||||
: undefined,
|
||||
series: metricData.namesMap[i],
|
||||
series: data.namesMap[i],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -477,7 +469,7 @@ function WidgetChart(props: Props) {
|
|||
return (
|
||||
<CustomMetricTableSessions
|
||||
metric={_metric}
|
||||
data={metricData}
|
||||
data={data}
|
||||
isTemplate={isTemplate}
|
||||
isEdit={!isSaved && !isTemplate}
|
||||
/>
|
||||
|
|
@ -487,7 +479,7 @@ function WidgetChart(props: Props) {
|
|||
return (
|
||||
<CustomMetricTableErrors
|
||||
metric={_metric}
|
||||
data={metricData}
|
||||
data={data}
|
||||
// isTemplate={isTemplate}
|
||||
isEdit={!isSaved && !isTemplate}
|
||||
/>
|
||||
|
|
@ -497,7 +489,7 @@ function WidgetChart(props: Props) {
|
|||
return (
|
||||
<SessionsBy
|
||||
metric={_metric}
|
||||
data={metricData}
|
||||
data={data}
|
||||
onClick={onChartClick}
|
||||
isTemplate={isTemplate}
|
||||
/>
|
||||
|
|
@ -530,10 +522,10 @@ function WidgetChart(props: Props) {
|
|||
}
|
||||
|
||||
if (metricType === INSIGHTS) {
|
||||
return <InsightsCard data={metricData} />;
|
||||
return <InsightsCard data={data} />;
|
||||
}
|
||||
|
||||
if (metricType === USER_PATH && metricData && metricData.links) {
|
||||
if (metricType === USER_PATH && data && data.links) {
|
||||
const isUngrouped = props.isPreview
|
||||
? !(_metric.hideExcess ?? true)
|
||||
: false;
|
||||
|
|
@ -541,7 +533,7 @@ function WidgetChart(props: Props) {
|
|||
return (
|
||||
<SankeyChart
|
||||
height={height}
|
||||
data={metricData}
|
||||
data={data}
|
||||
inGrid={!props.isPreview}
|
||||
onChartClick={(filters: any) => {
|
||||
dashboardStore.drillDownFilter.merge({ filters, page: 1 });
|
||||
|
|
@ -556,7 +548,7 @@ function WidgetChart(props: Props) {
|
|||
if (viewType === 'trend') {
|
||||
return (
|
||||
<LineChart
|
||||
data={metricData}
|
||||
data={data}
|
||||
colors={colors}
|
||||
params={params}
|
||||
onClick={onChartClick}
|
||||
|
|
@ -569,7 +561,7 @@ function WidgetChart(props: Props) {
|
|||
}
|
||||
console.log('Unknown metric type', metricType);
|
||||
return <div>{t('Unknown metric type')}</div>;
|
||||
}, [data, compData, enabledRows, _metric, metricData]);
|
||||
}, [data, compData, enabledRows, _metric, data]);
|
||||
|
||||
const showTable =
|
||||
_metric.metricType === TIMESERIES &&
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { Space } from 'antd';
|
|||
import { CUSTOM_RANGE, DATE_RANGE_COMPARISON_OPTIONS } from 'App/dateRange';
|
||||
import Period from 'Types/app/period';
|
||||
import RangeGranularity from './RangeGranularity';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function WidgetDateRange({
|
||||
label = 'Time Range',
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useRef, lazy } from 'react';
|
||||
import cn from 'classnames';
|
||||
import { ItemMenu, TextEllipsis } from 'UI';
|
||||
import { TextEllipsis } from 'UI';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { withSiteId, dashboardMetricDetails } from 'App/routes';
|
||||
import { FilterKey } from 'App/types/filter/filterType';
|
||||
import { TIMESERIES } from 'App/constants/card';
|
||||
import TemplateOverlay from './TemplateOverlay';
|
||||
|
||||
const WidgetChart = lazy(
|
||||
|
|
@ -45,7 +44,6 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
|
|||
isGridView = false,
|
||||
} = props;
|
||||
const { widget } = props;
|
||||
const isTimeSeries = widget.metricType === TIMESERIES;
|
||||
const isPredefined = widget.metricType === 'predefined';
|
||||
const dashboard = dashboardStore.selectedDashboard;
|
||||
|
||||
|
|
@ -73,13 +71,6 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
|
|||
}),
|
||||
});
|
||||
|
||||
const onDelete = async () => {
|
||||
dashboardStore.deleteDashboardWidget(
|
||||
dashboard?.dashboardId!,
|
||||
widget.widgetId,
|
||||
);
|
||||
};
|
||||
|
||||
const onChartClick = () => {
|
||||
if (!isSaved || isPredefined) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ interface Props {
|
|||
isSaved?: boolean;
|
||||
}
|
||||
|
||||
function WidgetWrapperNew(props: Props & RouteComponentProps) {
|
||||
function WidgetWrapperDashboard(props: Props & RouteComponentProps) {
|
||||
const { dashboardStore, metricStore } = useStore();
|
||||
const {
|
||||
isWidget = false,
|
||||
|
|
@ -178,4 +178,4 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) {
|
|||
);
|
||||
}
|
||||
|
||||
export default withRouter(observer(WidgetWrapperNew));
|
||||
export default withRouter(observer(WidgetWrapperDashboard));
|
||||
|
|
|
|||
|
|
@ -14,70 +14,39 @@ interface DashboardFilter {
|
|||
}
|
||||
export default class DashboardStore {
|
||||
siteId: any = null;
|
||||
|
||||
dashboards: Dashboard[] = [];
|
||||
|
||||
selectedDashboard: Dashboard | null = null;
|
||||
|
||||
dashboardInstance: Dashboard = new Dashboard();
|
||||
|
||||
selectedWidgets: Widget[] = [];
|
||||
|
||||
currentWidget: Widget = new Widget();
|
||||
|
||||
widgetCategories: any[] = [];
|
||||
|
||||
widgets: Widget[] = [];
|
||||
|
||||
period: Record<string, any> = Period({ rangeName: LAST_24_HOURS });
|
||||
|
||||
drillDownFilter: Filter = new Filter();
|
||||
|
||||
comparisonFilter: Filter = new Filter();
|
||||
|
||||
drillDownPeriod: Record<string, any> = Period({ rangeName: LAST_24_HOURS });
|
||||
|
||||
selectedDensity: number = 7;
|
||||
|
||||
comparisonPeriods: Record<string, any> = {};
|
||||
|
||||
startTimestamp: number = 0;
|
||||
|
||||
endTimestamp: number = 0;
|
||||
|
||||
pendingRequests: number = 0;
|
||||
|
||||
filter: DashboardFilter = { showMine: false, query: '' };
|
||||
|
||||
// Metrics
|
||||
metricsPage: number = 1;
|
||||
|
||||
metricsPageSize: number = 10;
|
||||
|
||||
metricsSearch: string = '';
|
||||
|
||||
// Loading states
|
||||
isLoading: boolean = false;
|
||||
|
||||
isSaving: boolean = false;
|
||||
|
||||
isDeleting: boolean = false;
|
||||
|
||||
loadingTemplates: boolean = false;
|
||||
|
||||
fetchingDashboard: boolean = false;
|
||||
|
||||
sessionsLoading: boolean = false;
|
||||
|
||||
showAlertModal: boolean = false;
|
||||
|
||||
// Pagination
|
||||
page: number = 1;
|
||||
|
||||
pageSize: number = 10;
|
||||
|
||||
dashboardsSearch: string = '';
|
||||
|
||||
sort: any = { by: 'desc' };
|
||||
|
||||
constructor() {
|
||||
|
|
@ -94,6 +63,10 @@ export default class DashboardStore {
|
|||
)
|
||||
}
|
||||
|
||||
resetDensity = () => {
|
||||
this.createDensity(this.period.getDuration());
|
||||
}
|
||||
|
||||
createDensity = (duration: number) => {
|
||||
const densityOpts = calculateGranularities(duration);
|
||||
const defaultOption = densityOpts[densityOpts.length - 2];
|
||||
|
|
@ -212,6 +185,7 @@ export default class DashboardStore {
|
|||
this.currentWidget.update(widget);
|
||||
}
|
||||
|
||||
listFetched = false;
|
||||
fetchList(): Promise<any> {
|
||||
this.isLoading = true;
|
||||
|
||||
|
|
@ -226,6 +200,7 @@ export default class DashboardStore {
|
|||
})
|
||||
.finally(() => {
|
||||
runInAction(() => {
|
||||
this.listFetched = true;
|
||||
this.isLoading = false;
|
||||
});
|
||||
});
|
||||
|
|
@ -388,7 +363,24 @@ export default class DashboardStore {
|
|||
new Dashboard();
|
||||
};
|
||||
|
||||
getDashboardById = (dashboardId: string) => {
|
||||
getDashboardById = async (dashboardId: string) => {
|
||||
if (!this.listFetched) {
|
||||
const maxWait = (5*1000)/250;
|
||||
let count = 0;
|
||||
await new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
if (this.listFetched) {
|
||||
clearInterval(interval);
|
||||
resolve(true);
|
||||
}
|
||||
if (count >= maxWait) {
|
||||
clearInterval(interval);
|
||||
resolve(false);
|
||||
}
|
||||
count++;
|
||||
}, 250);
|
||||
})
|
||||
}
|
||||
const dashboard = this.dashboards.find((d) => d.dashboardId == dashboardId);
|
||||
|
||||
if (dashboard) {
|
||||
|
|
@ -546,6 +538,7 @@ export default class DashboardStore {
|
|||
params,
|
||||
isSaved
|
||||
);
|
||||
console.log('db store', params)
|
||||
const res = metric.setData(data, period, isComparison, data.density)
|
||||
resolve(res);
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue