fix(ui): clean up api calls, remove dead features from dashboards

This commit is contained in:
sylenien 2022-08-23 12:15:37 +02:00
parent 21fb26b60d
commit e02f873970
9 changed files with 235 additions and 253 deletions

View file

@ -36,7 +36,6 @@ function DashbaordListModal(props: Props) {
leading = {(
<div className="ml-2 flex items-center">
{item.isPublic && <div className="p-1"><Icon name="user-friends" color="gray-light" size="16" /></div>}
{item.isPinned && <div className="p-1"><Icon name="pin-fill" size="16" /></div>}
</div>
)}
/>
@ -47,4 +46,4 @@ function DashbaordListModal(props: Props) {
);
}
export default withRouter(DashbaordListModal);
export default withRouter(DashbaordListModal);

View file

@ -1,6 +1,6 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Button } from 'UI';
import { Button, Loader } from 'UI';
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal';
@ -8,84 +8,105 @@ import { dashboardMetricCreate, withSiteId } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom';
interface IProps extends RouteComponentProps {
metrics: any[];
siteId: string;
title: string;
description: string;
siteId: string;
title: string;
description: string;
}
function AddMetric({ metrics, history, siteId, title, description }: IProps) {
const { dashboardStore } = useStore();
const { hideModal } = useModal();
function AddMetric({ history, siteId, title, description }: IProps) {
const [metrics, setMetrics] = React.useState<Record<string, any>[]>([]);
const dashboard = dashboardStore.selectedDashboard;
const selectedWidgetIds = dashboardStore.selectedWidgets.map((widget: any) => widget.metricId);
const queryParams = new URLSearchParams(location.search);
const { dashboardStore } = useStore();
const { hideModal } = useModal();
const onSave = () => {
if (selectedWidgetIds.length === 0) return;
dashboardStore
.save(dashboard)
.then(async (syncedDashboard) => {
if (dashboard.exists()) {
await dashboardStore.fetch(dashboard.dashboardId);
}
dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
})
.then(hideModal);
};
React.useEffect(() => {
dashboardStore?.fetchTemplates(true).then((cats: any[]) => {
const customMetrics = cats.find((category) => category.name === 'custom')?.widgets || [];
const onCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId);
if (!queryParams.has('modal')) history.push('?modal=addMetric');
history.push(path);
hideModal();
};
setMetrics(customMetrics);
});
}, []);
return (
<div style={{ maxWidth: '85vw', width: 1200 }}>
<div className="border-l shadow h-screen" style={{ backgroundColor: '#FAFAFA', zIndex: 999, width: '100%' }}>
<div className="mb-6 pt-8 px-8 flex items-start justify-between">
<div className="flex flex-col">
<h1 className="text-2xl">{title}</h1>
<div className="text-disabled-text">{description}</div>
</div>
<Button variant="text-primary" className="font-medium ml-2" onClick={onCreateNew}>
+ Create New
</Button>
</div>
const dashboard = dashboardStore.selectedDashboard;
const selectedWidgetIds = dashboardStore.selectedWidgets.map((widget: any) => widget.metricId);
const queryParams = new URLSearchParams(location.search);
<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={true}
isWidget={metric.metricType === 'predefined'}
onClick={() => dashboardStore.toggleWidgetSelection(metric)}
/>
)) : (
<div>No custom metrics created.</div>
)}
</div>
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);
};
<div className="py-4 border-t px-8 bg-white w-full flex items-center justify-between">
<div>
{'Selected '}
<span className="font-semibold">{selectedWidgetIds.length}</span>
{' out of '}
<span className="font-semibold">{metrics ? metrics.length : 0}</span>
</div>
<Button variant="primary" disabled={selectedWidgetIds.length === 0} onClick={onSave}>
Add Selected
</Button>
</div>
</div>
const onCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId);
if (!queryParams.has('modal')) history.push('?modal=addMetric');
history.push(path);
hideModal();
};
return (
<div style={{ maxWidth: '85vw', width: 1200 }}>
<div
className="border-l shadow h-screen"
style={{ backgroundColor: '#FAFAFA', zIndex: 999, width: '100%' }}
>
<div className="mb-6 pt-8 px-8 flex items-start justify-between">
<div className="flex flex-col">
<h1 className="text-2xl">{title}</h1>
<div className="text-disabled-text">{description}</div>
</div>
<Button variant="text-primary" className="font-medium ml-2" onClick={onCreateNew}>
+ 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={true}
isWidget={metric.metricType === 'predefined'}
onClick={() => dashboardStore.toggleWidgetSelection(metric)}
/>
))
) : (
<div>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>
{'Selected '}
<span className="font-semibold">{selectedWidgetIds.length}</span>
{' out of '}
<span className="font-semibold">{metrics ? metrics.length : 0}</span>
</div>
<Button variant="primary" disabled={selectedWidgetIds.length === 0} onClick={onSave}>
Add Selected
</Button>
</div>
</div>
</div>
);
}
export default withRouter(observer(AddMetric));

View file

@ -49,13 +49,8 @@ function AddMetricButton({ iconName, title, description, onClick, isPremade, isP
function AddMetricContainer({ siteId, isPopup }: any) {
const { showModal } = useModal();
const [categories, setCategories] = React.useState<Record<string, any>[]>([]);
const { dashboardStore } = useStore();
React.useEffect(() => {
dashboardStore?.fetchTemplates(true).then((cats) => setCategories(cats));
}, []);
const onAddCustomMetrics = () => {
dashboardStore.initDashboard(dashboardStore.selectedDashboard);
showModal(
@ -63,7 +58,6 @@ function AddMetricContainer({ siteId, isPopup }: any) {
siteId={siteId}
title="Custom Metrics"
description="Metrics that are manually created by you or your team."
metrics={categories.find((category) => category.name === 'custom')?.widgets}
/>,
{ right: true }
);
@ -76,7 +70,6 @@ function AddMetricContainer({ siteId, isPopup }: any) {
siteId={siteId}
title="Ready-Made Metrics"
description="Curated metrics predfined by OpenReplay."
categories={categories.filter((category) => category.name !== 'custom')}
/>,
{ right: true }
);

View file

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

View file

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

View file

@ -3,7 +3,15 @@ import cn from 'classnames';
import styles from './loader.module.css';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
const Loader = React.memo(
interface Props {
className?: string
loading?: boolean
children?: React.ReactNode
size?: number
style?: Record<string, any>
}
const Loader = React.memo<Props>(
({
className = '',
loading = true,
@ -12,7 +20,9 @@ const Loader = React.memo(
style = { minHeight: '150px' },
}) =>
!loading ? (
children
<>
{children}
</>
) : (
<div className={cn(styles.wrapper, className)} style={style}>
{/* <div className={ styles.loader } data-size={ size } /> */}

View file

@ -1,8 +1,6 @@
import {
makeAutoObservable,
runInAction,
observable,
action,
} from "mobx";
import Dashboard, { IDashboard } from "./types/dashboard";
import Widget, { IWidget } from "./types/widget";
@ -39,6 +37,7 @@ export interface IDashboardStore {
metricsSearch: string;
isLoading: boolean;
loadingTemplates: boolean;
isSaving: boolean;
isDeleting: boolean;
fetchingDashboard: boolean;
@ -74,7 +73,6 @@ export interface IDashboardStore {
selectDashboardById(dashboardId: string): void;
getDashboardById(dashboardId: string): boolean;
setSiteId(siteId: any): void;
selectDefaultDashboard(): Promise<IDashboard>;
saveMetric(metric: IWidget, dashboardId?: string): Promise<any>;
fetchTemplates(hardRefresh: boolean): Promise<any>;
@ -82,7 +80,6 @@ export interface IDashboardStore {
addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise<any>;
setDrillDownPeriod(period: any): void;
updatePinned(dashboardId: string): Promise<any>;
fetchMetricChartData(
metric: IWidget,
data: any,
@ -392,22 +389,6 @@ export default class DashboardStore implements IDashboardStore {
this.siteId = siteId;
};
selectDefaultDashboard = (): Promise<Dashboard> => {
return new Promise((resolve, reject) => {
if (this.dashboards.length > 0) {
const pinnedDashboard = this.dashboards.find((d) => d.isPinned);
if (pinnedDashboard) {
this.selectedDashboard = pinnedDashboard;
} else {
this.selectedDashboard = this.dashboards[0];
}
resolve(this.selectedDashboard);
}
reject(new Error("No dashboards found"));
});
};
fetchTemplates(hardRefresh): Promise<any> {
this.loadingTemplates = true
return new Promise((resolve, reject) => {
@ -473,28 +454,6 @@ export default class DashboardStore implements IDashboardStore {
});
}
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;
}
});
})
.catch(() => {
toast.error("Dashboard could not be pinned");
})
.finally(() => {
// this.isSaving = false
});
}
setPeriod(period: any) {
this.period = Period({
start: period.start,

View file

@ -12,7 +12,6 @@ export interface IDashboard {
widgets: IWidget[]
metrics: any[]
isValid: boolean
isPinned: boolean
currentWidget: IWidget
config: any
createdAt: Date
@ -43,7 +42,6 @@ export default class Dashboard implements IDashboard {
widgets: IWidget[] = []
metrics: any[] = []
isValid: boolean = false
isPinned: boolean = false
currentWidget: IWidget = new Widget()
config: any = {}
createdAt: Date = new Date()
@ -78,7 +76,6 @@ export default class Dashboard implements IDashboard {
this.name = json.name
this.description = json.description
this.isPublic = json.isPublic
this.isPinned = json.isPinned
this.createdAt = DateTime.fromMillis(new Date(json.createdAt).getTime())
this.widgets = json.widgets ? json.widgets.map((w: Widget) => new Widget().fromJson(w)).sort((a: Widget, b: Widget) => a.position - b.position) : []
})

View file

@ -18,7 +18,6 @@ export interface IDashboardService {
saveWidget(dashboardId: string, widget: IWidget): Promise<any>
deleteWidget(dashboardId: string, widgetId: string): Promise<any>
updatePinned(dashboardId: string): Promise<any>
}
@ -151,14 +150,4 @@ export default class DashboardService implements IDashboardService {
.then(response => response.json())
.then(response => response.data || {});
}
/**
* Update the pinned status of a dashboard.
* @param dashboardId
* @returns
*/
updatePinned(dashboardId: string): Promise<any> {
return this.client.get(`/dashboards/${dashboardId}/pin`, {})
.then(response => response.json())
}
}
}