2025-02-02 13:17:37 +08:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
import React, { useEffect, useMemo, useState } from 'react';
|
2025-01-07 02:29:29 +08:00
|
|
|
import isEmpty from 'lodash/isEmpty';
|
2025-02-20 10:53:49 +08:00
|
|
|
import { BusterResizeableGrid, BusterResizeableGridRow } from '@/components/ui/grid';
|
2025-04-19 12:34:15 +08:00
|
|
|
import { useDebounceFn, useMemoizedFn, useWhyDidYouUpdate } from '@/hooks';
|
2025-03-22 05:43:29 +08:00
|
|
|
import {
|
|
|
|
hasRemovedMetrics,
|
|
|
|
hasUnmappedMetrics,
|
|
|
|
normalizeNewMetricsIntoGrid,
|
|
|
|
removeChildrenFromItems
|
|
|
|
} from './helpers';
|
2025-02-02 13:17:37 +08:00
|
|
|
import { DashboardMetricItem } from './DashboardMetricItem';
|
2025-02-06 07:43:18 +08:00
|
|
|
import { DashboardContentControllerProvider } from './DashboardContentControllerContext';
|
2025-02-02 13:17:37 +08:00
|
|
|
import type {
|
|
|
|
BusterMetric,
|
|
|
|
BusterDashboardResponse,
|
|
|
|
DashboardConfig
|
|
|
|
} from '@/api/asset_interfaces';
|
2025-04-17 13:47:14 +08:00
|
|
|
import { DashboardEmptyState, DashboardNoContentReadOnly } from './DashboardEmptyState';
|
2025-03-13 08:40:15 +08:00
|
|
|
import { type useUpdateDashboardConfig } from '@/api/buster_rest/dashboards';
|
2025-04-09 12:08:10 +08:00
|
|
|
import last from 'lodash/last';
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
|
|
const DEFAULT_EMPTY_ROWS: DashboardConfig['rows'] = [];
|
2025-03-18 12:40:09 +08:00
|
|
|
const DEFAULT_EMPTY_METRICS: Record<string, BusterMetric> = {};
|
2025-01-07 02:29:29 +08:00
|
|
|
const DEFAULT_EMPTY_CONFIG: DashboardConfig = {};
|
|
|
|
|
2025-02-06 07:43:18 +08:00
|
|
|
export const DashboardContentController: React.FC<{
|
2025-03-19 03:36:58 +08:00
|
|
|
readOnly?: boolean;
|
2025-02-13 14:11:44 +08:00
|
|
|
metrics: BusterDashboardResponse['metrics'] | undefined;
|
2025-03-30 12:28:15 +08:00
|
|
|
chatId: string | undefined;
|
2025-02-13 14:11:44 +08:00
|
|
|
dashboard: BusterDashboardResponse['dashboard'] | undefined;
|
2025-03-13 08:40:15 +08:00
|
|
|
onUpdateDashboardConfig: ReturnType<typeof useUpdateDashboardConfig>['mutateAsync'];
|
2025-02-14 06:58:14 +08:00
|
|
|
onOpenAddContentModal: () => void;
|
2025-01-07 02:29:29 +08:00
|
|
|
}> = React.memo(
|
2025-02-06 07:43:18 +08:00
|
|
|
({
|
2025-02-14 06:58:14 +08:00
|
|
|
onOpenAddContentModal,
|
2025-02-06 07:43:18 +08:00
|
|
|
dashboard,
|
2025-03-30 12:28:15 +08:00
|
|
|
chatId,
|
2025-03-19 03:36:58 +08:00
|
|
|
readOnly = false,
|
2025-02-06 07:43:18 +08:00
|
|
|
metrics = DEFAULT_EMPTY_METRICS,
|
|
|
|
onUpdateDashboardConfig
|
|
|
|
}) => {
|
2025-02-13 14:11:44 +08:00
|
|
|
const dashboardConfig = dashboard?.config || DEFAULT_EMPTY_CONFIG;
|
2025-01-07 02:29:29 +08:00
|
|
|
const configRows = dashboardConfig?.rows || DEFAULT_EMPTY_ROWS;
|
|
|
|
const hasMetrics = !isEmpty(metrics);
|
2025-02-02 13:17:37 +08:00
|
|
|
const [draggingId, setDraggingId] = useState<string | null>(null);
|
2025-03-18 12:40:09 +08:00
|
|
|
const numberOfMetrics = Object.values(metrics).length;
|
2025-01-07 02:29:29 +08:00
|
|
|
|
2025-02-02 13:17:37 +08:00
|
|
|
const remapMetrics = useMemo(() => {
|
2025-03-21 03:37:34 +08:00
|
|
|
return hasUnmappedMetrics(metrics, configRows) || hasRemovedMetrics(metrics, configRows);
|
2025-01-07 02:29:29 +08:00
|
|
|
}, [metrics, configRows.length]);
|
|
|
|
|
|
|
|
const rows = useMemo(() => {
|
2025-02-02 13:17:37 +08:00
|
|
|
return remapMetrics ? normalizeNewMetricsIntoGrid(metrics, configRows) : configRows;
|
|
|
|
}, [remapMetrics, metrics, configRows]);
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
|
|
const dashboardRows = useMemo(() => {
|
2025-04-19 12:34:15 +08:00
|
|
|
console.log('dashboardRows! rerender', rows);
|
2025-01-07 02:29:29 +08:00
|
|
|
return rows
|
|
|
|
.filter((row) => row.items.length > 0)
|
|
|
|
.map((row) => {
|
|
|
|
return {
|
|
|
|
...row,
|
|
|
|
items: row.items.map((item) => {
|
2025-04-09 12:08:10 +08:00
|
|
|
const selectedMetric = metrics[item.id];
|
2025-04-17 07:07:14 +08:00
|
|
|
const versionNumber = selectedMetric.version_number;
|
2025-04-09 12:08:10 +08:00
|
|
|
|
2025-01-07 02:29:29 +08:00
|
|
|
return {
|
|
|
|
...item,
|
|
|
|
children: (
|
|
|
|
<DashboardMetricItem
|
|
|
|
key={item.id}
|
|
|
|
metricId={item.id}
|
2025-02-13 14:11:44 +08:00
|
|
|
dashboardId={dashboard!.id}
|
2025-03-19 03:36:58 +08:00
|
|
|
readOnly={readOnly}
|
2025-03-30 12:28:15 +08:00
|
|
|
chatId={chatId}
|
2025-03-18 12:40:09 +08:00
|
|
|
numberOfMetrics={numberOfMetrics}
|
2025-04-09 12:08:10 +08:00
|
|
|
versionNumber={versionNumber}
|
2025-01-07 02:29:29 +08:00
|
|
|
/>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
})
|
|
|
|
};
|
|
|
|
});
|
2025-03-21 03:37:34 +08:00
|
|
|
}, [rows, readOnly, dashboard?.id, numberOfMetrics]);
|
|
|
|
|
|
|
|
const { run: debouncedForInitialRenderOnUpdateDashboardConfig } = useDebounceFn(
|
|
|
|
onUpdateDashboardConfig,
|
|
|
|
{ wait: 650, leading: true }
|
|
|
|
);
|
|
|
|
|
|
|
|
const onRowLayoutChange = useMemoizedFn((rows: BusterResizeableGridRow[]) => {
|
2025-03-22 05:43:29 +08:00
|
|
|
if (dashboard) {
|
2025-04-10 04:00:48 +08:00
|
|
|
onUpdateDashboardConfig({ rows: removeChildrenFromItems(rows), dashboardId: dashboard.id });
|
2025-03-22 05:43:29 +08:00
|
|
|
}
|
2025-03-21 03:37:34 +08:00
|
|
|
});
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
|
|
const onDragEnd = useMemoizedFn(() => {
|
|
|
|
setDraggingId(null);
|
|
|
|
});
|
|
|
|
|
|
|
|
const onStartDrag = useMemoizedFn(({ id }: { id: string }) => {
|
|
|
|
setDraggingId(id);
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
2025-02-13 14:11:44 +08:00
|
|
|
if (remapMetrics && dashboard?.id) {
|
2025-04-10 04:00:48 +08:00
|
|
|
debouncedForInitialRenderOnUpdateDashboardConfig({ rows, dashboardId: dashboard.id });
|
2025-01-07 02:29:29 +08:00
|
|
|
}
|
2025-02-13 14:11:44 +08:00
|
|
|
}, [dashboard?.id, remapMetrics]);
|
2025-01-07 02:29:29 +08:00
|
|
|
|
2025-04-19 12:34:15 +08:00
|
|
|
useWhyDidYouUpdate('DashboardContentController', {
|
|
|
|
remapMetrics,
|
|
|
|
dashboard,
|
|
|
|
metrics,
|
|
|
|
dashboardRows,
|
|
|
|
readOnly,
|
|
|
|
numberOfMetrics,
|
|
|
|
rows,
|
|
|
|
chatId,
|
|
|
|
dashboardConfig,
|
|
|
|
configRows
|
|
|
|
});
|
|
|
|
|
2025-01-07 02:29:29 +08:00
|
|
|
return (
|
2025-02-06 13:35:46 +08:00
|
|
|
<div className="dashboard-content-controller">
|
2025-02-13 14:11:44 +08:00
|
|
|
{hasMetrics && !!dashboardRows.length && !!dashboard ? (
|
2025-02-06 07:43:18 +08:00
|
|
|
<DashboardContentControllerProvider dashboard={dashboard}>
|
2025-01-07 02:29:29 +08:00
|
|
|
<BusterResizeableGrid
|
|
|
|
rows={dashboardRows}
|
2025-03-19 03:36:58 +08:00
|
|
|
readOnly={readOnly}
|
2025-01-07 02:29:29 +08:00
|
|
|
onRowLayoutChange={onRowLayoutChange}
|
|
|
|
onStartDrag={onStartDrag}
|
|
|
|
onEndDrag={onDragEnd}
|
|
|
|
overlayComponent={
|
|
|
|
draggingId && (
|
|
|
|
<DashboardMetricItem
|
|
|
|
metricId={draggingId}
|
2025-04-09 12:08:10 +08:00
|
|
|
readOnly={true}
|
2025-02-06 07:43:18 +08:00
|
|
|
dashboardId={dashboard.id}
|
2025-01-07 02:29:29 +08:00
|
|
|
isDragOverlay
|
2025-03-18 12:40:09 +08:00
|
|
|
numberOfMetrics={numberOfMetrics}
|
2025-03-30 12:28:15 +08:00
|
|
|
chatId={undefined}
|
2025-04-09 12:08:10 +08:00
|
|
|
versionNumber={metrics[draggingId]?.version_number}
|
2025-01-07 02:29:29 +08:00
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
/>
|
2025-02-06 07:43:18 +08:00
|
|
|
</DashboardContentControllerProvider>
|
2025-03-22 04:03:43 +08:00
|
|
|
) : !readOnly ? (
|
2025-02-14 06:58:14 +08:00
|
|
|
<DashboardEmptyState onOpenAddContentModal={onOpenAddContentModal} />
|
2025-04-17 13:47:14 +08:00
|
|
|
) : (
|
|
|
|
<DashboardNoContentReadOnly />
|
|
|
|
)}
|
2025-01-07 02:29:29 +08:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
2025-02-06 07:43:18 +08:00
|
|
|
DashboardContentController.displayName = 'DashboardIndividualDashboard';
|