import { makeAutoObservable, runInAction, observable, action, reaction } 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 } from 'Types/app/period'; import { getChartFormatter } from 'Types/dashboard/helper'; export interface IDashboardSotre { dashboards: IDashboard[] selectedDashboard: IDashboard | null dashboardInstance: IDashboard selectedWidgets: IWidget[] startTimestamp: number endTimestamp: number period: Period siteId: any currentWidget: Widget widgetCategories: any[] widgets: Widget[] metricsPage: number metricsPageSize: number metricsSearch: string isLoading: boolean isSaving: boolean isDeleting: boolean fetchingDashboard: boolean 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 fetch(dashboardId: string): Promise save(dashboard: IDashboard): Promise saveDashboardWidget(dashboard: Dashboard, widget: Widget) deleteDashboard(dashboard: IDashboard): Promise 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 saveMetric(metric: IWidget, dashboardId?: string): Promise fetchTemplates(): Promise deleteDashboardWidget(dashboardId: string, widgetId: string): Promise addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise updatePinned(dashboardId: string): Promise fetchMetricChartData(metric: IWidget, data: any, isWidget: boolean): Promise setPeriod(period: any): void } export default class DashboardStore implements IDashboardSotre { siteId: any = null // Dashbaord / Widgets 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_7_DAYS }) startTimestamp: number = 0 endTimestamp: number = 0 // Metrics metricsPage: number = 1 metricsPageSize: number = 10 metricsSearch: string = '' // Loading states isLoading: boolean = true isSaving: boolean = false isDeleting: boolean = false fetchingDashboard: boolean = false constructor() { makeAutoObservable(this, { widgetCategories: observable.ref, resetCurrentWidget: action, addDashboard: action, removeDashboard: action, updateDashboard: action, getDashboard: action, getDashboardByIndex: action, getDashboardCount: action, selectDashboardById: action, selectDefaultDashboard: action, toJson: action, fromJson: action, setSiteId: action, editWidget: action, updateKey: action, toggleAllSelectedWidgets: action, removeSelectedWidgetByCategory: action, toggleWidgetSelection: action, fetchTemplates: action, updatePinned: action, setPeriod: action, fetchMetricChartData: action }) } toggleAllSelectedWidgets(isSelected: boolean) { if (isSelected) { const allWidgets = this.widgetCategories.reduce((acc, cat) => { return acc.concat(cat.widgets) }, []) this.selectedWidgets = allWidgets } else { this.selectedWidgets = [] } } removeSelectedWidgetByCategory = (category: any) => { 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); if (selectedWidgetIds.includes(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)) } initDashboard(dashboard: Dashboard) { this.dashboardInstance = dashboard ? new Dashboard().fromJson(dashboard) : new Dashboard() this.selectedWidgets = [] } updateKey(key: any, value: any) { this[key] = value } resetCurrentWidget() { this.currentWidget = new Widget() } editWidget(widget: any) { this.currentWidget.update(widget) } fetchList(): Promise { this.isLoading = true return dashboardService.getDashboards() .then((list: any) => { runInAction(() => { this.dashboards = list.map(d => new Dashboard().fromJson(d)).sort((a, b) => a.position - b.position) }) }).finally(() => { runInAction(() => { this.isLoading = false }) }) } fetch(dashboardId: string): Promise { this.fetchingDashboard = true return dashboardService.getDashboard(dashboardId).then(response => { this.selectedDashboard = new Dashboard().fromJson(response) }).finally(() => { this.fetchingDashboard = false }) } save(dashboard: IDashboard): Promise { this.isSaving = true const isCreating = !dashboard.dashboardId 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(_dashboard) } else { toast.success('Dashboard updated successfully') this.updateDashboard(_dashboard) } resolve(_dashboard) }) }).catch(error => { toast.error('Error saving dashboard') reject() }).finally(() => { runInAction(() => { this.isSaving = false }) }) }) } saveMetric(metric: IWidget, dashboardId: string): Promise { 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 } } deleteDashboard(dashboard: Dashboard): Promise { 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 }) }) } toJson() { return { dashboards: this.dashboards.map(d => d.toJson()) } } fromJson(json: any) { runInAction(() => { this.dashboards = json.dashboards.map(d => new Dashboard().fromJson(d)) }) return this } addDashboard(dashboard: Dashboard) { this.dashboards.push(dashboard) } removeDashboard(dashboard: Dashboard) { this.dashboards = this.dashboards.filter(d => d.dashboardId !== dashboard.dashboardId) } getDashboard(dashboardId: string): IDashboard|null { return this.dashboards.find(d => d.dashboardId === dashboardId) || null } getDashboardByIndex(index: number) { return this.dashboards[index] } getDashboardCount() { return this.dashboards.length } updateDashboard(dashboard: Dashboard) { const index = this.dashboards.findIndex(d => d.dashboardId === dashboard.dashboardId) if (index >= 0) { this.dashboards[index] = dashboard } } selectDashboardById = (dashboardId: any) => { this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard(); if (this.selectedDashboard.dashboardId) { this.fetch(this.selectedDashboard.dashboardId) } } setSiteId = (siteId: any) => { 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] } } if (this.selectedDashboard) { resolve(this.selectedDashboard) } // reject(new Error("No dashboards found")) }) } fetchTemplates(): Promise { return new Promise((resolve, reject) => { if (this.widgetCategories.length > 0) { resolve(this.widgetCategories) } else { metricService.getTemplates().then(response => { const categories: any[] = [] response.forEach(category => { const widgets: any[] = [] category.widgets.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) }) this.widgetCategories = categories resolve(this.widgetCategories) }).catch(error => { reject(error) }) } }) } deleteDashboardWidget(dashboardId: string, widgetId: string) { this.isDeleting = true return dashboardService.deleteWidget(dashboardId, widgetId).then(() => { toast.success('Widget deleted successfully') runInAction(() => { this.selectedDashboard?.removeWidget(widgetId) }) }).finally(() => { this.isDeleting = false }) } addWidgetToDashboard(dashboard: IDashboard, metricIds: any) : Promise { 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 { // 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.startDate, end: period.endDate, rangeName: period.rangeValue }) } fetchMetricChartData(metric: IWidget, data: any, isWidget: boolean = false): Promise { const period = this.period.toTimestamps() return new Promise((resolve, reject) => { // this.isLoading = true return metricService.getMetricChartData(metric, { ...period, ...data, key: metric.predefinedKey }, isWidget) .then(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.predefinedKey === 'errors_per_domains') { // console.log('errors_per_domains', data) // data.chart = data // } else { // data.chart = getChartFormatter(this.period)(Array.isArray(data) ? data : data.chart) // } 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; // console.log('map', data.namesMap) // const _data = { ...data, namesMap: data.namesMap, chart: data.chart } // metric.setData(_data) // resolve(_data); const _data = {} if (data.hasOwnProperty('chart')) { _data['chart'] = getChartFormatter(this.period)(data.chart) _data['namesMap'] = data.chart .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; }, []) } else { _data['chart'] = data _data['namesMap'] = 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) resolve({ ...data, ..._data }); } }).catch((err) => { console.log('err', err) reject(err) }) }) } }