update dashboard request strucutre

This commit is contained in:
Nate Kelley 2025-04-09 14:00:48 -06:00
parent 0e2fd9a93c
commit c66b1878f3
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
14 changed files with 109 additions and 55 deletions

View File

@ -32,6 +32,7 @@ import { addMetricToDashboardConfig, removeMetricFromDashboardConfig } from './h
import { addAndRemoveMetricsToDashboard } from './helpers/addAndRemoveMetricsToDashboard';
import { useParams, useSearchParams } from 'next/navigation';
import { RustApiError } from '../errors';
import { useOriginalDashboardStore } from '@/context/Dashboards';
export const useGetDashboardsList = (
params: Omit<Parameters<typeof dashboardsGetList>[0], 'page_token' | 'page_size'>
@ -125,7 +126,8 @@ export const useUpdateDashboard = (params?: {
updateVersion?: boolean;
saveToServer?: boolean;
}) => {
const { updateVersion = true, saveToServer = false } = params || {};
const setOriginalDashboards = useOriginalDashboardStore((x) => x.setOriginalDashboard);
const { updateVersion = false, saveToServer = false } = params || {};
const queryClient = useQueryClient();
@ -159,6 +161,7 @@ export const useUpdateDashboard = (params?: {
dashboardQueryKeys.dashboardGetDashboard(variables.id).queryKey,
data
);
setOriginalDashboards(data.dashboard);
}
}
});
@ -176,12 +179,13 @@ export const useUpdateDashboardConfig = (params?: {
const queryClient = useQueryClient();
const method = useMemoizedFn(
async (
newDashboard: Partial<BusterDashboard['config']> & {
id: string;
}
) => {
const options = dashboardQueryKeys.dashboardGetDashboard(newDashboard.id);
async ({
dashboardId,
...newDashboard
}: Partial<BusterDashboard['config']> & {
dashboardId: string;
}) => {
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId);
const previousDashboard = queryClient.getQueryData(options.queryKey);
const previousConfig = previousDashboard?.dashboard?.config;
if (previousConfig) {
@ -189,7 +193,7 @@ export const useUpdateDashboardConfig = (params?: {
Object.assign(draft, newDashboard);
});
return mutateAsync({
id: newDashboard.id,
id: dashboardId,
config: newConfig
});
}

View File

@ -390,7 +390,7 @@ export const useUpdateMetric = (params: {
const {
wait = 0,
updateOnSave = false,
updateVersion = true,
updateVersion = false,
saveToServer = false
} = params || {};
const queryClient = useQueryClient();
@ -408,8 +408,10 @@ export const useUpdateMetric = (params: {
);
const combineAndSaveMetric = useMemoizedFn(
(newMetricPartial: Omit<Partial<IBusterMetric>, 'status'> & { id: string }) => {
const metricId = newMetricPartial.id;
({
id: metricId,
...newMetricPartial
}: Omit<Partial<IBusterMetric>, 'status'> & { id: string }) => {
const options = metricsQueryKeys.metricsGetMetric(metricId);
const prevMetric = getOriginalMetric(metricId);
const newMetric = create(prevMetric, (draft) => {

View File

@ -133,7 +133,7 @@ export const BusterResizeColumns: React.FC<ContainerProps> = ({
useLayoutEffect(() => {
setSizes(columnSpansToPercent(columnSizes));
}, [items.length, columnSizes?.length]);
}, [items.length, columnSizes]);
return (
<SortableContext id={rowId} items={items} disabled={false}>

View File

@ -63,7 +63,6 @@ export const BusterResizeableGrid: React.FC<{
if (checkRowEquality(filteredRows, rows)) {
return;
}
onRowLayoutChange(filteredRows);
setRows(filteredRows);
});

View File

@ -8,8 +8,8 @@ export const useChatUpdate = () => {
const queryClient = useQueryClient();
const onUpdateChat = useMemoizedFn(
async (newChatConfig: Partial<IBusterChat> & { id: string }) => {
const options = queryKeys.chatsGetChat(newChatConfig.id);
async ({ id: chatId, ...newChatConfig }: Partial<IBusterChat> & { id: string }) => {
const options = queryKeys.chatsGetChat(chatId);
const queryKey = options.queryKey;
const currentData = queryClient.getQueryData<IBusterChat>(queryKey);
const iChat = create(currentData || ({} as IBusterChat), (draft) => {

View File

@ -5,6 +5,8 @@ import { useGetDashboard } from '@/api/buster_rest/dashboards';
import { BusterDashboardResponse } from '@/api/asset_interfaces/dashboard';
import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
import { compareObjectsByKeys } from '@/lib/objects';
import { useMemo } from 'react';
import { create } from 'mutative';
export const useIsDashboardChanged = ({ dashboardId }: { dashboardId: string }) => {
const queryClient = useQueryClient();
@ -26,17 +28,16 @@ export const useIsDashboardChanged = ({ dashboardId }: { dashboardId: string })
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId);
const currentDashboard = queryClient.getQueryData<BusterDashboardResponse>(options.queryKey);
if (originalDashboard && currentDashboard) {
queryClient.setQueryData(options.queryKey, {
...currentDashboard,
dashboard: originalDashboard
const resetDashboard = create(currentDashboard, (draft) => {
Object.assign(draft, originalDashboard);
});
queryClient.setQueryData(options.queryKey, resetDashboard);
}
refetchCurrentDashboard();
});
return {
onResetDashboardToOriginal,
isDashboardChanged:
const isDashboardChanged = useMemo(() => {
return (
!originalDashboard ||
!currentDashboard ||
!compareObjectsByKeys(originalDashboard, currentDashboard, [
@ -45,5 +46,11 @@ export const useIsDashboardChanged = ({ dashboardId }: { dashboardId: string })
'config',
'file'
])
);
}, [originalDashboard, currentDashboard]);
return {
onResetDashboardToOriginal,
isDashboardChanged
};
};

View File

@ -10,7 +10,6 @@ import { useUpdateMetric } from '@/api/buster_rest/metrics';
import { useMemoizedFn } from '@/hooks';
import { useGetMetricMemoized } from './useGetMetricMemoized';
import { useParams } from 'next/navigation';
import { useOriginalMetricStore } from './useOriginalMetricStore';
import { timeout } from '@/lib';
import { useState } from 'react';

View File

@ -94,7 +94,7 @@ export const DashboardContentController: React.FC<{
const onRowLayoutChange = useMemoizedFn((rows: BusterResizeableGridRow[]) => {
if (dashboard) {
onUpdateDashboardConfig({ rows: removeChildrenFromItems(rows), id: dashboard.id });
onUpdateDashboardConfig({ rows: removeChildrenFromItems(rows), dashboardId: dashboard.id });
}
});
@ -108,7 +108,7 @@ export const DashboardContentController: React.FC<{
useEffect(() => {
if (remapMetrics && dashboard?.id) {
debouncedForInitialRenderOnUpdateDashboardConfig({ rows, id: dashboard.id });
debouncedForInitialRenderOnUpdateDashboardConfig({ rows, dashboardId: dashboard.id });
}
}, [dashboard?.id, remapMetrics]);

View File

@ -25,11 +25,10 @@ export const DashboardEditTitles: React.FC<{
if (!readOnly) onUpdateDashboard({ name, id: dashboardId });
});
const { run: onChangeDashboardDescription } = useDebounceFn(
useMemoizedFn((value: React.ChangeEvent<HTMLTextAreaElement>) => {
const onChangeDashboardDescription = useMemoizedFn(
(value: React.ChangeEvent<HTMLTextAreaElement>) => {
if (!readOnly) onUpdateDashboard({ description: value.target.value, id: dashboardId });
}),
{ wait: 650 }
}
);
return (
@ -37,6 +36,7 @@ export const DashboardEditTitles: React.FC<{
<EditableTitle
className="w-full truncate"
readOnly={readOnly}
onSetValue={onChangeTitle}
onChange={onChangeTitle}
id={DASHBOARD_TITLE_INPUT_ID}
placeholder="New dashboard"

View File

@ -0,0 +1,38 @@
import { useGetDashboard, useUpdateDashboard } from '@/api/buster_rest/dashboards';
import { SaveResetFilePopup } from '@/components/features/popups/SaveResetFilePopup';
import { useIsDashboardChanged } from '@/context/Dashboards';
import { useMemoizedFn } from '@/hooks';
import React from 'react';
export const DashboardSaveFilePopup: React.FC<{ dashboardId: string }> = React.memo(
({ dashboardId }) => {
const { data: dashboardResponse } = useGetDashboard({ id: dashboardId });
const { isDashboardChanged, onResetDashboardToOriginal } = useIsDashboardChanged({
dashboardId
});
const { mutateAsync: onSaveDashboard, isPending: isSaving } = useUpdateDashboard({
saveToServer: true
});
const onSaveDashboardToServer = useMemoizedFn(() => {
const dashboard = dashboardResponse?.dashboard;
onSaveDashboard({
id: dashboardId,
name: dashboard?.name,
description: dashboard?.description,
config: dashboard?.config,
file: dashboard?.file
});
});
return (
<SaveResetFilePopup
open={isDashboardChanged}
onReset={onResetDashboardToOriginal}
onSave={onSaveDashboardToServer}
isSaving={isSaving}
showHotsKeys={false}
/>
);
}
);

View File

@ -8,6 +8,8 @@ import { useDashboardContentStore, useIsDashboardChanged } from '@/context/Dashb
import { ScrollArea } from '@/components/ui/scroll-area';
import { StatusCard } from '@/components/ui/card/StatusCard';
import { useIsDashboardReadOnly } from '@/context/Dashboards/useIsDashboardReadOnly';
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout';
import { DashboardSaveFilePopup } from './DashboardSaveFilePopup';
export const DashboardViewDashboardController: React.FC<{
dashboardId: string;
@ -22,10 +24,8 @@ export const DashboardViewDashboardController: React.FC<{
} = useGetDashboard({ id: dashboardId });
const { mutateAsync: onUpdateDashboardConfig } = useUpdateDashboardConfig();
const isVersionHistoryMode = useChatLayoutContextSelector((x) => x.isVersionHistoryMode);
const onOpenAddContentModal = useDashboardContentStore((x) => x.onOpenAddContentModal);
const { isDashboardChanged, onResetDashboardToOriginal } = useIsDashboardChanged({
dashboardId
});
const metrics = dashboardResponse?.metrics;
const dashboard = dashboardResponse?.dashboard;
@ -34,6 +34,10 @@ export const DashboardViewDashboardController: React.FC<{
readOnly: readOnlyProp
});
if (!isFetched) {
return <></>;
}
if (isError) {
return (
<div className="p-10">
@ -42,10 +46,6 @@ export const DashboardViewDashboardController: React.FC<{
);
}
if (!isFetched) {
return <></>;
}
return (
<ScrollArea className="h-full">
<div className="flex h-full flex-col space-y-3 p-10">
@ -64,6 +64,8 @@ export const DashboardViewDashboardController: React.FC<{
onOpenAddContentModal={onOpenAddContentModal}
readOnly={isReadOnly}
/>
{!isVersionHistoryMode && <DashboardSaveFilePopup dashboardId={dashboardId} />}
</div>
</ScrollArea>
);

View File

@ -0,0 +1,21 @@
import { SaveResetFilePopup } from '@/components/features/popups/SaveResetFilePopup';
import { useIsMetricChanged } from '@/context/Metrics/useIsMetricChanged';
import { useUpdateMetricChart } from '@/context/Metrics/useUpdateMetricChart';
import React from 'react';
export const MetricSaveFilePopup: React.FC<{ metricId: string }> = React.memo(({ metricId }) => {
const { isMetricChanged, onResetMetricToOriginal } = useIsMetricChanged({ metricId });
const { onSaveMetricToServer, isSaving } = useUpdateMetricChart({ metricId });
return (
<SaveResetFilePopup
open={isMetricChanged}
onReset={onResetMetricToOriginal}
onSave={onSaveMetricToServer}
isSaving={isSaving}
showHotsKeys={false}
/>
);
});
MetricSaveFilePopup.displayName = 'MetricSaveFilePopup';

View File

@ -14,6 +14,7 @@ import { SaveResetFilePopup } from '@/components/features/popups/SaveResetFilePo
import { useIsMetricChanged } from '@/context/Metrics/useIsMetricChanged';
import { useUpdateMetricChart } from '@/context/Metrics';
import { useIsMetricReadOnly } from '@/context/Metrics/useIsMetricReadOnly';
import { MetricSaveFilePopup } from './MetricSaveFilePopup';
export const MetricViewChart: React.FC<{
metricId: string;
@ -161,20 +162,3 @@ const AnimatePresenceWrapper: React.FC<{
</AnimatePresence>
);
};
const MetricSaveFilePopup: React.FC<{ metricId: string }> = React.memo(({ metricId }) => {
const { isMetricChanged, onResetMetricToOriginal } = useIsMetricChanged({ metricId });
const { onSaveMetricToServer, isSaving } = useUpdateMetricChart({ metricId });
return (
<SaveResetFilePopup
open={isMetricChanged}
onReset={onResetMetricToOriginal}
onSave={onSaveMetricToServer}
isSaving={isSaving}
showHotsKeys={false}
/>
);
});
MetricSaveFilePopup.displayName = 'MetricSaveFilePopup';

View File

@ -14,8 +14,7 @@ import {
useBulkUpdateMetricVerificationStatus,
useDeleteMetric,
useRemoveMetricFromCollection,
useSaveMetricToCollections,
useUpdateMetric
useSaveMetricToCollections
} from '@/api/buster_rest/metrics';
import { useThreeDotFavoritesOptions } from '@/components/features/dropdowns/useThreeDotFavoritesOptions';
@ -123,7 +122,6 @@ const StatusButton: React.FC<{
const isAdmin = useUserConfigContextSelector((state) => state.isAdmin);
const onVerify = useMemoizedFn(async (data: { id: string; status: VerificationStatus }[]) => {
console.log('onVerify', data);
await updateStatus(data);
onSelectChange([]);
});