mirror of https://github.com/buster-so/buster.git
error for metric
This commit is contained in:
parent
a6e12cb06b
commit
0ebcc6ffab
|
@ -1,6 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { useDebounceFn, useMemoizedFn, useMount, useUnmount } from '@/hooks';
|
||||
import { useDebounceFn, useMemoizedFn } from '@/hooks';
|
||||
import {
|
||||
deleteMetrics,
|
||||
duplicateMetric,
|
||||
|
@ -26,21 +26,32 @@ import {
|
|||
useAddAssetToCollection,
|
||||
useRemoveAssetFromCollection
|
||||
} from '../collections/queryRequests';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
/**
|
||||
* This is a hook that will use the version number from the URL params if it exists.
|
||||
*/
|
||||
export const useGetMetric = <TData = IBusterMetric>(
|
||||
{ id, version_number }: { id: string | undefined; version_number?: number },
|
||||
{ id, version_number: version_number_prop }: { id: string | undefined; version_number?: number },
|
||||
select?: (data: IBusterMetric) => TData
|
||||
) => {
|
||||
const searchParams = useSearchParams();
|
||||
const queryVersionNumber = searchParams.get('metric_version_number');
|
||||
const getAssetPassword = useBusterAssetsContextSelector((x) => x.getAssetPassword);
|
||||
const setAssetPasswordError = useBusterAssetsContextSelector((x) => x.setAssetPasswordError);
|
||||
const { password } = getAssetPassword(id!);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const options = metricsQueryKeys.metricsGetMetric(id!);
|
||||
const version_number = useMemo(() => {
|
||||
return version_number_prop || queryVersionNumber ? parseInt(queryVersionNumber!) : undefined;
|
||||
}, [version_number_prop, queryVersionNumber]);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return metricsQueryKeys.metricsGetMetric(id!, version_number);
|
||||
}, [id, version_number]);
|
||||
|
||||
const queryFn = useMemoizedFn(async () => {
|
||||
const result = await getMetric({ id: id!, password });
|
||||
const result = await getMetric({ id: id!, password, version_number });
|
||||
const oldMetric = queryClient.getQueryData(options.queryKey);
|
||||
return upgradeMetricToIMetric(result, oldMetric || null);
|
||||
});
|
||||
|
@ -65,7 +76,7 @@ export const prefetchGetMetric = async (
|
|||
) => {
|
||||
const queryClient = queryClientProp || new QueryClient();
|
||||
await queryClient.prefetchQuery({
|
||||
...metricsQueryKeys.metricsGetMetric(params.id),
|
||||
...metricsQueryKeys.metricsGetMetric(params.id, params.version_number),
|
||||
queryFn: async () => {
|
||||
const result = await getMetric_server(params);
|
||||
return upgradeMetricToIMetric(result, null);
|
||||
|
@ -112,33 +123,44 @@ export const prefetchGetMetricsList = async (
|
|||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a hook that will use the version number from the URL params if it exists.
|
||||
*/
|
||||
export const useGetMetricData = ({
|
||||
id,
|
||||
version_number
|
||||
version_number: version_number_prop
|
||||
}: {
|
||||
id: string;
|
||||
version_number?: number;
|
||||
}) => {
|
||||
const searchParams = useSearchParams();
|
||||
const queryVersionNumber = searchParams.get('metric_version_number');
|
||||
|
||||
const version_number = useMemo(() => {
|
||||
return version_number_prop || queryVersionNumber ? parseInt(queryVersionNumber!) : undefined;
|
||||
}, [version_number_prop, queryVersionNumber]);
|
||||
|
||||
const queryFn = useMemoizedFn(() => {
|
||||
return getMetricData({ id, version_number });
|
||||
});
|
||||
|
||||
return useQuery({
|
||||
...metricsQueryKeys.metricsGetData(id),
|
||||
...metricsQueryKeys.metricsGetData(id, version_number),
|
||||
queryFn,
|
||||
enabled: !!id
|
||||
});
|
||||
};
|
||||
|
||||
export const prefetchGetMetricDataClient = async (
|
||||
{ id }: { id: string },
|
||||
{ id, version_number }: { id: string; version_number?: number },
|
||||
queryClient: QueryClient
|
||||
) => {
|
||||
const options = metricsQueryKeys.metricsGetData(id);
|
||||
const options = metricsQueryKeys.metricsGetData(id, version_number);
|
||||
const existingData = queryClient.getQueryData(options.queryKey);
|
||||
if (!existingData) {
|
||||
await queryClient.prefetchQuery({
|
||||
...options,
|
||||
queryFn: () => getMetricData({ id })
|
||||
queryFn: () => getMetricData({ id, version_number })
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,9 +19,9 @@ export const metricsGetList = (filters?: Parameters<typeof listMetrics>[0]) =>
|
|||
staleTime: 10 * 1000
|
||||
});
|
||||
|
||||
export const metricsGetData = (id: string) =>
|
||||
export const metricsGetData = (id: string, version_number?: number) =>
|
||||
queryOptions<IBusterMetricData>({
|
||||
queryKey: ['metrics', 'data', id] as const,
|
||||
queryKey: ['metrics', 'data', id, version_number] as const,
|
||||
staleTime: 3 * 60 * 60 * 1000 // 3 hours,
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { MetricController } from '@/controllers/MetricController';
|
|||
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
||||
|
||||
export default async function MetricPage(props: { params: Promise<{ metricId: string }> }) {
|
||||
const params = await props.params;
|
||||
const [params] = await Promise.all([props.params]);
|
||||
const { metricId } = params;
|
||||
|
||||
return (
|
||||
|
|
|
@ -4,9 +4,19 @@ import { useGetMetric } from '@/api/buster_rest/metrics';
|
|||
import { CircleSpinnerLoaderContainer } from '@/components/ui/loaders';
|
||||
import { MetricViewChart } from '@/controllers/MetricController/MetricViewChart/MetricViewChart';
|
||||
|
||||
export default function EmbedMetricsPage({ params }: { params: { metricId: string } }) {
|
||||
export default function EmbedMetricsPage({
|
||||
params,
|
||||
searchParams
|
||||
}: {
|
||||
params: { metricId: string };
|
||||
searchParams: { version_number?: string };
|
||||
}) {
|
||||
const { metricId } = params;
|
||||
const { isFetched } = useGetMetric({ id: metricId });
|
||||
const { version_number } = searchParams;
|
||||
const { isFetched, error, ...rest } = useGetMetric({
|
||||
id: metricId,
|
||||
version_number: version_number ? parseInt(version_number) : undefined
|
||||
});
|
||||
|
||||
if (!isFetched) {
|
||||
return <CircleSpinnerLoaderContainer className="min-h-screen" />;
|
||||
|
|
|
@ -8,8 +8,11 @@ const statusVariants = cva('shadow p-3 rounded', {
|
|||
variants: {
|
||||
variant: {
|
||||
danger: 'bg-danger-foreground text-white',
|
||||
default: 'bg-background text-foreground'
|
||||
default: 'bg-background text-foreground border'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -21,7 +24,7 @@ export const StatusCard: React.FC<
|
|||
onClose?: () => void;
|
||||
extra?: React.ReactNode;
|
||||
} & VariantProps<typeof statusVariants>
|
||||
> = ({ message, title, variant, className, onClose, extra }) => {
|
||||
> = ({ message, title, variant = 'default', className, onClose, extra }) => {
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-1', statusVariants({ variant }), className)}>
|
||||
{title && <Text variant={'inherit'}>{title}</Text>}
|
||||
|
|
|
@ -4,7 +4,17 @@ import { useInViewport } from '@/hooks';
|
|||
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
|
||||
|
||||
export const useDashboardMetric = ({ metricId }: { metricId: string }) => {
|
||||
const { data: metric, isFetched: isMetricFetched } = useGetMetric({ id: metricId });
|
||||
const { data: metric, isFetched: isMetricFetched } = useGetMetric(
|
||||
{ id: metricId },
|
||||
({ name, description, time_frame, permission, evaluation_score, evaluation_summary }) => ({
|
||||
name,
|
||||
description,
|
||||
time_frame,
|
||||
permission,
|
||||
evaluation_score,
|
||||
evaluation_summary
|
||||
})
|
||||
);
|
||||
const {
|
||||
data: metricData,
|
||||
isFetched: isFetchedMetricData,
|
||||
|
|
|
@ -8,25 +8,34 @@ import {
|
|||
import { MetricViewComponents } from './config';
|
||||
import { FileIndeterminateLoader } from '@/components/features/FileIndeterminateLoader';
|
||||
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
|
||||
import { MetricViewError } from './MetricViewError';
|
||||
|
||||
export const MetricController: React.FC<{
|
||||
metricId: string;
|
||||
}> = React.memo(({ metricId }) => {
|
||||
const { isFetched: isMetricFetched } = useGetMetric({ id: metricId });
|
||||
const { isFetched: isMetricFetched, error: metricError } = useGetMetric({ id: metricId });
|
||||
const { isFetched: isMetricDataFetched } = useGetMetricData({ id: metricId });
|
||||
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView) || 'chart';
|
||||
|
||||
const showLoader = !isMetricFetched || !isMetricDataFetched;
|
||||
|
||||
const Component =
|
||||
isMetricFetched && selectedFileView in MetricViewComponents
|
||||
? MetricViewComponents[selectedFileView as MetricFileView]
|
||||
: () => <></>;
|
||||
const Component = React.useMemo(() => {
|
||||
if (metricError) {
|
||||
return <MetricViewError error={metricError.message} />;
|
||||
}
|
||||
|
||||
if (isMetricFetched && selectedFileView in MetricViewComponents) {
|
||||
const Component = MetricViewComponents[selectedFileView as MetricFileView];
|
||||
return <Component metricId={metricId} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [isMetricFetched, selectedFileView, metricError, metricId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showLoader && <FileIndeterminateLoader />}
|
||||
{Component && <Component metricId={metricId} />}
|
||||
{Component}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
ScatterAxis
|
||||
} from '@/api/asset_interfaces/metric/charts';
|
||||
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
|
||||
import { useUnmount } from '@/hooks';
|
||||
|
||||
export const MetricStylingApp: React.FC<{
|
||||
metricId: string;
|
||||
|
@ -21,15 +20,14 @@ export const MetricStylingApp: React.FC<{
|
|||
const [segment, setSegment] = useState<MetricStylingAppSegments>(
|
||||
MetricStylingAppSegments.VISUALIZE
|
||||
);
|
||||
const { data: metric } = useGetMetric({ id: metricId });
|
||||
const { data: chartConfig } = useGetMetric({ id: metricId }, (x) => x.chart_config);
|
||||
const { data: metricData } = useGetMetricData({ id: metricId });
|
||||
|
||||
if (!metric) return null;
|
||||
if (!chartConfig) return null;
|
||||
|
||||
const columnMetadata = metricData?.data_metadata?.column_metadata || [];
|
||||
const rowCount = metricData?.data_metadata?.row_count || 0;
|
||||
|
||||
const chartConfig = metric.chart_config;
|
||||
const {
|
||||
selectedChartType,
|
||||
lineGroupType,
|
||||
|
|
|
@ -17,7 +17,26 @@ export const MetricViewChart: React.FC<{
|
|||
cardClassName?: string;
|
||||
}> = React.memo(
|
||||
({ metricId, readOnly: readOnlyProp = false, className = '', cardClassName = '' }) => {
|
||||
const { data: metric } = useGetMetric({ id: metricId });
|
||||
const { data: metric } = useGetMetric(
|
||||
{ id: metricId },
|
||||
({
|
||||
chart_config,
|
||||
name,
|
||||
description,
|
||||
time_frame,
|
||||
permission,
|
||||
evaluation_score,
|
||||
evaluation_summary
|
||||
}) => ({
|
||||
name,
|
||||
description,
|
||||
time_frame,
|
||||
permission,
|
||||
evaluation_score,
|
||||
evaluation_summary,
|
||||
chart_config
|
||||
})
|
||||
);
|
||||
const {
|
||||
data: metricData,
|
||||
isFetched: isFetchedMetricData,
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import { Text, Title } from '@/components/ui/typography';
|
||||
import { StatusCard } from '@/components/ui/card/StatusCard';
|
||||
|
||||
export const MetricViewError: React.FC<{ error: string | undefined }> = ({
|
||||
error = `The metric you are trying to view has an error. Please contact support if the problem persists.`
|
||||
}) => {
|
||||
return (
|
||||
<div className="mx-6 mt-6">
|
||||
<StatusCard message={error} title="Metric Error" />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from './MetricViewError';
|
|
@ -7,7 +7,10 @@ import { useBusterNotifications } from '@/context/BusterNotifications';
|
|||
import { useGetMetric, useUpdateMetric } from '@/api/buster_rest/metrics';
|
||||
|
||||
export const MetricViewFile: React.FC<MetricViewProps> = React.memo(({ metricId }) => {
|
||||
const { data: metric } = useGetMetric({ id: metricId });
|
||||
const { data: metric } = useGetMetric({ id: metricId }, ({ file, file_name }) => ({
|
||||
file,
|
||||
file_name
|
||||
}));
|
||||
const { openSuccessMessage } = useBusterNotifications();
|
||||
const { mutateAsync: updateMetric } = useUpdateMetric();
|
||||
|
||||
|
|
|
@ -21,7 +21,10 @@ export const MetricViewResults: React.FC<MetricViewProps> = React.memo(({ metric
|
|||
const { runSQL, resetRunSQLData, saveSQL, warnBeforeNavigating, setWarnBeforeNavigating } =
|
||||
useMetricRunSQL();
|
||||
|
||||
const { data: metric } = useGetMetric({ id: metricId });
|
||||
const { data: metric } = useGetMetric({ id: metricId }, ({ sql, data_source_id }) => ({
|
||||
sql,
|
||||
data_source_id
|
||||
}));
|
||||
const { data: metricData } = useGetMetricData({ id: metricId });
|
||||
|
||||
const [sql, setSQL] = React.useState(metric?.sql || '');
|
||||
|
|
|
@ -261,7 +261,7 @@ const useCollectionSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
};
|
||||
|
||||
const useStatusSelectMenu = ({ metricId }: { metricId: string }) => {
|
||||
const { data: metric } = useGetMetric({ id: metricId }, (x) => x);
|
||||
const { data: metricStatus } = useGetMetric({ id: metricId }, (x) => x.status);
|
||||
const { mutateAsync: updateMetric } = useUpdateMetric();
|
||||
|
||||
const onChangeStatus = useMemoizedFn(async (status: VerificationStatus) => {
|
||||
|
@ -270,7 +270,7 @@ const useStatusSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
|
||||
const dropdownProps = useStatusDropdownContent({
|
||||
isAdmin: true,
|
||||
selectedStatus: metric?.status || VerificationStatus.NOT_REQUESTED,
|
||||
selectedStatus: metricStatus || VerificationStatus.NOT_REQUESTED,
|
||||
onChangeStatus
|
||||
});
|
||||
|
||||
|
@ -282,7 +282,7 @@ const useStatusSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
() => ({
|
||||
label: 'Status',
|
||||
value: 'status',
|
||||
icon: <StatusBadgeIndicator status={metric?.status || VerificationStatus.NOT_REQUESTED} />,
|
||||
icon: <StatusBadgeIndicator status={metricStatus || VerificationStatus.NOT_REQUESTED} />,
|
||||
items: [<React.Fragment key="status-sub-menu">{statusSubMenu}</React.Fragment>]
|
||||
}),
|
||||
[statusSubMenu]
|
||||
|
|
Loading…
Reference in New Issue