Merge remote-tracking branch 'origin/evals' into evals

This commit is contained in:
dal 2025-04-09 13:17:15 -06:00
commit 5ffaf3fc74
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
7 changed files with 108 additions and 20 deletions

View File

@ -0,0 +1 @@
export const MAX_NUMBER_OF_ITEMS_ON_DASHBOARD = 30;

View File

@ -1,2 +1,3 @@
export * from './dashboardConfigInterfaces'; export * from './dashboardConfigInterfaces';
export * from './interfaces'; export * from './interfaces';
export * from './config';

View File

@ -10,7 +10,11 @@ import {
unshareDashboard unshareDashboard
} from './requests'; } from './requests';
import { dashboardQueryKeys } from '@/api/query_keys/dashboard'; import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
import { BusterDashboard, BusterDashboardResponse } from '@/api/asset_interfaces/dashboard'; import {
BusterDashboard,
BusterDashboardResponse,
MAX_NUMBER_OF_ITEMS_ON_DASHBOARD
} from '@/api/asset_interfaces/dashboard';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useMemoizedFn } from '@/hooks'; import { useMemoizedFn } from '@/hooks';
import { useBusterNotifications } from '@/context/BusterNotifications'; import { useBusterNotifications } from '@/context/BusterNotifications';
@ -407,10 +411,23 @@ export const useAddAndRemoveMetricsFromDashboard = () => {
const { openErrorMessage } = useBusterNotifications(); const { openErrorMessage } = useBusterNotifications();
const ensureDashboardConfig = useEnsureDashboardConfig(); const ensureDashboardConfig = useEnsureDashboardConfig();
const addMetricToDashboard = useMemoizedFn( const addAndRemoveMetrics = useMemoizedFn(
async ({ metricIds, dashboardId }: { metricIds: string[]; dashboardId: string }) => { async ({ metricIds, dashboardId }: { metricIds: string[]; dashboardId: string }) => {
const dashboardResponse = await ensureDashboardConfig(dashboardId); const dashboardResponse = await ensureDashboardConfig(dashboardId);
const numberOfItemsOnDashboard: number =
dashboardResponse?.dashboard.config.rows?.reduce(
(acc, row) => acc + (row.items?.length || 0),
0
) || 0;
if (numberOfItemsOnDashboard > MAX_NUMBER_OF_ITEMS_ON_DASHBOARD) {
openErrorMessage(
`Dashboard is full, please remove some metrics before adding more. You can only have ${MAX_NUMBER_OF_ITEMS_ON_DASHBOARD} metrics on a dashboard`
);
return;
}
if (dashboardResponse) { if (dashboardResponse) {
const newConfig = addAndRemoveMetricsToDashboard( const newConfig = addAndRemoveMetricsToDashboard(
metricIds, metricIds,
@ -427,7 +444,7 @@ export const useAddAndRemoveMetricsFromDashboard = () => {
); );
return useMutation({ return useMutation({
mutationFn: addMetricToDashboard, mutationFn: addAndRemoveMetrics,
onSuccess: (data, variables) => { onSuccess: (data, variables) => {
if (data) { if (data) {
queryClient.setQueryData( queryClient.setQueryData(

View File

@ -1,11 +1,15 @@
import { useQuery, keepPreviousData } from '@tanstack/react-query'; import { useQuery, keepPreviousData, UseQueryOptions } from '@tanstack/react-query';
import { searchQueryKeys } from '@/api/query_keys/search'; import { searchQueryKeys } from '@/api/query_keys/search';
import { search } from './requests'; import { search } from './requests';
export const useSearch = (params: Parameters<typeof search>[0]) => { export const useSearch = (
params: Parameters<typeof search>[0],
options?: Omit<UseQueryOptions<Awaited<ReturnType<typeof search>>>, 'queryKey' | 'queryFn'>
) => {
return useQuery({ return useQuery({
...searchQueryKeys.getSearchResult(params), ...searchQueryKeys.getSearchResult(params),
queryFn: () => search(params), queryFn: () => search(params),
placeholderData: keepPreviousData placeholderData: keepPreviousData,
...options
}); });
}; };

View File

@ -5,7 +5,7 @@ import { search } from '../buster_rest/search';
export const getSearchResult = (params: Parameters<typeof search>[0]) => export const getSearchResult = (params: Parameters<typeof search>[0]) =>
queryOptions<BusterSearchResult[]>({ queryOptions<BusterSearchResult[]>({
queryKey: ['search', 'results', params] as const, queryKey: ['search', 'results', params] as const,
staleTime: 1000 * 10 // 10 seconds, staleTime: 1000 * 30 // 30 seconds,
}); });
export const searchQueryKeys = { export const searchQueryKeys = {

View File

@ -26,7 +26,7 @@ export const ResetPasswordForm: React.FC<{
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [password2, setPassword2] = useState(''); const [password2, setPassword2] = useState('');
const [goodPassword, setGoodPassword] = useState(false); const [goodPassword, setGoodPassword] = useState(false);
const { openErrorNotification, openSuccessMessage } = useBusterNotifications(); const { openErrorMessage, openSuccessMessage } = useBusterNotifications();
const [countdown, setCountdown] = useState(5); const [countdown, setCountdown] = useState(5);
const disabled = !goodPassword || loading || !password || !password2 || password !== password2; const disabled = !goodPassword || loading || !password || !password2 || password !== password2;
@ -60,7 +60,7 @@ export const ResetPasswordForm: React.FC<{
startCountdown(); startCountdown();
} }
} catch (error) { } catch (error) {
openErrorNotification(error); openErrorMessage(error as string);
} }
}); });

View File

@ -17,11 +17,15 @@ export const AddToDashboardModal: React.FC<{
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [selectedMetrics, setSelectedMetrics] = useState<string[]>([]); const [selectedMetrics, setSelectedMetrics] = useState<string[]>([]);
const debouncedSearchTerm = useDebounce(searchTerm, { wait: 150 }); const debouncedSearchTerm = useDebounce(searchTerm, { wait: 150 });
const { data: searchResults } = useSearch({
query: debouncedSearchTerm, const { data: searchResults } = useSearch(
asset_types: ['metric'], {
num_results: 100 query: debouncedSearchTerm,
}); asset_types: ['metric'],
num_results: 100
},
{ enabled: open }
);
const columns = useMemo<InputSelectModalProps['columns']>( const columns = useMemo<InputSelectModalProps['columns']>(
() => [ () => [
@ -81,6 +85,63 @@ export const AddToDashboardModal: React.FC<{
return undefined; return undefined;
}, [isFetchedDashboard, rows]); }, [isFetchedDashboard, rows]);
const addedMetricCount = useMemo(() => {
return selectedMetrics.filter((id) => !Object.keys(dashboard?.metrics || {}).includes(id))
.length;
}, [dashboard?.metrics, selectedMetrics]);
const removedMetricCount = useMemo(() => {
return Object.keys(dashboard?.metrics || {}).filter((id) => !selectedMetrics.includes(id))
.length;
}, [dashboard?.metrics, selectedMetrics]);
const primaryButtonText = useMemo(() => {
if (!isFetchedDashboard) {
return 'Loading metrics...';
}
const hasRemovedItems = removedMetricCount > 0;
const hasAddedItems = addedMetricCount > 0;
if (hasRemovedItems && hasAddedItems) {
return `Update dashboard`;
}
if (hasRemovedItems) {
return `Remove metrics`;
}
if (hasAddedItems) {
return `Add metrics`;
}
return `Update dashboard`;
}, [isFetchedDashboard, removedMetricCount, addedMetricCount]);
const primaryButtonTooltipText = useMemo(() => {
if (!isFetchedDashboard) {
return '';
}
const hasRemovedItems = removedMetricCount > 0;
const hasAddedItems = addedMetricCount > 0;
const returnText: string[] = [];
if (!hasRemovedItems && !hasAddedItems) {
return 'No changes to update';
}
if (hasRemovedItems) {
returnText.push(`Removing ${removedMetricCount}`);
}
if (hasAddedItems) {
returnText.push(`Adding ${addedMetricCount}`);
}
return returnText.join(', ');
}, [isFetchedDashboard, addedMetricCount, removedMetricCount]);
const footer: NonNullable<InputSelectModalProps['footer']> = useMemo(() => { const footer: NonNullable<InputSelectModalProps['footer']> = useMemo(() => {
return { return {
left: left:
@ -94,15 +155,19 @@ export const AddToDashboardModal: React.FC<{
onClick: onClose onClick: onClose
}, },
primaryButton: { primaryButton: {
text: `Update metrics`, text: primaryButtonText,
onClick: handleAddAndRemoveMetrics, onClick: handleAddAndRemoveMetrics,
disabled: !isSelectedChanged, disabled: !isSelectedChanged,
tooltip: isSelectedChanged tooltip: primaryButtonTooltipText
? `Adding ${selectedMetrics.length} metrics`
: 'No changes to update'
} }
}; };
}, [selectedMetrics.length, isSelectedChanged, handleAddAndRemoveMetrics]); }, [
selectedMetrics.length,
primaryButtonTooltipText,
primaryButtonText,
isSelectedChanged,
handleAddAndRemoveMetrics
]);
useLayoutEffect(() => { useLayoutEffect(() => {
if (isFetchedDashboard) { if (isFetchedDashboard) {
@ -113,7 +178,7 @@ export const AddToDashboardModal: React.FC<{
return ( return (
<InputSelectModal <InputSelectModal
width={665} width={675}
open={open} open={open}
onClose={onClose} onClose={onClose}
columns={columns} columns={columns}