diff --git a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx index d91d058b0..9b93b9942 100644 --- a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx +++ b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx @@ -36,7 +36,6 @@ function DashbaordListModal(props: Props) { leading = {(
{item.isPublic &&
} - {item.isPinned &&
}
)} /> @@ -47,4 +46,4 @@ function DashbaordListModal(props: Props) { ); } -export default withRouter(DashbaordListModal); \ No newline at end of file +export default withRouter(DashbaordListModal); diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx index 48f156098..b71fb1a81 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx @@ -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[]>([]); - 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 ( -
-
-
-
-

{title}

-
{description}
-
- - - -
+ const dashboard = dashboardStore.selectedDashboard; + const selectedWidgetIds = dashboardStore.selectedWidgets.map((widget: any) => widget.metricId); + const queryParams = new URLSearchParams(location.search); -
- {metrics ? metrics.map((metric: any) => ( - dashboardStore.toggleWidgetSelection(metric)} - /> - )) : ( -
No custom metrics created.
- )} -
+ const onSave = () => { + if (selectedWidgetIds.length === 0) return; + dashboardStore + .save(dashboard) + .then(async (syncedDashboard: Record) => { + if (dashboard.exists()) { + await dashboardStore.fetch(dashboard.dashboardId); + } + dashboardStore.selectDashboardById(syncedDashboard.dashboardId); + }) + .then(hideModal); + }; -
-
- {'Selected '} - {selectedWidgetIds.length} - {' out of '} - {metrics ? metrics.length : 0} -
- -
-
+ const onCreateNew = () => { + const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId); + if (!queryParams.has('modal')) history.push('?modal=addMetric'); + history.push(path); + hideModal(); + }; + + return ( +
+
+
+
+

{title}

+
{description}
+
+ +
- ); + +
+ {metrics ? ( + metrics.map((metric: any) => ( + dashboardStore.toggleWidgetSelection(metric)} + /> + )) + ) : ( +
No custom metrics created.
+ )} +
+
+ +
+
+ {'Selected '} + {selectedWidgetIds.length} + {' out of '} + {metrics ? metrics.length : 0} +
+ +
+
+
+ ); } export default withRouter(observer(AddMetric)); diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetricContainer.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetricContainer.tsx index fb1105856..b33cccf76 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetricContainer.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetricContainer.tsx @@ -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[]>([]); 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 } ); diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx index 8ab3cfeb0..4e95a2c6e 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx @@ -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[]; - 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>(); +function AddPredefinedMetric({ history, siteId, title, description }: IProps) { + const [categories, setCategories] = React.useState([]); + const { dashboardStore } = useStore(); + const { hideModal } = useModal(); + const [activeCategory, setActiveCategory] = React.useState>(); - const scrollContainer = React.useRef(null); + const scrollContainer = React.useRef(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 ( +
+
+
+
+

{title}

+
{description}
+
- const onCreateNew = () => { - const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId); - if (!queryParams.has('modal')) history.push('?modal=addMetric'); - history.push(path); - hideModal(); - }; - - return ( -
-
-
-
-

{title}

-
{description}
-
- - -
- -
-
-
- {activeCategory && - categories.map((category) => ( - - - - ))} -
-
- -
- {activeCategory && - activeCategory.widgets.map((metric: any) => ( - - dashboardStore.toggleWidgetSelection(metric)} - /> - - ))} -
-
- -
-
- {'Selected '} - {selectedWidgetIds.length} - {' out of '} - {totalMetricCount} -
- -
-
+
- ); + +
+
+
+ {activeCategory && + categories.map((category) => ( + + + + ))} +
+
+ +
+ {activeCategory && + activeCategory.widgets.map((metric: any) => ( + + dashboardStore.toggleWidgetSelection(metric)} + /> + + ))} +
+
+
+ +
+
+ {'Selected '} + {selectedWidgetIds.length} + {' out of '} + {totalMetricCount} +
+ +
+
+
+ ); } export default withRouter(observer(AddPredefinedMetric)); diff --git a/frontend/app/components/Header/SiteDropdown.js b/frontend/app/components/Header/SiteDropdown.js index 7f89eee82..04deb111b 100644 --- a/frontend/app/components/Header/SiteDropdown.js +++ b/frontend/app/components/Header/SiteDropdown.js @@ -54,7 +54,6 @@ export default class SiteDropdown extends React.PureComponent { this.props.clearSearchLive(); mstore.initClient(); - mstore.dashboardStore.selectDefaultDashboard(); } render() { diff --git a/frontend/app/components/ui/Loader/Loader.js b/frontend/app/components/ui/Loader/Loader.tsx similarity index 73% rename from frontend/app/components/ui/Loader/Loader.js rename to frontend/app/components/ui/Loader/Loader.tsx index e090dcf8d..50a4eeb46 100644 --- a/frontend/app/components/ui/Loader/Loader.js +++ b/frontend/app/components/ui/Loader/Loader.tsx @@ -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 +} + +const Loader = React.memo( ({ className = '', loading = true, @@ -12,7 +20,9 @@ const Loader = React.memo( style = { minHeight: '150px' }, }) => !loading ? ( - children + <> + {children} + ) : (
{/*
*/} diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index f327b1c60..3f77bace4 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -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; saveMetric(metric: IWidget, dashboardId?: string): Promise; fetchTemplates(hardRefresh: boolean): Promise; @@ -82,7 +80,6 @@ export interface IDashboardStore { addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise; setDrillDownPeriod(period: any): void; - updatePinned(dashboardId: string): Promise; fetchMetricChartData( metric: IWidget, data: any, @@ -392,22 +389,6 @@ export default class DashboardStore implements IDashboardStore { this.siteId = siteId; }; - selectDefaultDashboard = (): Promise => { - 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 { this.loadingTemplates = true return new Promise((resolve, reject) => { @@ -473,28 +454,6 @@ export default class DashboardStore implements IDashboardStore { }); } - updatePinned(dashboardId: string): Promise { - // 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, diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index 5b8c40b17..440303d4c 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -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) : [] }) diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts index b75a059fa..311830387 100644 --- a/frontend/app/services/DashboardService.ts +++ b/frontend/app/services/DashboardService.ts @@ -18,7 +18,6 @@ export interface IDashboardService { saveWidget(dashboardId: string, widget: IWidget): Promise deleteWidget(dashboardId: string, widgetId: string): Promise - updatePinned(dashboardId: string): Promise } @@ -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 { - return this.client.get(`/dashboards/${dashboardId}/pin`, {}) - .then(response => response.json()) - } -} \ No newline at end of file +}